diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
index b41ce36..9aae6fa 100644
--- a/.github/workflows/maven-publish.yml
+++ b/.github/workflows/maven-publish.yml
@@ -17,10 +17,10 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Set up JDK 11
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index d8d81a8..ab4f43c 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -21,17 +21,22 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Set up JDK 11
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
- run: mvn -B package --file source/pom.xml
+ run: mvn -B package --file pom.xml
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- - name: Update dependency graph
+ - name: Update dependency graph API
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
with:
- directory: source
+ directory: api
+
+ - name: Update dependency graph CLI
+ uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
+ with:
+ directory: cli
diff --git a/api/.gitignore b/api/.gitignore
new file mode 100644
index 0000000..1c3509c
--- /dev/null
+++ b/api/.gitignore
@@ -0,0 +1 @@
+mockserver_keystore*
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000..0608443
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,82 @@
+
+ 4.0.0
+
+ net.klesatschke.threema
+ msgapi-sdk-java
+ 1.1.5-SNAPSHOT
+
+ api
+ Threema Messsage Gateway API
+
+
+ MIT-License
+ http://opensource.org/licenses/mit-license.php
+ repo
+
+
+ https://github.com/lordyavin/threema-msgapi-sdk-java
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mock-server
+ mockserver-junit-jupiter-no-dependencies
+ test
+
+
+ org.mock-server
+ mockserver-client-java-no-dependencies
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-text
+
+
+ com.google.code.gson
+ gson
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ eu.neilalexander
+ jnacl
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+
+
+ github
+ GitHub lordyavin Apache Maven Packages
+ https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java
+
+
+
\ No newline at end of file
diff --git a/api/src/main/java/net/klesatschke/threema/api/APIConnector.java b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java
new file mode 100644
index 0000000..d1c2e42
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java
@@ -0,0 +1,458 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpClient.Redirect;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import lombok.extern.log4j.Log4j2;
+import net.klesatschke.threema.api.exceptions.ClientError;
+import net.klesatschke.threema.api.exceptions.ServerError;
+import net.klesatschke.threema.api.exceptions.ThreemaError;
+import net.klesatschke.threema.api.results.CapabilityResult;
+import net.klesatschke.threema.api.results.EncryptResult;
+import net.klesatschke.threema.api.results.UploadResult;
+
+/** Facilitates HTTPS communication with the Threema Message API. */
+@Log4j2
+public class APIConnector {
+ private static final int BUFFER_SIZE = 16384;
+
+ public interface ProgressListener {
+
+ /**
+ * Update the progress of an upload/download process.
+ *
+ * @param progress in percent (0..100)
+ */
+ void updateProgress(int progress);
+ }
+
+ public class InputStreamLength {
+ public final InputStream inputStream;
+ public final int length;
+
+ public InputStreamLength(InputStream inputStream, int length) {
+ this.inputStream = inputStream;
+ this.length = length;
+ }
+ }
+
+ private final String apiUrl;
+ private final PublicKeyStore publicKeyStore;
+ private final String apiIdentity;
+ private final String secret;
+ private HttpClient httpClient;
+
+ public APIConnector(String apiIdentity, String secret, PublicKeyStore publicKeyStore) {
+ this(apiIdentity, secret, "https://msgapi.threema.ch/", publicKeyStore);
+ }
+
+ public APIConnector(
+ String apiIdentity, String secret, String apiUrl, PublicKeyStore publicKeyStore) {
+ this.apiIdentity = apiIdentity;
+ this.secret = secret;
+ this.apiUrl = apiUrl;
+ this.publicKeyStore = publicKeyStore;
+ httpClient = HttpClient.newBuilder().followRedirects(Redirect.NEVER).build();
+ }
+
+ /**
+ * Send a text message with server-side encryption.
+ *
+ * @param to recipient ID
+ * @param text message text (max. 3500 bytes)
+ * @return message ID
+ * @throws IOException if a communication or server error occurs
+ */
+ public String sendTextMessageSimple(String to, String text) throws IOException {
+
+ var postParams = makeRequestParams();
+ postParams.put("to", to);
+ postParams.put("text", text);
+
+ return doPost(new URL(this.apiUrl + "send_simple"), postParams);
+ }
+
+ /**
+ * Send an end-to-end encrypted message.
+ *
+ * @param to recipient ID
+ * @param nonce nonce used for encryption (24 bytes)
+ * @param box encrypted message data (max. 4000 bytes)
+ * @return message ID
+ * @throws IOException if a communication or server error occurs
+ */
+ public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOException {
+
+ var postParams = makeRequestParams();
+ postParams.put("to", to);
+ postParams.put("nonce", DataUtils.byteArrayToHexString(nonce));
+ postParams.put("box", DataUtils.byteArrayToHexString(box));
+
+ return doPost(new URL(this.apiUrl + "send_e2e"), postParams);
+ }
+
+ /**
+ * Lookup an ID by phone number. The phone number will be hashed before being sent to the server.
+ *
+ * @param phoneNumber the phone number in E.164 format
+ * @return the ID, or null if not found
+ * @throws IOException if a communication or server error occurs
+ */
+ public String lookupPhone(String phoneNumber) throws IOException {
+ var getParams = makeRequestParams();
+ var phoneHash = CryptTool.hashPhoneNo(phoneNumber);
+
+ try {
+ return doGet(
+ URI.create(
+ this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)),
+ getParams);
+ } catch (ThreemaError e) {
+ switch (e.getResponse().statusCode()) {
+ case 400:
+ throw new ClientError(e.getResponse(), "the hash length is wrong");
+ case 401:
+ throw new ClientError(e.getResponse(), "API identity or secret are incorrect");
+ case 404:
+ throw new ClientError(e.getResponse(), "no matching ID could be found");
+ case 500:
+ throw new ServerError(e.getResponse(), "a temporary internal server error occurs");
+ default:
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Lookup an ID by email address. The email address will be hashed before being sent to the
+ * server.
+ *
+ * @param email the email address
+ * @return the ID, or null if not found
+ * @throws IOException if a communication or server error occurs
+ */
+ public String lookupEmail(String email) throws IOException {
+
+ try {
+ var getParams = makeRequestParams();
+
+ var emailHash = CryptTool.hashEmail(email);
+
+ return doGet(
+ URI.create(
+ this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)),
+ getParams);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Lookup a public key by ID.
+ *
+ * @param id the ID whose public key is desired
+ * @return the corresponding public key, or null if not found
+ * @throws IOException if a communication or server error occurs
+ */
+ public byte[] lookupKey(String id) throws IOException {
+ var key = this.publicKeyStore.getPublicKey(id);
+ if (key == null) {
+ try {
+ var getParams = makeRequestParams();
+ var pubkeyHex = doGet(URI.create(this.apiUrl + "pubkeys/" + id), getParams);
+ key = DataUtils.hexStringToByteArray(pubkeyHex);
+
+ if (key != null) {
+ this.publicKeyStore.save(id, key);
+ }
+ } catch (FileNotFoundException e) {
+ return new byte[0];
+ }
+ }
+ return key;
+ }
+
+ /**
+ * Lookup the capabilities of a ID
+ *
+ * @param threemaId The ID whose capabilities should be checked
+ * @return The capabilities, or null if not found
+ * @throws IOException
+ */
+ public CapabilityResult lookupKeyCapability(String threemaId) throws IOException {
+ var res = doGet(URI.create(this.apiUrl + "capabilities/" + threemaId), makeRequestParams());
+ if (res != null) {
+ return new CapabilityResult(threemaId, res.split(","));
+ }
+ return null;
+ }
+
+ public Integer lookupCredits() throws IOException {
+ var res = doGet(URI.create(this.apiUrl + "credits"), makeRequestParams());
+ if (res != null) {
+ return Integer.valueOf(res);
+ }
+ return null;
+ }
+ /**
+ * Upload a file.
+ *
+ * @param fileEncryptionResult The result of the file encryption (i.e. encrypted file data)
+ * @return the result of the upload
+ * @throws IOException
+ */
+ public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOException {
+
+ var attachmentName = "blob";
+ var attachmentFileName = "blob.file";
+ var crlf = "\r\n";
+ var twoHyphens = "--";
+
+ var chars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+ var rand = new SecureRandom();
+ var count = rand.nextInt(11) + 30;
+ var boundary = new StringBuilder(count);
+ for (var i = 0; i < count; i++) {
+ boundary.append(chars[rand.nextInt(chars.length)]);
+ }
+
+ var queryString = makeUrlEncoded(makeRequestParams());
+ var url = new URL(this.apiUrl + "upload_blob?" + queryString);
+
+ var connection = (HttpsURLConnection) url.openConnection();
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setUseCaches(false);
+
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Connection", "Keep-Alive");
+ connection.setRequestProperty("Cache-Control", "no-cache");
+ connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
+
+ var request = new DataOutputStream(connection.getOutputStream());
+
+ request.writeBytes(twoHyphens + boundary + crlf);
+ request.writeBytes(
+ "Content-Disposition: form-data; name=\""
+ + attachmentName
+ + "\";filename=\""
+ + attachmentFileName
+ + "\""
+ + crlf);
+ request.writeBytes(crlf);
+ request.write(fileEncryptionResult.getResult());
+ request.writeBytes(crlf);
+ request.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
+
+ String response = null;
+ var responseCode = connection.getResponseCode();
+
+ if (responseCode == 200) {
+ var is = connection.getInputStream();
+ var br = new BufferedReader(new InputStreamReader(is));
+ response = br.readLine();
+ br.close();
+ }
+
+ connection.disconnect();
+
+ return new UploadResult(
+ responseCode, response != null ? DataUtils.hexStringToByteArray(response) : null);
+ }
+
+ /**
+ * Download a file given its blob ID.
+ *
+ * @param blobId The blob ID of the file
+ * @return Encrypted file data
+ * @throws IOException
+ */
+ public byte[] downloadFile(byte[] blobId) throws IOException {
+ return this.downloadFile(blobId, null);
+ }
+
+ /**
+ * Download a file given its blob ID.
+ *
+ * @param blobId The blob ID of the file
+ * @param progressListener An object that will receive progress information, or null
+ * @return Encrypted file data
+ * @throws IOException
+ */
+ public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) throws IOException {
+ var queryString = makeUrlEncoded(makeRequestParams());
+ var blobUrl =
+ new URL(
+ String.format(
+ "%sblobs/%s?%s", this.apiUrl, DataUtils.byteArrayToHexString(blobId), queryString));
+
+ var connection = (HttpsURLConnection) blobUrl.openConnection();
+ connection.setConnectTimeout(20 * 1000);
+ connection.setReadTimeout(20 * 1000);
+ connection.setDoOutput(false);
+
+ var inputStream = connection.getInputStream();
+ var contentLength = connection.getContentLength();
+ var isl = new InputStreamLength(inputStream, contentLength);
+
+ /* Content length known? */
+ byte[] blob;
+ if (isl.length != -1) {
+ blob = new byte[isl.length];
+ var offset = 0;
+ int readed;
+
+ while (offset < isl.length
+ && (readed = isl.inputStream.read(blob, offset, isl.length - offset)) != -1) {
+ offset += readed;
+
+ if (progressListener != null) {
+ progressListener.updateProgress(100 * offset / isl.length);
+ }
+ }
+
+ if (offset != isl.length) {
+ throw new IOException(
+ "Unexpected read size. current: " + offset + ", excepted: " + isl.length);
+ }
+ } else {
+ /* Content length is unknown - need to read until EOF */
+
+ var bos = new ByteArrayOutputStream();
+ var buffer = new byte[BUFFER_SIZE];
+
+ int read;
+ while ((read = isl.inputStream.read(buffer)) != -1) {
+ bos.write(buffer, 0, read);
+ }
+
+ blob = bos.toByteArray();
+ }
+ if (progressListener != null) {
+ progressListener.updateProgress(100);
+ }
+
+ return blob;
+ }
+
+ private Map makeRequestParams() {
+ return Map.of("from", apiIdentity, "secret", secret);
+ }
+
+ private String doGet(URI uri, Map getParams) throws IOException {
+ var uriWithParameters =
+ Optional.ofNullable(getParams)
+ .map(params -> URI.create(uri.toString() + "?" + makeUrlEncoded(params)))
+ .orElse(uri);
+
+ var httpRequest = HttpRequest.newBuilder().uri(uriWithParameters).GET().build();
+ try {
+ HttpResponse response = httpClient.send(httpRequest, BodyHandlers.ofString());
+
+ var statusCode = response.statusCode();
+ if (400 <= statusCode) {
+ throw new ThreemaError(response);
+ }
+
+ return response.body();
+
+ } catch (InterruptedException e) {
+ log.warn("Interrupted");
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ }
+
+ private String doPost(URL url, Map postParams) throws IOException {
+
+ var postData = makeUrlEncoded(postParams).getBytes(StandardCharsets.UTF_8);
+
+ var connection = (HttpsURLConnection) url.openConnection();
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setInstanceFollowRedirects(false);
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ connection.setRequestProperty("Charset", "utf-8");
+ connection.setRequestProperty("Content-Length", Integer.toString(postData.length));
+ connection.setUseCaches(false);
+
+ var os = connection.getOutputStream();
+ os.write(postData);
+ os.flush();
+ os.close();
+
+ var is = connection.getInputStream();
+ var br = new BufferedReader(new InputStreamReader(is));
+ var response = br.readLine();
+ br.close();
+
+ connection.disconnect();
+
+ return response;
+ }
+
+ private String makeUrlEncoded(Map params) {
+ var s = new StringBuilder();
+
+ for (Map.Entry param : params.entrySet()) {
+ if (s.length() > 0) {
+ s.append('&');
+ }
+
+ s.append(param.getKey());
+ s.append('=');
+ try {
+ s.append(URLEncoder.encode(param.getValue(), "UTF-8"));
+ } catch (UnsupportedEncodingException ignored) {
+ // ignored
+ }
+ }
+
+ return s.toString();
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/CryptTool.java b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java
new file mode 100644
index 0000000..a5ea37b
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java
@@ -0,0 +1,474 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.io.EndianUtils;
+
+import com.neilalexander.jnacl.NaCl;
+
+import net.klesatschke.threema.api.exceptions.BadMessageException;
+import net.klesatschke.threema.api.exceptions.DecryptionFailedException;
+import net.klesatschke.threema.api.exceptions.MessageParseException;
+import net.klesatschke.threema.api.exceptions.UnsupportedMessageTypeException;
+import net.klesatschke.threema.api.messages.DeliveryReceipt;
+import net.klesatschke.threema.api.messages.FileMessage;
+import net.klesatschke.threema.api.messages.ImageMessage;
+import net.klesatschke.threema.api.messages.TextMessage;
+import net.klesatschke.threema.api.messages.ThreemaMessage;
+import net.klesatschke.threema.api.results.EncryptResult;
+import net.klesatschke.threema.api.results.UploadResult;
+
+/** Contains static methods to do various Threema cryptography related tasks. */
+public class CryptTool {
+ private static final String HMAC_SHA256 = "HmacSHA256";
+
+ private CryptTool() {}
+
+ /* HMAC-SHA256 keys for email/mobile phone hashing */
+ private static final byte[] EMAIL_HMAC_KEY = {
+ (byte) 0x30,
+ (byte) 0xa5,
+ (byte) 0x50,
+ (byte) 0x0f,
+ (byte) 0xed,
+ (byte) 0x97,
+ (byte) 0x01,
+ (byte) 0xfa,
+ (byte) 0x6d,
+ (byte) 0xef,
+ (byte) 0xdb,
+ (byte) 0x61,
+ (byte) 0x08,
+ (byte) 0x41,
+ (byte) 0x90,
+ (byte) 0x0f,
+ (byte) 0xeb,
+ (byte) 0xb8,
+ (byte) 0xe4,
+ (byte) 0x30,
+ (byte) 0x88,
+ (byte) 0x1f,
+ (byte) 0x7a,
+ (byte) 0xd8,
+ (byte) 0x16,
+ (byte) 0x82,
+ (byte) 0x62,
+ (byte) 0x64,
+ (byte) 0xec,
+ (byte) 0x09,
+ (byte) 0xba,
+ (byte) 0xd7
+ };
+ private static final byte[] PHONENO_HMAC_KEY = {
+ (byte) 0x85,
+ (byte) 0xad,
+ (byte) 0xf8,
+ (byte) 0x22,
+ (byte) 0x69,
+ (byte) 0x53,
+ (byte) 0xf3,
+ (byte) 0xd9,
+ (byte) 0x6c,
+ (byte) 0xfd,
+ (byte) 0x5d,
+ (byte) 0x09,
+ (byte) 0xbf,
+ (byte) 0x29,
+ (byte) 0x55,
+ (byte) 0x5e,
+ (byte) 0xb9,
+ (byte) 0x55,
+ (byte) 0xfc,
+ (byte) 0xd8,
+ (byte) 0xaa,
+ (byte) 0x5e,
+ (byte) 0xc4,
+ (byte) 0xf9,
+ (byte) 0xfc,
+ (byte) 0xd8,
+ (byte) 0x69,
+ (byte) 0xe2,
+ (byte) 0x58,
+ (byte) 0x37,
+ (byte) 0x07,
+ (byte) 0x23
+ };
+
+ private static final byte[] FILE_NONCE = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ private static final byte[] FILE_THUMBNAIL_NONCE = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
+ };
+
+ private static final SecureRandom random = new SecureRandom();
+
+ /**
+ * Encrypt a text message.
+ *
+ * @param text the text to be encrypted (max. 3500 bytes)
+ * @param senderPrivateKey the private key of the sending ID
+ * @param recipientPublicKey the public key of the receiving ID
+ * @return encrypted result
+ */
+ public static EncryptResult encryptTextMessage(
+ String text, byte[] senderPrivateKey, byte[] recipientPublicKey) {
+ return encryptMessage(new TextMessage(text), senderPrivateKey, recipientPublicKey);
+ }
+
+ /**
+ * Encrypt an image message.
+ *
+ * @param encryptResult result of the image encryption
+ * @param uploadResult result of the upload
+ * @param senderPrivateKey the private key of the sending ID
+ * @param recipientPublicKey the public key of the receiving ID
+ * @return encrypted result
+ */
+ public static EncryptResult encryptImageMessage(
+ EncryptResult encryptResult,
+ UploadResult uploadResult,
+ byte[] senderPrivateKey,
+ byte[] recipientPublicKey) {
+ return encryptMessage(
+ new ImageMessage(
+ uploadResult.getBlobId(), encryptResult.getSize(), encryptResult.getNonce()),
+ senderPrivateKey,
+ recipientPublicKey);
+ }
+
+ /**
+ * Encrypt a file message.
+ *
+ * @param encryptResult result of the file data encryption
+ * @param uploadResult result of the upload
+ * @param mimeType MIME type of the file
+ * @param fileName File name
+ * @param fileSize Size of the file, in bytes
+ * @param uploadResultThumbnail result of thumbnail upload
+ * @param senderPrivateKey Private key of sender
+ * @param recipientPublicKey Public key of recipient
+ * @return Result of the file message encryption (not the same as the file data encryption!)
+ */
+ public static EncryptResult encryptFileMessage(
+ EncryptResult encryptResult,
+ UploadResult uploadResult,
+ String mimeType,
+ String fileName,
+ int fileSize,
+ UploadResult uploadResultThumbnail,
+ byte[] senderPrivateKey,
+ byte[] recipientPublicKey) {
+ return encryptMessage(
+ new FileMessage(
+ uploadResult.getBlobId(),
+ encryptResult.getSecret(),
+ mimeType,
+ fileName,
+ fileSize,
+ uploadResultThumbnail != null ? uploadResultThumbnail.getBlobId() : null),
+ senderPrivateKey,
+ recipientPublicKey);
+ }
+
+ private static EncryptResult encryptMessage(
+ ThreemaMessage threemaMessage, byte[] privateKey, byte[] publicKey) {
+ /* determine random amount of PKCS7 padding */
+ var padbytes = random.nextInt(254) + 1;
+
+ byte[] messageBytes;
+ try {
+ messageBytes = threemaMessage.getData();
+ } catch (BadMessageException e) {
+ return null;
+ }
+
+ /* prepend type byte (0x02) to message data */
+ var data = new byte[1 + messageBytes.length + padbytes];
+ data[0] = (byte) threemaMessage.getTypeCode();
+
+ System.arraycopy(messageBytes, 0, data, 1, messageBytes.length);
+
+ /* append padding */
+ for (var i = 0; i < padbytes; i++) {
+ data[i + 1 + messageBytes.length] = (byte) padbytes;
+ }
+
+ return encrypt(data, privateKey, publicKey);
+ }
+
+ /**
+ * Decrypt an NaCl box using the recipient's private key and the sender's public key.
+ *
+ * @param box The box to be decrypted
+ * @param privateKey The private key of the recipient
+ * @param publicKey The public key of the sender
+ * @param nonce The nonce that was used for encryption
+ * @return The decrypted data, or null if decryption failed
+ */
+ public static byte[] decrypt(byte[] box, byte[] privateKey, byte[] publicKey, byte[] nonce) {
+ return new NaCl(privateKey, publicKey).decrypt(box, nonce);
+ }
+
+ /**
+ * Decrypt symmetrically encrypted file data.
+ *
+ * @param fileData The encrypted file data
+ * @param secret The symmetric key that was used for encryption
+ * @return The decrypted file data, or null if decryption failed
+ */
+ public static byte[] decryptFileData(byte[] fileData, byte[] secret) {
+ return NaCl.symmetricDecryptData(fileData, secret, FILE_NONCE);
+ }
+
+ /**
+ * Decrypt symmetrically encrypted file thumbnail data.
+ *
+ * @param fileData The encrypted thumbnail data
+ * @param secret The symmetric key that was used for encryption
+ * @return The decrypted thumbnail data, or null if decryption failed
+ */
+ public static byte[] decryptFileThumbnailData(byte[] fileData, byte[] secret) {
+ return NaCl.symmetricDecryptData(fileData, secret, FILE_THUMBNAIL_NONCE);
+ }
+
+ /**
+ * Decrypt a message.
+ *
+ * @param box the box to be decrypted
+ * @param recipientPrivateKey the private key of the receiving ID
+ * @param senderPublicKey the public key of the sending ID
+ * @param nonce the nonce that was used for the encryption
+ * @return decrypted message (text or delivery receipt)
+ */
+ public static ThreemaMessage decryptMessage(
+ byte[] box, byte[] recipientPrivateKey, byte[] senderPublicKey, byte[] nonce)
+ throws MessageParseException {
+
+ var data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce);
+ if (data == null) {
+ throw new DecryptionFailedException();
+ }
+
+ /* remove padding */
+ var padbytes = data[data.length - 1] & 0xFF;
+ var realDataLength = data.length - padbytes;
+ if (realDataLength < 1) {
+ throw new BadMessageException(); /* Bad message padding */
+ }
+
+ /* first byte of data is type */
+ var type = data[0] & 0xFF;
+
+ switch (type) {
+ case TextMessage.TYPE_CODE:
+ /* Text message */
+ if (realDataLength < 2) {
+ throw new BadMessageException();
+ }
+
+ return new TextMessage(new String(data, 1, realDataLength - 1, StandardCharsets.UTF_8));
+
+ case DeliveryReceipt.TYPE_CODE:
+ /* Delivery receipt */
+ if (realDataLength < MessageId.MESSAGE_ID_LEN + 2
+ || (realDataLength - 2) % MessageId.MESSAGE_ID_LEN != 0) {
+ throw new BadMessageException();
+ }
+
+ var receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF);
+ if (receiptType == null) {
+ throw new BadMessageException();
+ }
+
+ List messageIds = new LinkedList<>();
+
+ var numMsgIds = (realDataLength - 2) / MessageId.MESSAGE_ID_LEN;
+ for (var i = 0; i < numMsgIds; i++) {
+ messageIds.add(new MessageId(data, 2 + i * MessageId.MESSAGE_ID_LEN));
+ }
+
+ return new DeliveryReceipt(receiptType, messageIds);
+
+ case ImageMessage.TYPE_CODE:
+ if (realDataLength != 1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES) {
+ System.out.println(String.valueOf(realDataLength));
+ System.out.println(String.valueOf(1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES));
+ throw new BadMessageException();
+ }
+ var blobId = new byte[ThreemaMessage.BLOB_ID_LEN];
+ System.arraycopy(data, 1, blobId, 0, ThreemaMessage.BLOB_ID_LEN);
+ var size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN);
+ var fileNonce = new byte[NaCl.NONCEBYTES];
+ System.arraycopy(data, 1 + 4 + ThreemaMessage.BLOB_ID_LEN, fileNonce, 0, nonce.length);
+
+ return new ImageMessage(blobId, size, fileNonce);
+
+ case FileMessage.TYPE_CODE:
+ try {
+ return FileMessage.fromString(new String(data, 1, realDataLength - 1, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new BadMessageException();
+ }
+
+ default:
+ throw new UnsupportedMessageTypeException();
+ }
+ }
+
+ /**
+ * Generate a new key pair.
+ *
+ * @param privateKey is used to return the generated private key (length must be
+ * NaCl.PRIVATEKEYBYTES)
+ * @param publicKey is used to return the generated public key (length must be
+ * NaCl.PUBLICKEYBYTES)
+ */
+ public static void generateKeyPair(byte[] privateKey, byte[] publicKey) {
+ if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) {
+ throw new IllegalArgumentException("Wrong key length");
+ }
+
+ NaCl.genkeypair(publicKey, privateKey);
+ }
+
+ /**
+ * Encrypt data using NaCl asymmetric ("box") encryption.
+ *
+ * @param data the data to be encrypted
+ * @param privateKey is used to return the generated private key (length must be
+ * NaCl.PRIVATEKEYBYTES)
+ * @param publicKey is used to return the generated public key (length must be
+ * NaCl.PUBLICKEYBYTES)
+ */
+ public static EncryptResult encrypt(byte[] data, byte[] privateKey, byte[] publicKey) {
+ if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) {
+ throw new IllegalArgumentException("Wrong key length");
+ }
+
+ var nonce = randomNonce();
+ var naCl = new NaCl(privateKey, publicKey);
+ return new EncryptResult(naCl.encrypt(data, nonce), null, nonce);
+ }
+
+ /**
+ * Encrypt file data using NaCl symmetric encryption with a random key.
+ *
+ * @param data the file contents to be encrypted
+ * @return the encryption result including the random key
+ */
+ public static EncryptResult encryptFileData(byte[] data) {
+ // create random key
+ var rnd = new SecureRandom();
+ var encryptionKey = new byte[NaCl.SYMMKEYBYTES];
+ rnd.nextBytes(encryptionKey);
+
+ // encrypt file data in-place
+ NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_NONCE);
+
+ return new EncryptResult(data, encryptionKey, FILE_NONCE);
+ }
+
+ /**
+ * Encrypt file thumbnail data using NaCl symmetric encryption with a random key.
+ *
+ * @param data the file contents to be encrypted
+ * @return the encryption result including the random key
+ */
+ public static EncryptResult encryptFileThumbnailData(byte[] data, byte[] encryptionKey) {
+ // encrypt file data in-place
+ NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_THUMBNAIL_NONCE);
+
+ return new EncryptResult(data, encryptionKey, FILE_THUMBNAIL_NONCE);
+ }
+
+ /**
+ * Hashes an email address for identity lookup.
+ *
+ * @param email the email address
+ * @return the raw hash
+ */
+ public static byte[] hashEmail(String email) {
+ try {
+ var emailMac = Mac.getInstance(HMAC_SHA256);
+ emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, HMAC_SHA256));
+ var normalizedEmail = email.toLowerCase().trim();
+ return emailMac.doFinal(normalizedEmail.getBytes("US-ASCII"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Hashes a phone number for identity lookup.
+ *
+ * @param phoneNo the phone number
+ * @return the raw hash
+ */
+ public static byte[] hashPhoneNo(String phoneNo) {
+ try {
+ var phoneMac = Mac.getInstance(HMAC_SHA256);
+ phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, HMAC_SHA256));
+ var normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", "");
+ return phoneMac.doFinal(normalizedPhoneNo.getBytes("US-ASCII"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Generate a random nonce.
+ *
+ * @return random nonce
+ */
+ public static byte[] randomNonce() {
+ var nonce = new byte[NaCl.NONCEBYTES];
+ random.nextBytes(nonce);
+ return nonce;
+ }
+
+ /**
+ * Return the public key that corresponds with a given private key.
+ *
+ * @param privateKey The private key whose public key should be derived
+ * @return The corresponding public key.
+ */
+ public static byte[] derivePublicKey(byte[] privateKey) {
+ return NaCl.derivePublicKey(privateKey);
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/DataUtils.java b/api/src/main/java/net/klesatschke/threema/api/DataUtils.java
new file mode 100644
index 0000000..be992b8
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/DataUtils.java
@@ -0,0 +1,142 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import net.klesatschke.threema.api.Key.KeyType;
+import net.klesatschke.threema.api.exceptions.InvalidKeyException;
+
+public class DataUtils {
+ private DataUtils() {}
+
+ /**
+ * Convert a string in hexadecimal representation to a byte array.
+ *
+ * @param s hex string
+ * @return decoded byte array
+ */
+ public static byte[] hexStringToByteArray(String s) {
+ var sc = s.replaceAll("[^0-9a-fA-F]", "");
+ var len = sc.length();
+ var data = new byte[len / 2];
+ for (var i = 0; i < len; i += 2) {
+ data[i / 2] =
+ (byte) ((Character.digit(sc.charAt(i), 16) << 4) + Character.digit(sc.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+ /**
+ * Convert a byte array into a hexadecimal string (lowercase).
+ *
+ * @param bytes the bytes to encode
+ * @return hex encoded string
+ */
+ public static String byteArrayToHexString(byte[] bytes) {
+ final char[] hexArray = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+ var hexChars = new char[bytes.length * 2];
+ int v;
+ for (var j = 0; j < bytes.length; j++) {
+ v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ /**
+ * Read hexadecimal data from a file and return it as a byte array.
+ *
+ * @param inFile input file
+ * @return the decoded data
+ * @throws java.io.IOException
+ */
+ public static byte[] readHexFile(File inFile) throws IOException {
+ try (var br = new BufferedReader(new FileReader(inFile))) {
+ return hexStringToByteArray(br.readLine().trim());
+ }
+ }
+
+ /**
+ * Write a byte array into a file in hexadecimal format.
+ *
+ * @param outFile output file
+ * @param data the data to be written
+ */
+ public static void writeHexFile(File outFile, byte[] data) throws IOException {
+ try (var fw = new FileWriter(outFile)) {
+ fw.write(byteArrayToHexString(data));
+ fw.write('\n');
+ }
+ }
+
+ /**
+ * Read an encoded key from a file and return it as a key instance.
+ *
+ * @param inFile input file
+ * @return the decoded key
+ * @throws java.io.IOException
+ */
+ public static Key readKeyFile(File inFile) throws IOException, InvalidKeyException {
+ try (var br = new BufferedReader(new FileReader(inFile))) {
+ return Key.decodeKey(br.readLine().trim());
+ }
+ }
+
+ /**
+ * Read an encoded key from a file and return it as a key instance.
+ *
+ * @param inFile input file
+ * @param expectedKeyType validates the key type (private or public)
+ * @return the decoded key
+ * @throws java.io.IOException
+ */
+ public static Key readKeyFile(File inFile, KeyType expectedKeyType)
+ throws IOException, InvalidKeyException {
+ try (var br = new BufferedReader(new FileReader(inFile))) {
+ return Key.decodeKey(br.readLine().trim(), expectedKeyType);
+ }
+ }
+
+ /**
+ * Write an encoded key to a file Encoded key format: type:hex_key.
+ *
+ * @param outFile output file
+ * @param key a key that will be encoded and written to a file
+ */
+ public static void writeKeyFile(File outFile, Key key) throws IOException {
+ try (var fw = new FileWriter(outFile)) {
+ fw.write(key.encode());
+ fw.write('\n');
+ }
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/Key.java b/api/src/main/java/net/klesatschke/threema/api/Key.java
new file mode 100644
index 0000000..faeda2c
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/Key.java
@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2022 Kai Klesatschke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import lombok.Value;
+import net.klesatschke.threema.api.exceptions.InvalidKeyException;
+
+/** Encapsulates an asymmetric key, either public or private. */
+@Value
+public class Key {
+ public static final String SEPARATOR = ":";
+
+ public enum KeyType {
+ PRIVATE,
+ PUBLIC;
+ }
+
+ private final KeyType type;
+ private final byte[] key;
+
+ /**
+ * Decodes and validates an encoded key. Encoded key format: type:hex_key
+ *
+ * @param encodedKey an encoded key
+ * @throws ch.threema.apitool.exceptions.InvalidKeyException
+ */
+ public static Key decodeKey(String encodedKey) throws InvalidKeyException {
+ // Split key and check length
+ var keyArray = encodedKey.split(Key.SEPARATOR);
+ if (keyArray.length != 2) {
+ throw new InvalidKeyException("Does not contain a valid key format");
+ }
+
+ // Unpack key
+ var keyType = KeyType.valueOf(keyArray[0].toUpperCase());
+ var keyContent = keyArray[1];
+
+ // Is this a valid hex key?
+ if (!keyContent.matches("[0-9a-fA-F]{64}")) {
+ throw new InvalidKeyException("Does not contain a valid key");
+ }
+
+ return new Key(keyType, DataUtils.hexStringToByteArray(keyContent));
+ }
+
+ /**
+ * Decodes and validates an encoded key. Encoded key format: type:hex_key
+ *
+ * @param encodedKey an encoded key
+ * @param expectedKeyType the expected type of the key
+ * @throws InvalidKeyException
+ */
+ public static Key decodeKey(String encodedKey, KeyType expectedKeyType)
+ throws InvalidKeyException {
+ var key = decodeKey(encodedKey);
+
+ // Check key type
+ if (!key.type.equals(expectedKeyType)) {
+ throw new InvalidKeyException("Expected key type: " + expectedKeyType + ", got: " + key.type);
+ }
+
+ return key;
+ }
+
+ /**
+ * Encodes a key.
+ *
+ * @return an encoded key
+ */
+ public String encode() {
+ return this.type.toString().toLowerCase()
+ + Key.SEPARATOR
+ + DataUtils.byteArrayToHexString(this.key);
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/MessageId.java b/api/src/main/java/net/klesatschke/threema/api/MessageId.java
similarity index 58%
rename from source/src/main/java/ch/threema/apitool/MessageId.java
rename to api/src/main/java/net/klesatschke/threema/api/MessageId.java
index 2c3d5b1..6cd3087 100644
--- a/source/src/main/java/ch/threema/apitool/MessageId.java
+++ b/api/src/main/java/net/klesatschke/threema/api/MessageId.java
@@ -22,38 +22,38 @@
* THE SOFTWARE
*/
-package ch.threema.apitool;
+package net.klesatschke.threema.api;
-/**
- * Encapsulates the 8-byte message IDs that Threema uses.
- */
+/** Encapsulates the 8-byte message IDs that Threema uses. */
public class MessageId {
- public static final int MESSAGE_ID_LEN = 8;
+ public static final int MESSAGE_ID_LEN = 8;
- private final byte[] messageId;
+ private final byte[] messageId;
- public MessageId(byte[] messageId) {
- if (messageId.length != MESSAGE_ID_LEN)
- throw new IllegalArgumentException("Bad message ID length");
+ public MessageId(byte[] messageId) {
+ if (messageId.length != MESSAGE_ID_LEN) {
+ throw new IllegalArgumentException("Bad message ID length");
+ }
- this.messageId = messageId;
- }
+ this.messageId = messageId;
+ }
- public MessageId(byte[] data, int offset) {
- if ((offset + MESSAGE_ID_LEN) > data.length)
- throw new IllegalArgumentException("Bad message ID buffer length");
+ public MessageId(byte[] data, int offset) {
+ if (offset + MESSAGE_ID_LEN > data.length) {
+ throw new IllegalArgumentException("Bad message ID buffer length");
+ }
- this.messageId = new byte[MESSAGE_ID_LEN];
- System.arraycopy(data, offset, this.messageId, 0, MESSAGE_ID_LEN);
- }
+ this.messageId = new byte[MESSAGE_ID_LEN];
+ System.arraycopy(data, offset, this.messageId, 0, MESSAGE_ID_LEN);
+ }
- public byte[] getMessageId() {
- return messageId;
- }
+ public byte[] getMessageId() {
+ return messageId;
+ }
- @Override
- public String toString() {
- return DataUtils.byteArrayToHexString(messageId);
- }
+ @Override
+ public String toString() {
+ return DataUtils.byteArrayToHexString(messageId);
+ }
}
diff --git a/api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java b/api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java
new file mode 100644
index 0000000..f155288
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2022 Kai Klesatschke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Stores and caches public keys for Threema users. Extend this class to provide your own storage
+ * implementation, e.g. in a file or database.
+ */
+public abstract class PublicKeyStore {
+ private final Map cache = new ConcurrentHashMap<>();
+
+ /**
+ * Get the public key for a given Threema ID. The cache is checked first; if it is not found in
+ * the cache, fetchPublicKey() is called.
+ *
+ * @param threemaId The Threema ID whose public key should be obtained
+ * @return The public key, or null if not found.
+ */
+ public final byte[] getPublicKey(String threemaId) {
+ return this.cache.computeIfAbsent(threemaId, this::fetchPublicKey);
+ }
+
+ /**
+ * Store the public key for a given Threema ID in the cache, and the underlying store.
+ *
+ * @param threemaId The Threema ID whose public key should be stored
+ * @param publicKey The corresponding public key.
+ */
+ public final void setPublicKey(String threemaId, byte[] publicKey) {
+ Optional.ofNullable(publicKey)
+ .ifPresent(
+ key ->
+ cache.compute(
+ threemaId,
+ (id, old) -> {
+ save(id, key);
+ return key;
+ }));
+ }
+
+ /**
+ * Fetch the public key for the given Threema ID from the store. Override to provide your own
+ * implementation to read from the store.
+ *
+ * @param threemaId The Threema ID whose public key should be obtained
+ * @return The public key, or null if not found.
+ */
+ protected abstract byte[] fetchPublicKey(String threemaId);
+
+ /**
+ * Save the public key for a given Threema ID in the store. Override to provide your own
+ * implementation to write to the store.
+ *
+ * @param threemaId The Threema ID whose public key should be stored
+ * @param publicKey The corresponding public key.
+ */
+ protected abstract void save(String threemaId, byte[] publicKey);
+}
diff --git a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java
similarity index 84%
rename from source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java
rename to api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java
index 1b4099d..265d952 100644
--- a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java
@@ -22,12 +22,10 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.exceptions;
+package net.klesatschke.threema.api.exceptions;
-/**
- * Exception that gets thrown if a message has a bad/illegal format after it has been decrypted.
- */
+/** Exception that gets thrown if a message has a bad/illegal format after it has been decrypted. */
public class BadMessageException extends MessageParseException {
- private static final long serialVersionUID = 4812096297596964107L;
+ private static final long serialVersionUID = 4812096297596964107L;
}
diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java
new file mode 100644
index 0000000..a2c1bd5
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java
@@ -0,0 +1,11 @@
+package net.klesatschke.threema.api.exceptions;
+
+import java.net.http.HttpResponse;
+
+public class ClientError extends ThreemaError {
+ private static final long serialVersionUID = 5586830942850922017L;
+
+ public ClientError(HttpResponse> response, String string) {
+ super(response, string);
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java
similarity index 88%
rename from source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java
rename to api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java
index 1992d37..83aacdc 100644
--- a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java
@@ -22,12 +22,13 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.exceptions;
+package net.klesatschke.threema.api.exceptions;
/**
- * Exception that gets thrown when decryption fails (because the keys are incorrect, or the data is corrupted).
+ * Exception that gets thrown when decryption fails (because the keys are incorrect, or the data is
+ * corrupted).
*/
public class DecryptionFailedException extends MessageParseException {
- private static final long serialVersionUID = 2523453399446307538L;
+ private static final long serialVersionUID = 2523453399446307538L;
}
diff --git a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java
similarity index 81%
rename from source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java
rename to api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java
index f213b55..d23f1c4 100644
--- a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java
@@ -22,15 +22,13 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.exceptions;
+package net.klesatschke.threema.api.exceptions;
-/**
- * Exception that gets thrown when an invalid key has been specified (e.g. wrong length).
- */
+/** Exception that gets thrown when an invalid key has been specified (e.g. wrong length). */
public class InvalidKeyException extends Exception {
- private static final long serialVersionUID = 7585373757748175309L;
+ private static final long serialVersionUID = 7585373757748175309L;
- public InvalidKeyException(String s) {
- super(s);
- }
+ public InvalidKeyException(String s) {
+ super(s);
+ }
}
diff --git a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java
similarity index 91%
rename from source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java
rename to api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java
index aaed853..35a9570 100644
--- a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java
@@ -22,12 +22,12 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.exceptions;
+package net.klesatschke.threema.api.exceptions;
/**
* Base class for exceptions that may occur when parsing/decrypting an encrypted Threema message.
*/
public class MessageParseException extends Exception {
- private static final long serialVersionUID = 6829629439344637547L;
+ private static final long serialVersionUID = 6829629439344637547L;
}
diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java
new file mode 100644
index 0000000..f78abd8
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java
@@ -0,0 +1,16 @@
+package net.klesatschke.threema.api.exceptions;
+
+import java.net.http.HttpResponse;
+
+public class ServerError extends ThreemaError {
+
+ private static final long serialVersionUID = 1306889474607193102L;
+
+ public ServerError(HttpResponse> response) {
+ super(response);
+ }
+
+ public ServerError(HttpResponse> response, String string) {
+ super(response, string);
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java
new file mode 100644
index 0000000..f621f6d
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java
@@ -0,0 +1,18 @@
+package net.klesatschke.threema.api.exceptions;
+
+import java.io.IOException;
+import java.net.http.HttpResponse;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+public class ThreemaError extends IOException {
+ private static final long serialVersionUID = -5089520812601719876L;
+ @Getter private final HttpResponse> response;
+
+ public ThreemaError(HttpResponse> response, String string) {
+ super(string);
+ this.response = response;
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java
similarity index 87%
rename from source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java
rename to api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java
index 76c9260..d6804b8 100644
--- a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java
+++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java
@@ -22,13 +22,13 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.exceptions;
+package net.klesatschke.threema.api.exceptions;
/**
- * Exception that gets thrown when an attempt has been made to decrypt a message
- * of a type that is not supported by this library.
+ * Exception that gets thrown when an attempt has been made to decrypt a message of a type that is
+ * not supported by this library.
*/
public class UnsupportedMessageTypeException extends MessageParseException {
- private static final long serialVersionUID = -686063411249892256L;
+ private static final long serialVersionUID = -686063411249892256L;
}
diff --git a/api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java b/api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java
new file mode 100644
index 0000000..a792b8b
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java
@@ -0,0 +1,117 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api.messages;
+
+import java.util.List;
+
+import net.klesatschke.threema.api.MessageId;
+
+/**
+ * A delivery receipt message that can be sent/received with end-to-end encryption via Threema. Each
+ * delivery receipt message confirms the receipt of one or multiple regular messages.
+ */
+public class DeliveryReceipt extends ThreemaMessage {
+
+ public static final int TYPE_CODE = 0x80;
+
+ private final Type receiptType;
+ private final List ackedMessageIds;
+
+ public DeliveryReceipt(Type receiptType, List ackedMessageIds) {
+ this.receiptType = receiptType;
+ this.ackedMessageIds = ackedMessageIds;
+ }
+
+ public Type getReceiptType() {
+ return receiptType;
+ }
+
+ public List getAckedMessageIds() {
+ return ackedMessageIds;
+ }
+
+ @Override
+ public int getTypeCode() {
+ return TYPE_CODE;
+ }
+
+ @Override
+ public String toString() {
+ var sb = new StringBuilder("Delivery receipt (");
+ sb.append(receiptType);
+ sb.append("): ");
+ var i = 0;
+ for (MessageId messageId : ackedMessageIds) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(messageId);
+ i++;
+ }
+ return sb.toString();
+ }
+
+ /**
+ * A delivery receipt type. The following types are defined:
+ *
+ *
+ *
RECEIVED: the message has been received and decrypted on the recipient's device
+ *
READ: the message has been shown to the user in the chat view (note that this status can
+ * be disabled)
+ *
USER_ACK: the user has explicitly acknowledged the message (usually by long-pressing it
+ * and choosing the "acknowledge" option)
+ *
+ */
+ public enum Type {
+ RECEIVED(1),
+ READ(2),
+ USER_ACK(3);
+
+ private final int code;
+
+ Type(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public static Type get(int code) {
+ for (Type t : values()) {
+ if (t.code == code) {
+ return t;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getData() {
+ // Not implemented yet
+ return new byte[0];
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java
new file mode 100644
index 0000000..2d7a736
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java
@@ -0,0 +1,159 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api.messages;
+
+import java.io.UnsupportedEncodingException;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+
+import net.klesatschke.threema.api.DataUtils;
+import net.klesatschke.threema.api.exceptions.BadMessageException;
+
+/** A file message that can be sent/received with end-to-end encryption via Threema. */
+public class FileMessage extends ThreemaMessage {
+ private static final String KEY_BLOB_ID = "b";
+ private static final String KEY_THUMBNAIL_BLOB_ID = "t";
+ private static final String KEY_ENCRYPTION_KEY = "k";
+ private static final String KEY_MIME_TYPE = "m";
+ private static final String KEY_FILE_NAME = "n";
+ private static final String KEY_FILE_SIZE = "s";
+ private static final String KEY_TYPE = "i";
+
+ public static final int TYPE_CODE = 0x17;
+
+ private final byte[] blobId;
+ private final byte[] encryptionKey;
+ private final String mimeType;
+ private final String fileName;
+ private final int fileSize;
+ private final byte[] thumbnailBlobId;
+
+ public FileMessage(
+ byte[] blobId,
+ byte[] encryptionKey,
+ String mimeType,
+ String fileName,
+ int fileSize,
+ byte[] thumbnailBlobId) {
+ this.blobId = blobId;
+ this.encryptionKey = encryptionKey;
+ this.mimeType = mimeType;
+ this.fileName = fileName;
+ this.fileSize = fileSize;
+ this.thumbnailBlobId = thumbnailBlobId;
+ }
+
+ public byte[] getBlobId() {
+ return this.blobId;
+ }
+
+ public byte[] getEncryptionKey() {
+ return this.encryptionKey;
+ }
+
+ public String getMimeType() {
+ return this.mimeType;
+ }
+
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ public int getFileSize() {
+ return this.fileSize;
+ }
+
+ public byte[] getThumbnailBlobId() {
+ return this.thumbnailBlobId;
+ }
+
+ @Override
+ public int getTypeCode() {
+ return TYPE_CODE;
+ }
+
+ @Override
+ public String toString() {
+ return "file message " + this.fileName;
+ }
+
+ @Override
+ public byte[] getData() throws BadMessageException {
+ var o = new JsonObject();
+ try {
+ o.addProperty(KEY_BLOB_ID, DataUtils.byteArrayToHexString(this.blobId));
+ o.addProperty(
+ KEY_THUMBNAIL_BLOB_ID,
+ this.thumbnailBlobId != null
+ ? DataUtils.byteArrayToHexString(this.thumbnailBlobId)
+ : null);
+ o.addProperty(KEY_ENCRYPTION_KEY, DataUtils.byteArrayToHexString(this.encryptionKey));
+ o.addProperty(KEY_MIME_TYPE, this.mimeType);
+ o.addProperty(KEY_FILE_NAME, this.fileName);
+ o.addProperty(KEY_FILE_SIZE, this.fileSize);
+ o.addProperty(KEY_TYPE, 0);
+ } catch (Exception e) {
+ throw new BadMessageException();
+ }
+
+ try {
+ return o.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ public static FileMessage fromString(String json) throws BadMessageException {
+ try {
+ var o = new Gson().fromJson(json, JsonObject.class);
+ byte[] encryptionKey =
+ DataUtils.hexStringToByteArray(o.get(KEY_ENCRYPTION_KEY).getAsString());
+ var mimeType = o.get(KEY_MIME_TYPE).getAsString();
+ var fileSize = o.get(KEY_FILE_SIZE).getAsInt();
+ byte[] blobId = DataUtils.hexStringToByteArray(o.get(KEY_BLOB_ID).getAsString());
+
+ String fileName;
+ byte[] thumbnailBlobId = null;
+
+ // optional field
+ if (o.has(KEY_THUMBNAIL_BLOB_ID)) {
+ thumbnailBlobId =
+ DataUtils.hexStringToByteArray(o.get(KEY_THUMBNAIL_BLOB_ID).getAsString());
+ }
+
+ if (o.has(KEY_FILE_NAME)) {
+ fileName = o.get(KEY_FILE_NAME).getAsString();
+ } else {
+ fileName = "unnamed";
+ }
+
+ return new FileMessage(blobId, encryptionKey, mimeType, fileName, fileSize, thumbnailBlobId);
+ } catch (JsonSyntaxException e) {
+ throw new BadMessageException();
+ }
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java
similarity index 52%
rename from source/src/main/java/ch/threema/apitool/messages/ImageMessage.java
rename to api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java
index 40e6369..958fba6 100644
--- a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java
+++ b/api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java
@@ -22,66 +22,62 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.messages;
+package net.klesatschke.threema.api.messages;
-import ch.threema.apitool.DataUtils;
-import com.neilalexander.jnacl.NaCl;
import org.apache.commons.io.EndianUtils;
-/**
- * An image message that can be sent/received with end-to-end encryption via Threema.
- */
-public class ImageMessage extends ThreemaMessage {
-
- public static final int TYPE_CODE = 0x02;
- private final byte[] blobId;
- private final int size;
- private final byte[] nonce;
-
-
- public ImageMessage(byte[] blobId, int size, byte[] nonce) {
-
- this.blobId = blobId;
- this.size = size;
- this.nonce = nonce;
- }
-
- public byte[] getBlobId() {
- return this.blobId;
- }
-
-
- public int getSize() {
- return this.size;
- }
-
-
- public byte[] getNonce() {
- return this.nonce;
- }
-
- @Override
- public int getTypeCode() {
- return TYPE_CODE;
- }
-
- @Override
- public String toString() {
- return "blob " + DataUtils.byteArrayToHexString(this.blobId);
- }
-
- @Override
- public byte[] getData() {
- byte[] data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES];
- int pos = 0;
- System.arraycopy(this.blobId, 0, data, pos, BLOB_ID_LEN);
- pos += BLOB_ID_LEN;
+import com.neilalexander.jnacl.NaCl;
- EndianUtils.writeSwappedInteger(data, pos, this.size);
- pos += 4;
+import net.klesatschke.threema.api.DataUtils;
- System.arraycopy(this.nonce, 0, data, pos, NaCl.NONCEBYTES);
- return data;
+/** An image message that can be sent/received with end-to-end encryption via Threema. */
+public class ImageMessage extends ThreemaMessage {
- }
+ public static final int TYPE_CODE = 0x02;
+ private final byte[] blobId;
+ private final int size;
+ private final byte[] nonce;
+
+ public ImageMessage(byte[] blobId, int size, byte[] nonce) {
+
+ this.blobId = blobId;
+ this.size = size;
+ this.nonce = nonce;
+ }
+
+ public byte[] getBlobId() {
+ return this.blobId;
+ }
+
+ public int getSize() {
+ return this.size;
+ }
+
+ public byte[] getNonce() {
+ return this.nonce;
+ }
+
+ @Override
+ public int getTypeCode() {
+ return TYPE_CODE;
+ }
+
+ @Override
+ public String toString() {
+ return "blob " + DataUtils.byteArrayToHexString(this.blobId);
+ }
+
+ @Override
+ public byte[] getData() {
+ var data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES];
+ var pos = 0;
+ System.arraycopy(this.blobId, 0, data, pos, BLOB_ID_LEN);
+ pos += BLOB_ID_LEN;
+
+ EndianUtils.writeSwappedInteger(data, pos, this.size);
+ pos += 4;
+
+ System.arraycopy(this.nonce, 0, data, pos, NaCl.NONCEBYTES);
+ return data;
+ }
}
diff --git a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java
similarity index 67%
rename from source/src/main/java/ch/threema/apitool/messages/TextMessage.java
rename to api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java
index 3a22e06..7a3e8fc 100644
--- a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java
+++ b/api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java
@@ -22,43 +22,41 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.messages;
+package net.klesatschke.threema.api.messages;
import java.io.UnsupportedEncodingException;
-/**
- * A text message that can be sent/received with end-to-end encryption via Threema.
- */
+/** A text message that can be sent/received with end-to-end encryption via Threema. */
public class TextMessage extends ThreemaMessage {
- public static final int TYPE_CODE = 0x01;
+ public static final int TYPE_CODE = 0x01;
- private final String text;
+ private final String text;
- public TextMessage(String text) {
- this.text = text;
- }
+ public TextMessage(String text) {
+ this.text = text;
+ }
- public String getText() {
- return text;
- }
+ public String getText() {
+ return text;
+ }
- @Override
- public int getTypeCode() {
- return TYPE_CODE;
- }
+ @Override
+ public int getTypeCode() {
+ return TYPE_CODE;
+ }
- @Override
- public String toString() {
- return text;
- }
+ @Override
+ public String toString() {
+ return text;
+ }
- @Override
- public byte[] getData() {
- try {
- return text.getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
+ @Override
+ public byte[] getData() {
+ try {
+ return text.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
}
diff --git a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java
similarity index 73%
rename from source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java
rename to api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java
index f96265b..e4131f9 100644
--- a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java
+++ b/api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java
@@ -22,24 +22,18 @@
* THE SOFTWARE
*/
-package ch.threema.apitool.messages;
+package net.klesatschke.threema.api.messages;
-import ch.threema.apitool.exceptions.BadMessageException;
+import net.klesatschke.threema.api.exceptions.BadMessageException;
-/**
- * Abstract base class of messages that can be sent with end-to-end encryption via Threema.
- */
+/** Abstract base class of messages that can be sent with end-to-end encryption via Threema. */
public abstract class ThreemaMessage {
- public static final int BLOB_ID_LEN = 16;
+ public static final int BLOB_ID_LEN = 16;
- /**
- * @return The message's raw content
- */
- public abstract byte[] getData() throws BadMessageException;
+ /** @return The message's raw content */
+ public abstract byte[] getData() throws BadMessageException;
- /**
- * @return the message's type code
- */
- public abstract int getTypeCode();
+ /** @return the message's type code */
+ public abstract int getTypeCode();
}
diff --git a/api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java b/api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java
new file mode 100644
index 0000000..21cd88e
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java
@@ -0,0 +1,88 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api.results;
+
+import lombok.Value;
+
+/** Result of a capability lookup */
+@Value
+public class CapabilityResult {
+ private final String key;
+
+ /** capabilities as a string array. */
+ private final String[] capabilities;
+
+ /** Get all capabilities as a string array. */
+ public String[] getCapabilities() {
+ return capabilities;
+ }
+
+ /** Check whether the Threema ID can receive text */
+ public boolean canText() {
+ return this.can("text");
+ }
+
+ /** Check whether the Threema ID can receive images */
+ public boolean canImage() {
+ return this.can("image");
+ }
+
+ /** Check whether the Threema ID can receive videos */
+ public boolean canVideo() {
+ return this.can("video");
+ }
+
+ /** Check whether the Threema ID can receive audio */
+ public boolean canAudio() {
+ return this.can("audio");
+ }
+
+ /** Check whether the Threema ID can receive files */
+ public boolean canFile() {
+ return this.can("file");
+ }
+
+ private boolean can(String key) {
+ for (String k : this.capabilities) {
+ if (k.equals(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ var b = new StringBuilder();
+ b.append(this.key).append(": ");
+ for (var n = 0; n < this.capabilities.length; n++) {
+ if (n > 0) {
+ b.append(",");
+ }
+ b.append(this.capabilities[n]);
+ }
+ return b.toString();
+ }
+}
diff --git a/api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java b/api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java
new file mode 100644
index 0000000..25aa3a4
--- /dev/null
+++ b/api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api.results;
+
+import lombok.Value;
+
+/** Result of a data encryption */
+@Value
+public class EncryptResult {
+ /** the encrypted data */
+ private final byte[] result;
+ /** the secret that was used for encryption (only for symmetric encryption, e.g. files) */
+ private final byte[] secret;
+ /** the nonce that was used for encryption */
+ private final byte[] nonce;
+
+ /** @return the size (in bytes) of the encrypted data */
+ public int getSize() {
+ return this.result.length;
+ }
+}
diff --git a/source/src/test/java/ch/threema/apitool/Assert.java b/api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java
similarity index 74%
rename from source/src/test/java/ch/threema/apitool/Assert.java
rename to api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java
index e3dce18..72595c5 100644
--- a/source/src/test/java/ch/threema/apitool/Assert.java
+++ b/api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java
@@ -21,18 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE
*/
-package ch.threema.apitool;
-import java.util.Arrays;
+package net.klesatschke.threema.api.results;
-/**
- * Assert
- */
-public class Assert extends org.junit.Assert {
- public static void assertEquals(byte[] expected, byte[] actual) {
- assertEquals(null, expected, actual);
- }
- public static void assertEquals(String message, byte[] expected, byte[] actual) {
- assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
- }
+import lombok.Value;
+
+/** Result of a file upload */
+@Value
+public class UploadResult {
+ private final int responseCode;
+ private final byte[] blobId;
+
+ /** @return whether the upload succeeded */
+ public boolean isSuccess() {
+ return this.responseCode == 200;
+ }
}
diff --git a/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java b/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java
new file mode 100644
index 0000000..a4a2d4b
--- /dev/null
+++ b/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java
@@ -0,0 +1,90 @@
+package net.klesatschke.threema.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIOException;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+
+import java.io.IOException;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.junit.jupiter.MockServerExtension;
+import org.mockserver.junit.jupiter.MockServerSettings;
+import org.mockserver.logging.MockServerLogger;
+import org.mockserver.socket.PortFactory;
+import org.mockserver.socket.tls.KeyStoreFactory;
+
+@ExtendWith(MockServerExtension.class)
+@ExtendWith(MockitoExtension.class)
+@MockServerSettings(ports = {8787, 8888})
+class APIConnectorTest {
+ private static ClientAndServer client;
+
+ @Mock PublicKeyStore keystore;
+
+ private APIConnector apiConnector;
+
+ @BeforeAll
+ static void startMockServer() {
+ // ensure all connection using HTTPS will use the SSL context defined by
+ // MockServer to allow dynamically generated certificates to be accepted
+ HttpsURLConnection.setDefaultSSLSocketFactory(
+ new KeyStoreFactory(new MockServerLogger()).sslContext().getSocketFactory());
+ client = ClientAndServer.startClientAndServer(PortFactory.findFreePort());
+ }
+
+ @BeforeEach
+ void setup() {
+ client.reset();
+ apiConnector =
+ new APIConnector("ID", "secret", "https://localhost:" + client.getPort() + "/", keystore);
+ }
+
+ @Test
+ void testLookupEmail() throws IOException {
+ // GIVEN
+ var email = "test@mail.box";
+ var hash = DataUtils.byteArrayToHexString(CryptTool.hashEmail(email));
+ var path = "/lookup/email_hash/" + hash;
+ var threemaID = "the ID";
+ client.when(request().withMethod("GET").withPath(path)).respond(response().withBody(threemaID));
+
+ assertThat(apiConnector.lookupEmail(email)).isEqualTo(threemaID);
+ }
+
+ @Test
+ void testLookupPhone() throws IOException {
+ // GIVEN
+ var number = "+49172989127128";
+ var hash = DataUtils.byteArrayToHexString(CryptTool.hashPhoneNo(number));
+ var path = "/lookup/phone_hash/" + hash;
+ var threemaID = "the ID";
+ client.when(request().withMethod("GET").withPath(path)).respond(response().withBody(threemaID));
+
+ assertThat(apiConnector.lookupPhone(number)).isEqualTo(threemaID);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {400, 401, 404, 500})
+ void testLookupPhoneErrors(int httpStatusCode) throws IOException {
+ // GIVEN
+ var number = "+49172989127128";
+ var hash = DataUtils.byteArrayToHexString(CryptTool.hashPhoneNo(number));
+ var path = "/lookup/phone_hash/" + hash;
+ client
+ .when(request().withMethod("GET").withPath(path))
+ .respond(response().withStatusCode(httpStatusCode));
+
+ assertThatIOException().isThrownBy(() -> apiConnector.lookupPhone(number));
+ }
+}
diff --git a/api/src/test/java/net/klesatschke/threema/api/Common.java b/api/src/test/java/net/klesatschke/threema/api/Common.java
new file mode 100644
index 0000000..bfe4e36
--- /dev/null
+++ b/api/src/test/java/net/klesatschke/threema/api/Common.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+/** Common Stuff */
+public abstract class Common {
+
+ public static final String myPrivateKey =
+ "private:94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
+ public static final String myPrivateKeyExtract =
+ "94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
+
+ public static final String myPublicKey =
+ "public:3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531";
+ public static final String myPublicKeyExtract =
+ "3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531";
+
+ public static final String otherPrivateKey =
+ "private:8318e05220acd38e97ba41a9a6318688214219916075ca060f9339a6d1f7fc29";
+ public static final String otherPublicKey =
+ "public:10ac7fd937eafb806f9a05bf9afa340a99387b0063cc9cb0d1ea5505d39cc076";
+
+ public static final String echochoPublicKey =
+ "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b";
+ public static final String randomNonce = "516f4f1562dda0704a7bae8997cf0b354c6980181152ac32";
+
+ public static boolean hasContent(byte[] byteArray) {
+ for (byte b : byteArray) {
+ if (b != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java b/api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java
new file mode 100644
index 0000000..14cf182
--- /dev/null
+++ b/api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import com.neilalexander.jnacl.NaCl;
+
+import net.klesatschke.threema.api.messages.TextMessage;
+
+class CryptToolTest {
+
+ @Test
+ void testRandomNonce() throws Exception {
+ var randomNonce = CryptTool.randomNonce();
+
+ // with a length of 24
+ assertThat(randomNonce).hasSize(NaCl.NONCEBYTES);
+ }
+
+ @Test
+ void testCreateKeyPair() {
+ var privateKey = new byte[NaCl.SECRETKEYBYTES];
+ var publicKey = new byte[NaCl.PUBLICKEYBYTES];
+
+ CryptTool.generateKeyPair(privateKey, publicKey);
+
+ assertThat(privateKey).isNotNull().satisfies(Common::hasContent);
+ assertThat(publicKey).isNotNull().satisfies(Common::hasContent);
+ }
+
+ @Test
+ void testDecrypt() throws Exception {
+ var nonce = "0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0";
+ var box =
+ "45181c7aed95a1c100b1b559116c61b43ce15d04014a805288b7d14bf3a993393264fe554794ce7d6007233e8ef5a0f1ccdd704f34e7c7b77c72c239182caf1d061d6fff6ffbbfe8d3b8f3475c2fe352e563aa60290c666b2e627761e32155e62f048b52ef2f39c13ac229f393c67811749467396ecd09f42d32a4eb419117d0451056ac18fac957c52b0cca67568e2d97e5a3fd829a77f914a1ad403c5909fd510a313033422ea5db71eaf43d483238612a54cb1ecfe55259b1de5579e67c6505df7d674d34a737edf721ea69d15b567bc2195ec67e172f3cb8d6842ca88c29138cc33e9351dbc1e4973a82e1cf428c1c763bb8f3eb57770f914a";
+
+ var privateKey = Key.decodeKey(Common.otherPrivateKey);
+ var publicKey = Key.decodeKey(Common.myPublicKey);
+
+ var message =
+ CryptTool.decryptMessage(
+ DataUtils.hexStringToByteArray(box),
+ privateKey.getKey(),
+ publicKey.getKey(),
+ DataUtils.hexStringToByteArray(nonce));
+
+ assertThat(message)
+ .isInstanceOf(TextMessage.class)
+ .extracting(msg -> ((TextMessage) msg).getText())
+ .isEqualTo("Dies ist eine Testnachricht. äöü");
+ }
+
+ @Test
+ void testEncrypt() throws Exception {
+ var text = "Dies ist eine Testnachricht. äöü";
+
+ var privateKey = Key.decodeKey(Common.myPrivateKey);
+ var publicKey = Key.decodeKey(Common.otherPublicKey);
+
+ var res = CryptTool.encryptTextMessage(text, privateKey.getKey(), publicKey.getKey());
+
+ assertThat(res).isNotNull();
+ assertThat(res.getNonce()).isNotNull().satisfies(Common::hasContent);
+ assertThat(res.getResult()).isNotNull().satisfies(Common::hasContent);
+ }
+
+ @Test
+ void testDerivePublicKey() throws Exception {
+ var privateKey = Key.decodeKey(Common.myPrivateKey);
+ var publicKey = Key.decodeKey(Common.myPublicKey);
+ var derivedPublicKey = CryptTool.derivePublicKey(privateKey.getKey());
+ assertThat(derivedPublicKey).isEqualTo(publicKey.getKey());
+ }
+}
diff --git a/api/src/test/java/net/klesatschke/threema/api/KeyTest.java b/api/src/test/java/net/klesatschke/threema/api/KeyTest.java
new file mode 100644
index 0000000..8510d78
--- /dev/null
+++ b/api/src/test/java/net/klesatschke/threema/api/KeyTest.java
@@ -0,0 +1,73 @@
+/*
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+
+package net.klesatschke.threema.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import org.junit.jupiter.api.Test;
+
+import net.klesatschke.threema.api.exceptions.InvalidKeyException;
+
+class KeyTest {
+
+ @Test
+ void testDecodeWrongKey() {
+ assertThatExceptionOfType(InvalidKeyException.class)
+ .isThrownBy(() -> Key.decodeKey("imnotarealkey"));
+ }
+
+ @Test
+ void testDecodeKeyPrivate() throws Exception {
+ var key =
+ Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234");
+
+ assertThat(key).isNotNull();
+ assertThat(key.getType()).isEqualTo(Key.KeyType.PRIVATE);
+ assertThat(key.getKey())
+ .isEqualTo(
+ DataUtils.hexStringToByteArray(
+ "1234567890123456789012345678901234567890123456789012345678901234"));
+ }
+
+ @Test
+ void testDecodeKeyPublic() throws Exception {
+ var key =
+ Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234");
+ assertThat(key).isNotNull();
+ assertThat(key.getType()).isEqualTo(Key.KeyType.PUBLIC);
+ assertThat(key.getKey())
+ .isEqualTo(
+ DataUtils.hexStringToByteArray(
+ "1234567890123456789012345678901234567890123456789012345678901234"));
+ }
+
+ @Test
+ void testEncodePrivate() throws Exception {
+ var keyAsByte = DataUtils.hexStringToByteArray(Common.myPrivateKeyExtract);
+ var key = new Key(Key.KeyType.PRIVATE, keyAsByte);
+ assertThat(key.getType()).isEqualTo(Key.KeyType.PRIVATE);
+ assertThat(key.getKey()).isEqualTo(keyAsByte);
+ assertThat(key.encode()).isEqualTo(Common.myPrivateKey);
+ }
+}
diff --git a/source/pom.xml b/cli/pom.xml
similarity index 53%
rename from source/pom.xml
rename to cli/pom.xml
index 4fafe59..e1b5937 100644
--- a/source/pom.xml
+++ b/cli/pom.xml
@@ -3,20 +3,14 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- ch.threema.apitool
- msgapi-sdk-java
- 1.1.4
- Threema MsgApi SDK
-
- UTF-8
- ${source.encoding}
- ${source.encoding}
-
-
- Threema GmbH, Staldenbachstrasse 11, 8808 Pfäffikon SZ, Schweiz
- https://www.threema.ch
-
- https://gateway.threema.ch/
+
+ net.klesatschke.threema
+ msgapi-sdk-java
+ 1.1.5-SNAPSHOT
+
+ cli
+ Threema Messsage Gateway CLI
+ https://github.com/lordyavin/threema-msgapi-sdk-javaMIT-License
@@ -24,52 +18,22 @@
repo
-
-
- junit
- junit
- 4.13.1
- test
-
-
- commons-io
- commons-io
- 2.10.0
-
-
- org.apache.commons
- commons-lang3
- 3.12.0
-
-
- com.google.code.gson
- gson
- 2.8.9
-
-
+
+ net.klesatschke.threema.cli.ConsoleMain
+
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.1
-
- ${source.encoding}
- 11
- 11
-
- org.apache.maven.pluginsmaven-jar-plugin
- 3.2.0
+ 3.3.0truelib/
- ch.threema.apitool.Console
+ ${mainClass}
@@ -81,7 +45,7 @@
- ch.threema.apitool.ConsoleMain
+ ${mainClass}
@@ -100,6 +64,13 @@
+
+
+ net.klesatschke.threema
+ api
+ 1.1.5-SNAPSHOT
+
+ github
diff --git a/cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java b/cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java
new file mode 100644
index 0000000..3949715
--- /dev/null
+++ b/cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java
@@ -0,0 +1,217 @@
+/*
+ * The MIT License (MIT)
+ * Copyright (c) 2015 Threema GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE
+ */
+package net.klesatschke.threema.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+
+import net.klesatschke.threema.api.APIConnector;
+import net.klesatschke.threema.api.CryptTool;
+import net.klesatschke.threema.cli.console.commands.CapabilityCommand;
+import net.klesatschke.threema.cli.console.commands.Command;
+import net.klesatschke.threema.cli.console.commands.CreditsCommand;
+import net.klesatschke.threema.cli.console.commands.DecryptAndDownloadCommand;
+import net.klesatschke.threema.cli.console.commands.DecryptCommand;
+import net.klesatschke.threema.cli.console.commands.DerivePublicKeyCommand;
+import net.klesatschke.threema.cli.console.commands.EncryptCommand;
+import net.klesatschke.threema.cli.console.commands.FetchPublicKey;
+import net.klesatschke.threema.cli.console.commands.GenerateKeyPairCommand;
+import net.klesatschke.threema.cli.console.commands.HashEmailCommand;
+import net.klesatschke.threema.cli.console.commands.HashPhoneCommand;
+import net.klesatschke.threema.cli.console.commands.IDLookupByEmail;
+import net.klesatschke.threema.cli.console.commands.IDLookupByPhoneNo;
+import net.klesatschke.threema.cli.console.commands.SendE2EFileMessageCommand;
+import net.klesatschke.threema.cli.console.commands.SendE2EImageMessageCommand;
+import net.klesatschke.threema.cli.console.commands.SendE2ETextMessageCommand;
+import net.klesatschke.threema.cli.console.commands.SendSimpleMessageCommand;
+
+/**
+ * Command line interface for {@link CryptTool} and {@link APIConnector} operations for testing
+ * purposes and simple invocation from other programming languages.
+ */
+public class ConsoleMain {
+
+ static class Commands {
+ protected final List commandGroups = new ArrayList<>();
+
+ public CommandGroup create(String description) {
+ var g = new CommandGroup(description);
+ this.commandGroups.add(g);
+ return g;
+ }
+
+ public ArgumentCommand find(String... arguments) {
+ if (arguments.length > 0) {
+ for (CommandGroup g : this.commandGroups) {
+ var c = g.find(arguments);
+ if (c != null) {
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ static class CommandGroup {
+ protected final String description;
+ protected List argumentCommands = new ArrayList<>();
+
+ CommandGroup(String description) {
+ this.description = description;
+ }
+
+ public CommandGroup add(Command command, String... arguments) {
+ this.argumentCommands.add(new ArgumentCommand(arguments, command));
+ return this;
+ }
+
+ public ArgumentCommand find(String... arguments) {
+ ArgumentCommand matchedArgumentCommand = null;
+ var argMatchedSize = -1;
+ for (ArgumentCommand c : this.argumentCommands) {
+ var matched = true;
+ var matchedSize = 0;
+ for (var n = 0; n < c.arguments.length; n++) {
+ if (n > arguments.length || !c.arguments[n].equals(arguments[n])) {
+ matched = false;
+ break;
+ }
+ matchedSize++;
+ }
+
+ if (matched && matchedSize > argMatchedSize) {
+ matchedArgumentCommand = c;
+ argMatchedSize = matchedSize;
+ }
+ }
+ return matchedArgumentCommand;
+ }
+ }
+
+ static class ArgumentCommand {
+ protected final String[] arguments;
+ protected final Command command;
+
+ ArgumentCommand(String[] arguments, Command command) {
+ this.arguments = arguments;
+ this.command = command;
+ }
+
+ public void run(String[] givenArguments) throws Exception {
+ if (givenArguments.length < this.arguments.length) {
+ throw new Exception("invalid arguments");
+ }
+
+ this.command.run(
+ ArrayUtils.subarray(givenArguments, this.arguments.length, givenArguments.length));
+ }
+ }
+
+ private static final Commands commands = new Commands();
+
+ public static void main(String[] args) throws Exception {
+
+ commands
+ .create("Local operations (no network communication)")
+ .add(new EncryptCommand(), "-e")
+ .add(new DecryptCommand(), "-d")
+ .add(new HashEmailCommand(), "-h", "-e")
+ .add(new HashPhoneCommand(), "-h", "-p")
+ .add(new GenerateKeyPairCommand(), "-g")
+ .add(new DerivePublicKeyCommand(), "-p");
+
+ commands
+ .create("Network operations")
+ .add(new SendSimpleMessageCommand(), "-s")
+ .add(new SendE2ETextMessageCommand(), "-S")
+ .add(new SendE2EImageMessageCommand(), "-S", "-i")
+ .add(new SendE2EFileMessageCommand(), "-S", "-f")
+ .add(new IDLookupByEmail(), "-l", "-e")
+ .add(new IDLookupByPhoneNo(), "-l", "-p")
+ .add(new FetchPublicKey(), "-l", "-k")
+ .add(new CapabilityCommand(), "-c")
+ .add(new DecryptAndDownloadCommand(), "-D")
+ .add(new CreditsCommand(), "-C");
+
+ var argumentCommand = commands.find(args);
+ if (argumentCommand == null) {
+ usage(args.length == 1 && "html".equals(args[0]));
+ } else {
+ argumentCommand.run(args);
+ }
+ }
+
+ private static void usage(boolean htmlOutput) {
+ if (!htmlOutput) {
+ System.out.println("version:" + ConsoleMain.class.getPackage().getImplementationVersion());
+
+ System.out.println("usage:\n");
+
+ System.out.println("General information");
+ System.out.println("-------------------\n");
+
+ System.out.println("Where a key needs to be specified, it can either be given directly as");
+ System.out.println("a command line parameter (in hex with a prefix indicating the type;");
+ System.out.println("not recommended on shared machines as other users may be able to see");
+ System.out.println("the arguments), or as the path to a file that it should be read from");
+ System.out.println("(file contents also in hex with the prefix).\n");
+ }
+
+ var groupDescriptionTemplate =
+ htmlOutput ? "
\n\n", description);
- } else {
- System.out.println(" " + WordUtils.wrap(description, 76, "\n ", false));
- System.out.println("");
- }
- }
- }
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/CryptTool.java b/source/src/main/java/ch/threema/apitool/CryptTool.java
deleted file mode 100644
index 688a798..0000000
--- a/source/src/main/java/ch/threema/apitool/CryptTool.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import ch.threema.apitool.exceptions.BadMessageException;
-import ch.threema.apitool.exceptions.DecryptionFailedException;
-import ch.threema.apitool.exceptions.MessageParseException;
-import ch.threema.apitool.exceptions.UnsupportedMessageTypeException;
-import ch.threema.apitool.messages.*;
-import ch.threema.apitool.results.EncryptResult;
-import ch.threema.apitool.results.UploadResult;
-import com.neilalexander.jnacl.NaCl;
-import org.apache.commons.io.EndianUtils;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.io.UnsupportedEncodingException;
-import java.security.SecureRandom;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Contains static methods to do various Threema cryptography related tasks.
- */
-public class CryptTool {
-
- /* HMAC-SHA256 keys for email/mobile phone hashing */
- private static final byte[] EMAIL_HMAC_KEY = new byte[] {(byte)0x30,(byte)0xa5,(byte)0x50,(byte)0x0f,(byte)0xed,(byte)0x97,(byte)0x01,(byte)0xfa,(byte)0x6d,(byte)0xef,(byte)0xdb,(byte)0x61,(byte)0x08,(byte)0x41,(byte)0x90,(byte)0x0f,(byte)0xeb,(byte)0xb8,(byte)0xe4,(byte)0x30,(byte)0x88,(byte)0x1f,(byte)0x7a,(byte)0xd8,(byte)0x16,(byte)0x82,(byte)0x62,(byte)0x64,(byte)0xec,(byte)0x09,(byte)0xba,(byte)0xd7};
- private static final byte[] PHONENO_HMAC_KEY = new byte[] {(byte)0x85,(byte)0xad,(byte)0xf8,(byte)0x22,(byte)0x69,(byte)0x53,(byte)0xf3,(byte)0xd9,(byte)0x6c,(byte)0xfd,(byte)0x5d,(byte)0x09,(byte)0xbf,(byte)0x29,(byte)0x55,(byte)0x5e,(byte)0xb9,(byte)0x55,(byte)0xfc,(byte)0xd8,(byte)0xaa,(byte)0x5e,(byte)0xc4,(byte)0xf9,(byte)0xfc,(byte)0xd8,(byte)0x69,(byte)0xe2,(byte)0x58,(byte)0x37,(byte)0x07,(byte)0x23};
-
- private static final byte[] FILE_NONCE = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01};
- private static final byte[] FILE_THUMBNAIL_NONCE = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02};
-
- private static final SecureRandom random = new SecureRandom();
-
- /**
- * Encrypt a text message.
- *
- * @param text the text to be encrypted (max. 3500 bytes)
- * @param senderPrivateKey the private key of the sending ID
- * @param recipientPublicKey the public key of the receiving ID
- * @return encrypted result
- */
- public static EncryptResult encryptTextMessage(String text, byte[] senderPrivateKey, byte[] recipientPublicKey) {
- return encryptMessage(new TextMessage(text), senderPrivateKey, recipientPublicKey);
- }
-
-
- /**
- * Encrypt an image message.
- *
- * @param encryptResult result of the image encryption
- * @param uploadResult result of the upload
- * @param senderPrivateKey the private key of the sending ID
- * @param recipientPublicKey the public key of the receiving ID
- * @return encrypted result
- */
- public static EncryptResult encryptImageMessage(EncryptResult encryptResult, UploadResult uploadResult, byte[] senderPrivateKey, byte[] recipientPublicKey) {
- return encryptMessage(
- new ImageMessage(uploadResult.getBlobId(),
- encryptResult.getSize(),
- encryptResult.getNonce()),
- senderPrivateKey,
- recipientPublicKey);
- }
-
- /**
- * Encrypt a file message.
- *
- * @param encryptResult result of the file data encryption
- * @param uploadResult result of the upload
- * @param mimeType MIME type of the file
- * @param fileName File name
- * @param fileSize Size of the file, in bytes
- * @param uploadResultThumbnail result of thumbnail upload
- * @param senderPrivateKey Private key of sender
- * @param recipientPublicKey Public key of recipient
- * @return Result of the file message encryption (not the same as the file data encryption!)
- */
- public static EncryptResult encryptFileMessage(EncryptResult encryptResult,
- UploadResult uploadResult,
- String mimeType,
- String fileName,
- int fileSize,
- UploadResult uploadResultThumbnail,
- byte[] senderPrivateKey, byte[] recipientPublicKey) {
- return encryptMessage(
- new FileMessage(uploadResult.getBlobId(),
- encryptResult.getSecret(),
- mimeType,
- fileName,
- fileSize,
- uploadResultThumbnail != null ? uploadResultThumbnail.getBlobId() : null),
- senderPrivateKey,
- recipientPublicKey);
- }
-
-
- private static EncryptResult encryptMessage(ThreemaMessage threemaMessage, byte[] privateKey, byte[] publicKey) {
- /* determine random amount of PKCS7 padding */
- int padbytes = random.nextInt(254) + 1;
-
- byte[] messageBytes;
- try {
- messageBytes = threemaMessage.getData();
- } catch (BadMessageException e) {
- return null;
- }
-
- /* prepend type byte (0x02) to message data */
- byte[] data = new byte[1 + messageBytes.length + padbytes];
- data[0] = (byte)threemaMessage.getTypeCode();
-
- System.arraycopy(messageBytes, 0, data, 1, messageBytes.length);
-
- /* append padding */
- for (int i = 0; i < padbytes; i++) {
- data[i + 1 + messageBytes.length] = (byte)padbytes;
- }
-
- return encrypt(data, privateKey, publicKey);
- }
-
- /**
- * Decrypt an NaCl box using the recipient's private key and the sender's public key.
- *
- * @param box The box to be decrypted
- * @param privateKey The private key of the recipient
- * @param publicKey The public key of the sender
- * @param nonce The nonce that was used for encryption
- * @return The decrypted data, or null if decryption failed
- */
- public static byte[] decrypt(byte[] box, byte[] privateKey, byte[] publicKey, byte[] nonce) {
- return new NaCl(privateKey, publicKey).decrypt(box, nonce);
- }
-
- /**
- * Decrypt symmetrically encrypted file data.
- *
- * @param fileData The encrypted file data
- * @param secret The symmetric key that was used for encryption
- * @return The decrypted file data, or null if decryption failed
- */
- public static byte[] decryptFileData(byte[] fileData, byte[] secret) {
- return NaCl.symmetricDecryptData(fileData, secret, FILE_NONCE);
- }
-
- /**
- * Decrypt symmetrically encrypted file thumbnail data.
- *
- * @param fileData The encrypted thumbnail data
- * @param secret The symmetric key that was used for encryption
- * @return The decrypted thumbnail data, or null if decryption failed
- */
- public static byte[] decryptFileThumbnailData(byte[] fileData, byte[] secret) {
- return NaCl.symmetricDecryptData(fileData, secret, FILE_THUMBNAIL_NONCE);
- }
-
- /**
- * Decrypt a message.
- *
- * @param box the box to be decrypted
- * @param recipientPrivateKey the private key of the receiving ID
- * @param senderPublicKey the public key of the sending ID
- * @param nonce the nonce that was used for the encryption
- * @return decrypted message (text or delivery receipt)
- */
- public static ThreemaMessage decryptMessage(byte[] box, byte[] recipientPrivateKey, byte[] senderPublicKey, byte[] nonce) throws MessageParseException {
-
- byte[] data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce);
- if (data == null)
- throw new DecryptionFailedException();
-
- /* remove padding */
- int padbytes = data[data.length-1] & 0xFF;
- int realDataLength = data.length - padbytes;
- if (realDataLength < 1)
- throw new BadMessageException(); /* Bad message padding */
-
- /* first byte of data is type */
- int type = data[0] & 0xFF;
-
- switch (type) {
- case TextMessage.TYPE_CODE:
- /* Text message */
- if (realDataLength < 2)
- throw new BadMessageException();
-
- try {
- return new TextMessage(new String(data, 1, realDataLength - 1, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- /* should never happen, UTF-8 is always supported */
- throw new RuntimeException(e);
- }
-
- case DeliveryReceipt.TYPE_CODE:
- /* Delivery receipt */
- if (realDataLength < MessageId.MESSAGE_ID_LEN + 2 || ((realDataLength - 2) % MessageId.MESSAGE_ID_LEN) != 0)
- throw new BadMessageException();
-
- DeliveryReceipt.Type receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF);
- if (receiptType == null)
- throw new BadMessageException();
-
- List messageIds = new LinkedList();
-
- int numMsgIds = ((realDataLength - 2) / MessageId.MESSAGE_ID_LEN);
- for (int i = 0; i < numMsgIds; i++) {
- messageIds.add(new MessageId(data, 2 + i*MessageId.MESSAGE_ID_LEN));
- }
-
- return new DeliveryReceipt(receiptType, messageIds);
-
- case ImageMessage.TYPE_CODE:
- if(realDataLength != (1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)) {
- System.out.println(String.valueOf(realDataLength));
- System.out.println(String.valueOf(1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES));
- throw new BadMessageException();
- }
- byte[] blobId = new byte[ThreemaMessage.BLOB_ID_LEN];
- System.arraycopy(data, 1, blobId, 0, ThreemaMessage.BLOB_ID_LEN);
- int size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN);
- byte[] fileNonce = new byte[NaCl.NONCEBYTES];
- System.arraycopy(data, 1 + 4 + ThreemaMessage.BLOB_ID_LEN, fileNonce, 0, nonce.length);
-
- return new ImageMessage(blobId, size, fileNonce);
-
- case FileMessage.TYPE_CODE:
- try {
- return FileMessage.fromString(new String(data, 1, realDataLength-1, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- throw new BadMessageException();
- }
-
- default:
- throw new UnsupportedMessageTypeException();
- }
- }
-
- /**
- * Generate a new key pair.
- *
- * @param privateKey is used to return the generated private key (length must be NaCl.PRIVATEKEYBYTES)
- * @param publicKey is used to return the generated public key (length must be NaCl.PUBLICKEYBYTES)
- */
- public static void generateKeyPair(byte[] privateKey, byte[] publicKey) {
- if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) {
- throw new IllegalArgumentException("Wrong key length");
- }
-
- NaCl.genkeypair(publicKey, privateKey);
- }
-
- /**
- * Encrypt data using NaCl asymmetric ("box") encryption.
- *
- * @param data the data to be encrypted
- * @param privateKey is used to return the generated private key (length must be NaCl.PRIVATEKEYBYTES)
- * @param publicKey is used to return the generated public key (length must be NaCl.PUBLICKEYBYTES)
- */
- public static EncryptResult encrypt(byte[] data,byte[] privateKey, byte[] publicKey) {
- if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) {
- throw new IllegalArgumentException("Wrong key length");
- }
-
- byte[] nonce = randomNonce();
- NaCl naCl = new NaCl(privateKey, publicKey);
- return new EncryptResult(naCl.encrypt(data, nonce), null, nonce);
- }
-
- /**
- * Encrypt file data using NaCl symmetric encryption with a random key.
- *
- * @param data the file contents to be encrypted
- * @return the encryption result including the random key
- */
- public static EncryptResult encryptFileData(byte[] data) {
- //create random key
- SecureRandom rnd = new SecureRandom();
- byte[] encryptionKey = new byte[NaCl.SYMMKEYBYTES];
- rnd.nextBytes(encryptionKey);
-
- //encrypt file data in-place
- NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_NONCE);
-
- return new EncryptResult(data, encryptionKey, FILE_NONCE);
- }
-
- /**
- * Encrypt file thumbnail data using NaCl symmetric encryption with a random key.
- *
- * @param data the file contents to be encrypted
- * @return the encryption result including the random key
- */
- public static EncryptResult encryptFileThumbnailData(byte[] data, byte[] encryptionKey) {
- // encrypt file data in-place
- NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_THUMBNAIL_NONCE);
-
- return new EncryptResult(data, encryptionKey, FILE_THUMBNAIL_NONCE);
- }
-
- /**
- * Hashes an email address for identity lookup.
- *
- * @param email the email address
- * @return the raw hash
- */
- public static byte[] hashEmail(String email) {
- try {
- Mac emailMac = Mac.getInstance("HmacSHA256");
- emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, "HmacSHA256"));
- String normalizedEmail = email.toLowerCase().trim();
- return emailMac.doFinal(normalizedEmail.getBytes("US-ASCII"));
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- /**
- * Hashes a phone number for identity lookup.
- *
- * @param phoneNo the phone number
- * @return the raw hash
- */
- public static byte[] hashPhoneNo(String phoneNo) {
- try {
- Mac phoneMac = Mac.getInstance("HmacSHA256");
- phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, "HmacSHA256"));
- String normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", "");
- return phoneMac.doFinal(normalizedPhoneNo.getBytes("US-ASCII"));
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- /**
- * Generate a random nonce.
- *
- * @return random nonce
- */
- public static byte[] randomNonce() {
- byte[] nonce = new byte[NaCl.NONCEBYTES];
- random.nextBytes(nonce);
- return nonce;
- }
-
- /**
- * Return the public key that corresponds with a given private key.
- *
- * @param privateKey The private key whose public key should be derived
- * @return The corresponding public key.
- */
- public static byte[] derivePublicKey(byte[] privateKey) {
- return NaCl.derivePublicKey(privateKey);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/DataUtils.java b/source/src/main/java/ch/threema/apitool/DataUtils.java
deleted file mode 100644
index f9ffa68..0000000
--- a/source/src/main/java/ch/threema/apitool/DataUtils.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import ch.threema.apitool.exceptions.InvalidKeyException;
-
-import java.io.*;
-
-public class DataUtils {
-
- /**
- * Convert a string in hexadecimal representation to a byte array.
- *
- * @param s hex string
- * @return decoded byte array
- */
- public static byte[] hexStringToByteArray(String s) {
- String sc = s.replaceAll("[^0-9a-fA-F]", "");
- int len = sc.length();
- byte[] data = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(sc.charAt(i), 16) << 4)
- + Character.digit(sc.charAt(i+1), 16));
- }
- return data;
- }
-
- /**
- * Convert a byte array into a hexadecimal string (lowercase).
- *
- * @param bytes the bytes to encode
- * @return hex encoded string
- */
- public static String byteArrayToHexString(byte[] bytes) {
- final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
- char[] hexChars = new char[bytes.length * 2];
- int v;
- for (int j = 0; j < bytes.length; j++) {
- v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- /**
- * Read hexadecimal data from a file and return it as a byte array.
- *
- * @param inFile input file
- * @return the decoded data
- * @throws java.io.IOException
- */
- public static byte[] readHexFile(File inFile) throws IOException {
- BufferedReader br = new BufferedReader(new FileReader(inFile));
- byte[] data = hexStringToByteArray(br.readLine().trim());
- br.close();
- return data;
- }
-
- /**
- * Write a byte array into a file in hexadecimal format.
- *
- * @param outFile output file
- * @param data the data to be written
- */
- public static void writeHexFile(File outFile, byte[] data) throws IOException {
- FileWriter fw = new FileWriter(outFile);
- fw.write(byteArrayToHexString(data));
- fw.write('\n');
- fw.close();
- }
-
- /**
- * Read an encoded key from a file and return it as a key instance.
- *
- * @param inFile input file
- * @return the decoded key
- * @throws java.io.IOException
- */
- public static Key readKeyFile(File inFile) throws IOException, InvalidKeyException {
- BufferedReader br = new BufferedReader(new FileReader(inFile));
- String encodedKey = br.readLine().trim();
- br.close();
- return Key.decodeKey(encodedKey);
- }
-
- /**
- * Read an encoded key from a file and return it as a key instance.
- *
- * @param inFile input file
- * @param expectedKeyType validates the key type (private or public)
- * @return the decoded key
- * @throws java.io.IOException
- */
- public static Key readKeyFile(File inFile, String expectedKeyType) throws IOException, InvalidKeyException {
- BufferedReader br = new BufferedReader(new FileReader(inFile));
- String encodedKey = br.readLine().trim();
- br.close();
- return Key.decodeKey(encodedKey, expectedKeyType);
- }
-
- /**
- * Write an encoded key to a file
- * Encoded key format: type:hex_key.
- *
- * @param outFile output file
- * @param key a key that will be encoded and written to a file
- */
- public static void writeKeyFile(File outFile, Key key) throws IOException {
- FileWriter fw = new FileWriter(outFile);
- fw.write(key.encode());
- fw.write('\n');
- fw.close();
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/Key.java b/source/src/main/java/ch/threema/apitool/Key.java
deleted file mode 100644
index d88cd79..0000000
--- a/source/src/main/java/ch/threema/apitool/Key.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import ch.threema.apitool.exceptions.InvalidKeyException;
-
-/**
- * Encapsulates an asymmetric key, either public or private.
- */
-public class Key {
- public static final String separator = ":";
-
- public static class KeyType {
- public static final String PRIVATE = "private";
- public static final String PUBLIC = "public";
- }
-
- /* Attributes */
- public byte[] key;
- public String type;
-
- public Key(String type, byte[] key) {
- this.key = key;
- this.type = type;
- }
-
- /**
- * Decodes and validates an encoded key.
- * Encoded key format: type:hex_key
- *
- * @param encodedKey an encoded key
- * @throws ch.threema.apitool.exceptions.InvalidKeyException
- */
- public static Key decodeKey(String encodedKey) throws InvalidKeyException {
- // Split key and check length
- String[] keyArray = encodedKey.split(Key.separator);
- if (keyArray.length != 2) {
- throw new InvalidKeyException("Does not contain a valid key format");
- }
-
- // Unpack key
- String keyType = keyArray[0];
- String keyContent = keyArray[1];
-
- // Is this a valid hex key?
- if (!keyContent.matches("[0-9a-fA-F]{64}")) {
- throw new InvalidKeyException("Does not contain a valid key");
- }
-
- return new Key(keyType, DataUtils.hexStringToByteArray(keyContent));
- }
-
- /**
- * Decodes and validates an encoded key.
- * Encoded key format: type:hex_key
- *
- * @param encodedKey an encoded key
- * @param expectedKeyType the expected type of the key
- * @throws InvalidKeyException
- */
- public static Key decodeKey(String encodedKey, String expectedKeyType) throws InvalidKeyException {
- Key key = decodeKey(encodedKey);
-
- // Check key type
- if (!key.type.equals(expectedKeyType)) {
- throw new InvalidKeyException("Expected key type: " + expectedKeyType + ", got: " + key.type);
- }
-
- return key;
- }
-
- /**
- * Encodes a key.
- *
- * @return an encoded key
- */
- public String encode() {
- return this.type + Key.separator + DataUtils.byteArrayToHexString(this.key);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java b/source/src/main/java/ch/threema/apitool/PublicKeyStore.java
deleted file mode 100644
index 741a0a0..0000000
--- a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Stores and caches public keys for Threema users. Extend this class to provide your
- * own storage implementation, e.g. in a file or database.
- */
-public abstract class PublicKeyStore {
- private final Map cache = new HashMap<>();
-
- /**
- * Get the public key for a given Threema ID. The cache is checked first; if it
- * is not found in the cache, fetchPublicKey() is called.
- *
- * @param threemaId The Threema ID whose public key should be obtained
- * @return The public key, or null if not found.
- */
- public final byte[] getPublicKey(String threemaId) {
- synchronized (this.cache) {
- byte[] pk = this.cache.get(threemaId);
-
- if (pk == null) {
- pk = this.fetchPublicKey(threemaId);
- this.cache.put(threemaId, pk);
- }
- return pk;
- }
-
- }
-
- /**
- * Store the public key for a given Threema ID in the cache, and the underlying store.
- *
- * @param threemaId The Threema ID whose public key should be stored
- * @param publicKey The corresponding public key.
- */
- public final void setPublicKey(String threemaId, byte[] publicKey) {
- if(publicKey != null) {
- synchronized (this.cache) {
- this.cache.put(threemaId, publicKey);
- this.save(threemaId, publicKey);
- }
- }
- }
-
- /**
- * Fetch the public key for the given Threema ID from the store. Override to provide
- * your own implementation to read from the store.
- *
- * @param threemaId The Threema ID whose public key should be obtained
- * @return The public key, or null if not found.
- */
- abstract protected byte[] fetchPublicKey(String threemaId);
-
- /**
- * Save the public key for a given Threema ID in the store. Override to provide
- * your own implementation to write to the store.
- *
- * @param threemaId The Threema ID whose public key should be stored
- * @param publicKey The corresponding public key.
- */
- abstract protected void save(String threemaId, byte[] publicKey);
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/Command.java b/source/src/main/java/ch/threema/apitool/console/commands/Command.java
deleted file mode 100644
index f8e136f..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/Command.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.APIConnector;
-import ch.threema.apitool.PublicKeyStore;
-import ch.threema.apitool.console.commands.fields.*;
-
-import java.io.*;
-import java.util.LinkedList;
-import java.util.List;
-
-abstract public class Command {
- private final List fields = new LinkedList<>();
- private final String subject;
- private final String description;
-
- public Command(String subject, String description) {
- this.subject = subject;
- this.description = description;
- }
-
- private void addField(Field f) {
- if(f.isRequired()) {
- int pos = this.fields.size();
- //add after last required
- for(int n = 0; n < this.fields.size(); n++) {
- if(!this.fields.get(n).isRequired()) {
- pos = n;
- break;
- }
- }
- this.fields.add(pos, f);
- }
- else {
- this.fields.add(f);
- }
- }
- protected TextField createTextField(String key) {
- return this.createTextField(key, true);
- }
- protected TextField createTextField(String key, boolean required) {
- TextField field = new TextField(key, required);
- this.addField(field);
- return field;
- }
-
- protected ThreemaIDField createThreemaId(String key) {
- return this.createThreemaId(key, true);
- }
-
- protected ThreemaIDField createThreemaId(String key, boolean required) {
- ThreemaIDField field = new ThreemaIDField(key, required);
- this.addField(field);
- return field;
- }
-
- protected PublicKeyField createPublicKeyField(String key) {
- return this.createPublicKeyField(key, true);
- }
-
- protected PublicKeyField createPublicKeyField(String key, boolean required) {
- PublicKeyField field = new PublicKeyField(key, required);
- this.addField(field);
- return field;
- }
-
- protected PrivateKeyField createPrivateKeyField(String key) {
- return this.createPrivateKeyField(key, true);
- }
-
- protected PrivateKeyField createPrivateKeyField(String key, boolean required) {
- PrivateKeyField field = new PrivateKeyField(key, required);
- this.addField(field);
- return field;
- }
-
- protected FileField createFileField(String key) {
- return this.createFileField(key, true);
- }
-
- protected FileField createFileField(String key, boolean required) {
- FileField field = new FileField(key, required);
- this.addField(field);
- return field;
- }
-
- protected FolderField createFolderField(String key) {
- return this.createFolderField(key, true);
- }
-
- protected FolderField createFolderField(String key, boolean required) {
- FolderField field = new FolderField(key, required);
- this.addField(field);
- return field;
- }
-
- protected ByteArrayField createByteArrayField(String key) {
- return this.createByteArrayField(key, true);
- }
-
- protected ByteArrayField createByteArrayField(String key, boolean required) {
- ByteArrayField field = new ByteArrayField(key, required);
- this.addField(field);
- return field;
- }
-
-
- protected APIConnector createConnector(String gatewayId, String secret) {
- return new APIConnector(gatewayId, secret, new PublicKeyStore() {
- @Override
- protected byte[] fetchPublicKey(String threemaId) {
- return null;
- }
-
- @Override
- protected void save(String threemaId, byte[] publicKey) {
- //do nothing
- }
- });
- }
-
- protected String readStream(InputStream stream, String charset) throws IOException {
- try {
- Reader reader = new BufferedReader(new InputStreamReader(stream, charset));
- StringBuilder builder = new StringBuilder();
- char[] buffer = new char[8192];
- int read;
- while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
- builder.append(buffer, 0, read);
- }
- return builder.toString();
- } finally {
- stream.close();
- }
- }
-
- public final void run(String[] arguments) throws Exception {
- int pos = 0;
- for(Field f: this.fields) {
- if(arguments.length > pos) {
- f.setValue(arguments[pos]);
- }
- pos++;
- }
-
- //validate
- for(Field f: this.fields) {
- if(!f.isValid()) {
- return;
- }
- }
-
- this.execute();
- }
-
- public final String getSubject() {
- return this.subject;
- }
-
- public final String getUsageArguments() {
- StringBuilder usage = new StringBuilder();
- for(Field f: this.fields) {
- usage.append(" ")
- .append(f.isRequired() ? "<" : "[")
- .append(f.getKey())
- .append(f.isRequired() ? ">" : "]");
- }
- return usage.toString().trim();
- }
-
- public final String getUsageDescription() {
- return this.description;
- }
-
- protected abstract void execute() throws Exception;
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java
deleted file mode 100644
index 2dd42f4..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.DataUtils;
-import ch.threema.apitool.console.commands.fields.*;
-import ch.threema.apitool.helpers.E2EHelper;
-
-import java.nio.file.Path;
-
-public class DecryptAndDownloadCommand extends Command {
- private final ThreemaIDField threemaId;
- private final ThreemaIDField fromField;
- private final TextField secretField;
- private final PrivateKeyField privateKeyField;
- private final ByteArrayField nonceField;
- private final FolderField outputFolderField;
- private final TextField messageIdField;
-
- public DecryptAndDownloadCommand() {
- super("Decrypt and download",
- "Decrypt a box (box from the stdin) message and download (if the message is a image or file message) the file(s) to the defined directory"
- );
- this.threemaId = this.createThreemaId("id");
- this.fromField = this.createThreemaId("from");
- this.secretField = this.createTextField("secret");
- this.privateKeyField = this.createPrivateKeyField("privateKey");
- this.messageIdField = this.createTextField("messageId");
- this.nonceField = this.createByteArrayField("nonce");
- this.outputFolderField = this.createFolderField("outputFolder", false);
- }
-
- @Override
- protected void execute() throws Exception {
- String id = this.threemaId.getValue();
- String from = this.fromField.getValue();
- String secret = this.secretField.getValue();
- byte[] privateKey = this.privateKeyField.getValue();
- byte[] nonce = this.nonceField.getValue();
- String messageId = this.messageIdField.getValue();
- Path outputFolder = this.outputFolderField.getValue();
-
- E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey);
-
- byte[] box = DataUtils.hexStringToByteArray(this.readStream(System.in, "UTF-8").trim());
-
- E2EHelper.ReceiveMessageResult res = e2EHelper.receiveMessage(id, messageId, box, nonce, outputFolder);
- System.out.println(res.toString());
- System.out.println(res.getFiles().toString());
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java
deleted file mode 100644
index fd74ad1..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.console.commands.fields.ByteArrayField;
-import ch.threema.apitool.console.commands.fields.PrivateKeyField;
-import ch.threema.apitool.console.commands.fields.PublicKeyField;
-import ch.threema.apitool.CryptTool;
-import ch.threema.apitool.DataUtils;
-import ch.threema.apitool.messages.ThreemaMessage;
-
-public class DecryptCommand extends Command {
- private final PrivateKeyField privateKeyField;
- private final PublicKeyField publicKeyField;
- private final ByteArrayField nonceField;
-
- public DecryptCommand() {
- super("Decrypt",
- "Decrypt standard input using the given recipient private key and sender public key. The nonce must be given on the command line, and the box (hex) on standard input. Prints the decrypted message to standard output.");
-
- this.privateKeyField = this.createPrivateKeyField("privateKey");
- this.publicKeyField = this.createPublicKeyField("publicKey");
- this.nonceField = this.createByteArrayField("nonce");
- }
-
- @Override
- protected void execute() throws Exception {
- byte[] privateKey = this.privateKeyField.getValue();
- byte[] publicKey = this.publicKeyField.getValue();
- byte[] nonce = this.nonceField.getValue();
-
- /* read box from stdin */
- byte[] box = DataUtils.hexStringToByteArray(readStream(System.in, "UTF-8"));
-
- ThreemaMessage message = CryptTool.decryptMessage(box, privateKey, publicKey, nonce);
-
- System.out.println(message);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java
deleted file mode 100644
index 8abaf04..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.console.commands.fields.PrivateKeyField;
-import ch.threema.apitool.console.commands.fields.PublicKeyField;
-import ch.threema.apitool.CryptTool;
-import ch.threema.apitool.DataUtils;
-import ch.threema.apitool.results.EncryptResult;
-
-public class EncryptCommand extends Command {
- private final PrivateKeyField privateKeyField;
- private final PublicKeyField publicKeyField;
-
- public EncryptCommand() {
- super("Encrypt",
- "Encrypt standard input using the given sender private key and recipient public key. Prints two lines to standard output: first the nonce (hex), and then the box (hex).");
-
- this.privateKeyField = this.createPrivateKeyField("privateKey");
- this.publicKeyField = this.createPublicKeyField("publicKey");
- }
-
- @Override
- protected void execute() throws Exception {
- byte[] privateKey = this.privateKeyField.getValue();
- byte[] publicKey = this.publicKeyField.getValue();
-
- /* read text from stdin */
- String text = readStream(System.in, "UTF-8").trim();
-
- EncryptResult res = CryptTool.encryptTextMessage(text, privateKey, publicKey);
-
- System.out.println(DataUtils.byteArrayToHexString(res.getNonce()));
- System.out.println(DataUtils.byteArrayToHexString(res.getResult()));
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java
deleted file mode 100644
index 3029618..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.console.commands.fields.FileField;
-import ch.threema.apitool.console.commands.fields.PrivateKeyField;
-import ch.threema.apitool.console.commands.fields.TextField;
-import ch.threema.apitool.console.commands.fields.ThreemaIDField;
-import ch.threema.apitool.helpers.E2EHelper;
-
-import java.io.File;
-
-public class SendE2EFileMessageCommand extends Command {
- private final ThreemaIDField threemaId;
- private final ThreemaIDField fromField;
- private final TextField secretField;
- private final PrivateKeyField privateKeyField;
- private final FileField fileField;
- private final FileField thumbnailField;
-
- public SendE2EFileMessageCommand() {
- super("Send End-to-End Encrypted File Message",
- "Encrypt the file (and thumbnail) and send a file message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."
- );
- this.threemaId = this.createThreemaId("to");
- this.fromField = this.createThreemaId("from");
- this.secretField = this.createTextField("secret");
- this.privateKeyField = this.createPrivateKeyField("privateKey");
- this.fileField = this.createFileField("file");
- this.thumbnailField = this.createFileField("thumbnail", false);
-
- }
-
- @Override
- protected void execute() throws Exception {
- String to = this.threemaId.getValue();
- String from = this.fromField.getValue();
- String secret = this.secretField.getValue();
- byte[] privateKey = this.privateKeyField.getValue();
- File file = this.fileField.getValue();
- File thumbnail = this.thumbnailField.getValue();
-
- E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey);
- String messageId = e2EHelper.sendFileMessage(to, file, thumbnail);
- System.out.println("MessageId: " + messageId);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java
deleted file mode 100644
index 14a248f..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.console.commands.fields.FolderField;
-import ch.threema.apitool.console.commands.fields.PrivateKeyField;
-import ch.threema.apitool.console.commands.fields.TextField;
-import ch.threema.apitool.console.commands.fields.ThreemaIDField;
-import ch.threema.apitool.helpers.E2EHelper;
-
-import java.nio.file.Path;
-
-public class SendE2EImageMessageCommand extends Command {
- private final ThreemaIDField toField;
- private final ThreemaIDField fromField;
- private final TextField secretField;
- private final PrivateKeyField privateKeyField;
- private final FolderField imageFilePath;
-
- public SendE2EImageMessageCommand() {
- super("Send End-to-End Encrypted Image Message",
- "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success.");
-
- this.toField = this.createThreemaId("to", true);
- this.fromField = this.createThreemaId("from", true);
- this.secretField = this.createTextField("secret", true);
- this.privateKeyField = this.createPrivateKeyField("privateKey", true);
- this.imageFilePath = this.createFolderField("imageFilePath", true);
- }
-
- @Override
- protected void execute() throws Exception {
- String to = this.toField.getValue();
- String from = this.fromField.getValue();
- String secret = this.secretField.getValue();
- byte[] privateKey = this.privateKeyField.getValue();
- Path imageFilePath = this.imageFilePath.getValue();
-
- E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey);
- String messageId = e2EHelper.sendImageMessage(to, imageFilePath.toString());
- System.out.println("MessageId: " + messageId);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java
deleted file mode 100644
index 1aaf576..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.console.commands.fields.PrivateKeyField;
-import ch.threema.apitool.console.commands.fields.TextField;
-import ch.threema.apitool.console.commands.fields.ThreemaIDField;
-import ch.threema.apitool.helpers.E2EHelper;
-
-public class SendE2ETextMessageCommand extends Command {
- private final ThreemaIDField threemaId;
- private final ThreemaIDField fromField;
- private final TextField secretField;
- private final PrivateKeyField privateKeyField;
-
- public SendE2ETextMessageCommand() {
- super("Send End-to-End Encrypted Text Message",
- "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success.");
- this.threemaId = this.createThreemaId("to");
- this.fromField = this.createThreemaId("from");
- this.secretField = this.createTextField("secret");
- this.privateKeyField = this.createPrivateKeyField("privateKey");
- }
-
- @Override
- protected void execute() throws Exception {
- String to = this.threemaId.getValue();
- String from = this.fromField.getValue();
- String secret = this.secretField.getValue();
- byte[] privateKey = this.privateKeyField.getValue();
-
- String text = this.readStream(System.in, "UTF-8").trim();
-
- E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey);
- String messageId = e2EHelper.sendTextMessage(to, text);
- System.out.println("MessageId: " + messageId);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java
deleted file mode 100644
index 1fbef43..0000000
--- a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.console.commands;
-
-import ch.threema.apitool.APIConnector;
-import ch.threema.apitool.console.commands.fields.TextField;
-import ch.threema.apitool.console.commands.fields.ThreemaIDField;
-
-public class SendSimpleMessageCommand extends Command {
- private final ThreemaIDField threemaId;
- private final ThreemaIDField fromField;
- private final TextField secretField;
-
- public SendSimpleMessageCommand() {
- super("Send Simple Message",
- "Send a message from standard input with server-side encryption to the given ID. 'from' is the API identity and 'secret' is the API secret. Returns the message ID on success.");
-
- this.threemaId = this.createThreemaId("to");
- this.fromField = this.createThreemaId("from");
- this.secretField = this.createTextField("secret");
- }
-
- @Override
- protected void execute() throws Exception {
- String to = this.threemaId.getValue();
- String from = this.fromField.getValue();
- String secret = this.secretField.getValue();
-
- String text = readStream(System.in, "UTF-8").trim();
-
- APIConnector apiConnector = this.createConnector(from, secret);
- String messageId = apiConnector.sendTextMessageSimple(to, text);
- System.out.println(messageId);
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java b/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java
deleted file mode 100644
index c8a2d7f..0000000
--- a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.helpers;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.io.IOUtils;
-
-import com.neilalexander.jnacl.NaCl;
-
-import ch.threema.apitool.APIConnector;
-import ch.threema.apitool.CryptTool;
-import ch.threema.apitool.exceptions.InvalidKeyException;
-import ch.threema.apitool.exceptions.MessageParseException;
-import ch.threema.apitool.exceptions.NotAllowedException;
-import ch.threema.apitool.messages.FileMessage;
-import ch.threema.apitool.messages.ImageMessage;
-import ch.threema.apitool.messages.ThreemaMessage;
-import ch.threema.apitool.results.CapabilityResult;
-import ch.threema.apitool.results.EncryptResult;
-import ch.threema.apitool.results.UploadResult;
-
-/**
- * Helper to handle Threema end-to-end encryption.
- */
-public class E2EHelper {
- private final APIConnector apiConnector;
- private final byte[] privateKey;
-
- public class ReceiveMessageResult {
- private final String messageId;
- private final ThreemaMessage message;
- protected List files = new ArrayList<>();
- protected List errors = new ArrayList<>();
-
- public ReceiveMessageResult(String messageId, ThreemaMessage message) {
- this.messageId = messageId;
- this.message = message;
- }
-
- public List getFiles() {
- return this.files;
- }
-
- public List getErrors() {
- return this.errors;
- }
-
- public String getMessageId() {
- return messageId;
- }
-
- public ThreemaMessage getMessage() {
- return message;
- }
- }
-
- public E2EHelper(APIConnector apiConnector, byte[] privateKey) {
- this.apiConnector = apiConnector;
- this.privateKey = privateKey;
- }
-
- /**
- * Encrypt a text message and send it to the given recipient.
- *
- * @param threemaId target Threema ID
- * @param text the text to send
- * @return generated message ID
- */
- public String sendTextMessage(String threemaId, String text) throws Exception {
- // fetch public key
- byte[] publicKey = this.apiConnector.lookupKey(threemaId);
-
- if (publicKey == null) {
- throw new Exception("invalid threema id");
- }
- EncryptResult res = CryptTool.encryptTextMessage(text, this.privateKey, publicKey);
-
- return this.apiConnector.sendE2EMessage(threemaId, res.getNonce(), res.getResult());
- }
-
- /**
- * Encrypt an image message and send it to the given recipient.
- *
- * @param threemaId target Threema ID
- * @param imageFilePath path to read image data from
- * @return generated message ID
- * @throws NotAllowedException
- * @throws IOException
- * @throws InvalidKeyException
- */
- public String sendImageMessage(String threemaId, String imageFilePath)
- throws NotAllowedException, IOException, InvalidKeyException {
- // fetch public key
- byte[] publicKey = this.apiConnector.lookupKey(threemaId);
-
- if (publicKey == null) {
- throw new InvalidKeyException("invalid threema id");
- }
-
- // check capability of a key
- CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId);
- if (capabilityResult == null || !capabilityResult.canImage()) {
- throw new NotAllowedException();
- }
-
- byte[] fileData = Files.readAllBytes(Paths.get(imageFilePath));
- if (fileData == null) {
- throw new IOException("invalid file");
- }
-
- // encrypt the image
- EncryptResult encryptResult = CryptTool.encrypt(fileData, this.privateKey, publicKey);
-
- // upload the image
- UploadResult uploadResult = apiConnector.uploadFile(encryptResult);
-
- if (!uploadResult.isSuccess()) {
- throw new IOException("could not upload file (upload response " + uploadResult.getResponseCode() + ")");
- }
-
- // send it
- EncryptResult imageMessage = CryptTool.encryptImageMessage(encryptResult, uploadResult, privateKey, publicKey);
-
- return apiConnector.sendE2EMessage(threemaId, imageMessage.getNonce(), imageMessage.getResult());
- }
-
- /**
- * Encrypt a file message and send it to the given recipient.
- * The thumbnailMessagePath can be null.
- *
- * @param threemaId target Threema ID
- * @param fileMessageFile the file to be sent
- * @param thumbnailMessagePath file for thumbnail; if not set, no thumbnail will be sent
- * @return generated message ID
- * @throws InvalidKeyException
- * @throws IOException
- * @throws NotAllowedException
- */
- public String sendFileMessage(String threemaId, File fileMessageFile, File thumbnailMessagePath)
- throws InvalidKeyException, IOException, NotAllowedException {
- // fetch public key
- byte[] publicKey = this.apiConnector.lookupKey(threemaId);
-
- if (publicKey == null) {
- throw new InvalidKeyException("invalid threema id");
- }
-
- // check capability of a key
- CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId);
- if (capabilityResult == null || !capabilityResult.canImage()) {
- throw new NotAllowedException();
- }
-
- if (!fileMessageFile.isFile()) {
- throw new IOException("invalid file");
- }
-
- byte[] fileData = this.readFile(fileMessageFile);
-
- if (fileData == null) {
- throw new IOException("invalid file");
- }
-
- // encrypt the image
- EncryptResult encryptResult = CryptTool.encryptFileData(fileData);
-
- // upload the image
- UploadResult uploadResult = apiConnector.uploadFile(encryptResult);
-
- if (!uploadResult.isSuccess()) {
- throw new IOException("could not upload file (upload response " + uploadResult.getResponseCode() + ")");
- }
-
- UploadResult uploadResultThumbnail = null;
-
- if (thumbnailMessagePath != null && thumbnailMessagePath.isFile()) {
- byte[] thumbnailData = this.readFile(thumbnailMessagePath);
- if (thumbnailData == null) {
- throw new IOException("invalid thumbnail file");
- }
-
- // encrypt the thumbnail
- EncryptResult encryptResultThumbnail = CryptTool.encryptFileThumbnailData(fileData,
- encryptResult.getSecret());
-
- // upload the thumbnail
- uploadResultThumbnail = this.apiConnector.uploadFile(encryptResultThumbnail);
- }
-
- // send it
- EncryptResult fileMessage = CryptTool.encryptFileMessage(encryptResult, uploadResult,
- Files.probeContentType(fileMessageFile.toPath()), fileMessageFile.getName(),
- (int) fileMessageFile.length(), uploadResultThumbnail, privateKey, publicKey);
-
- return this.apiConnector.sendE2EMessage(threemaId, fileMessage.getNonce(), fileMessage.getResult());
- }
-
- /**
- * Decrypt a Message and download the blobs of the Message (e.g. image or file)
- *
- * @param threemaId Threema ID of the sender
- * @param messageId Message ID
- * @param box Encrypted box data of the file/image message
- * @param nonce Nonce that was used for message encryption
- * @param outputFolder Output folder for storing decrypted images/files
- * @return result of message reception
- * @throws IOException
- * @throws InvalidKeyException
- * @throws MessageParseException
- */
- public ReceiveMessageResult receiveMessage(String threemaId, String messageId, byte[] box, byte[] nonce,
- Path outputFolder) throws IOException, InvalidKeyException, MessageParseException {
- // fetch public key
- byte[] publicKey = this.apiConnector.lookupKey(threemaId);
-
- if (publicKey == null) {
- throw new InvalidKeyException("invalid threema id");
- }
-
- ThreemaMessage message = CryptTool.decryptMessage(box, this.privateKey, publicKey, nonce);
- if (message == null) {
- return null;
- }
-
- ReceiveMessageResult result = new ReceiveMessageResult(messageId, message);
-
- if (message instanceof ImageMessage) {
- // download image
- ImageMessage imageMessage = (ImageMessage) message;
- byte[] fileData = this.apiConnector.downloadFile(imageMessage.getBlobId());
-
- if (fileData == null) {
- throw new MessageParseException();
- }
-
- byte[] decryptedFileContent = CryptTool.decrypt(fileData, privateKey, publicKey, imageMessage.getNonce());
- File imageFile = new File(outputFolder.toString() + "/" + messageId + ".jpg");
- FileOutputStream fos = new FileOutputStream(imageFile);
- fos.write(decryptedFileContent);
- fos.close();
-
- result.files.add(imageFile);
- } else if (message instanceof FileMessage) {
- // download file
- FileMessage fileMessage = (FileMessage) message;
- byte[] fileData = this.apiConnector.downloadFile(fileMessage.getBlobId());
-
- byte[] decryptedFileData = CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey());
- File file = new File(outputFolder.toString() + "/" + messageId + "-" + fileMessage.getFileName());
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(decryptedFileData);
- fos.close();
-
- result.files.add(file);
-
- if (fileMessage.getThumbnailBlobId() != null) {
- byte[] thumbnailData = this.apiConnector.downloadFile(fileMessage.getThumbnailBlobId());
-
- byte[] decryptedThumbnailData = CryptTool.decryptFileThumbnailData(thumbnailData,
- fileMessage.getEncryptionKey());
- File thumbnailFile = new File(outputFolder.toString() + "/" + messageId + "-thumbnail.jpg");
- fos = new FileOutputStream(thumbnailFile);
- fos.write(decryptedThumbnailData);
- fos.close();
-
- result.files.add(thumbnailFile);
- }
- }
-
- return result;
- }
-
- /**
- * Read file data from file - store at offset in byte array for in-place encryption
- *
- * @param file input file
- * @return file data with padding/offset for NaCl
- * @throws IOException
- */
- private byte[] readFile(File file) throws IOException {
- int fileLength = (int) file.length();
- byte[] fileData = new byte[fileLength + NaCl.BOXOVERHEAD];
- IOUtils.readFully(new FileInputStream(file), fileData, NaCl.BOXOVERHEAD, fileLength);
- return fileData;
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java b/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java
deleted file mode 100644
index 6f79f76..0000000
--- a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.messages;
-
-import ch.threema.apitool.MessageId;
-
-import java.util.List;
-
-/**
- * A delivery receipt message that can be sent/received with end-to-end encryption via Threema.
- * Each delivery receipt message confirms the receipt of one or multiple regular messages.
- */
-public class DeliveryReceipt extends ThreemaMessage {
-
- public static final int TYPE_CODE = 0x80;
-
- private final Type receiptType;
- private final List ackedMessageIds;
-
- public DeliveryReceipt(Type receiptType, List ackedMessageIds) {
- this.receiptType = receiptType;
- this.ackedMessageIds = ackedMessageIds;
- }
-
- public Type getReceiptType() {
- return receiptType;
- }
-
- public List getAckedMessageIds() {
- return ackedMessageIds;
- }
-
- @Override
- public int getTypeCode() {
- return TYPE_CODE;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("Delivery receipt (");
- sb.append(receiptType);
- sb.append("): ");
- int i = 0;
- for (MessageId messageId : ackedMessageIds) {
- if (i != 0)
- sb.append(", ");
- sb.append(messageId);
- i++;
- }
- return sb.toString();
- }
-
- /**
- * A delivery receipt type. The following types are defined:
- *
- *
- *
RECEIVED: the message has been received and decrypted on the recipient's device
- *
READ: the message has been shown to the user in the chat view
- * (note that this status can be disabled)
- *
USER_ACK: the user has explicitly acknowledged the message (usually by
- * long-pressing it and choosing the "acknowledge" option)
- *
- */
- public enum Type {
- RECEIVED(1), READ(2), USER_ACK(3);
-
- private final int code;
-
- Type(int code) {
- this.code = code;
- }
-
- public int getCode() {
- return code;
- }
-
- public static Type get(int code) {
- for (Type t : values()) {
- if (t.code == code)
- return t;
- }
- return null;
- }
- }
-
- @Override
- public byte[] getData() {
- //Not implemented yet
- return new byte[0];
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java b/source/src/main/java/ch/threema/apitool/messages/FileMessage.java
deleted file mode 100644
index f630c08..0000000
--- a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.messages;
-
-import java.io.UnsupportedEncodingException;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonSyntaxException;
-
-import ch.threema.apitool.DataUtils;
-import ch.threema.apitool.exceptions.BadMessageException;
-
-/**
- * A file message that can be sent/received with end-to-end encryption via Threema.
- */
-public class FileMessage extends ThreemaMessage {
- private final static String KEY_BLOB_ID = "b";
- private final static String KEY_THUMBNAIL_BLOB_ID = "t";
- private final static String KEY_ENCRYPTION_KEY = "k";
- private final static String KEY_MIME_TYPE = "m";
- private final static String KEY_FILE_NAME = "n";
- private final static String KEY_FILE_SIZE = "s";
- private final static String KEY_TYPE = "i";
-
- public static final int TYPE_CODE = 0x17;
-
- private final byte[] blobId;
- private final byte[] encryptionKey;
- private final String mimeType;
- private final String fileName;
- private final int fileSize;
- private final byte[] thumbnailBlobId;
-
- public FileMessage(byte[] blobId, byte[] encryptionKey, String mimeType, String fileName, int fileSize,
- byte[] thumbnailBlobId) {
- this.blobId = blobId;
- this.encryptionKey = encryptionKey;
- this.mimeType = mimeType;
- this.fileName = fileName;
- this.fileSize = fileSize;
- this.thumbnailBlobId = thumbnailBlobId;
- }
-
- public byte[] getBlobId() {
- return this.blobId;
- }
-
- public byte[] getEncryptionKey() {
- return this.encryptionKey;
- }
-
- public String getMimeType() {
- return this.mimeType;
- }
-
- public String getFileName() {
- return this.fileName;
- }
-
- public int getFileSize() {
- return this.fileSize;
- }
-
- public byte[] getThumbnailBlobId() {
- return this.thumbnailBlobId;
- }
-
- @Override
- public int getTypeCode() {
- return TYPE_CODE;
- }
-
- @Override
- public String toString() {
- return "file message " + this.fileName;
- }
-
- @Override
- public byte[] getData() throws BadMessageException {
- JsonObject o = new JsonObject();
- try {
- o.addProperty(KEY_BLOB_ID, DataUtils.byteArrayToHexString(this.blobId));
- o.addProperty(KEY_THUMBNAIL_BLOB_ID,
- this.thumbnailBlobId != null ? DataUtils.byteArrayToHexString(this.thumbnailBlobId) : null);
- o.addProperty(KEY_ENCRYPTION_KEY, DataUtils.byteArrayToHexString(this.encryptionKey));
- o.addProperty(KEY_MIME_TYPE, this.mimeType);
- o.addProperty(KEY_FILE_NAME, this.fileName);
- o.addProperty(KEY_FILE_SIZE, this.fileSize);
- o.addProperty(KEY_TYPE, 0);
- } catch (Exception e) {
- throw new BadMessageException();
- }
-
- try {
- return o.toString().getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
- public static FileMessage fromString(String json) throws BadMessageException {
- try {
- JsonObject o = new Gson().fromJson(json, JsonObject.class);
- byte[] encryptionKey = DataUtils.hexStringToByteArray(o.get(KEY_ENCRYPTION_KEY).getAsString());
- String mimeType = o.get(KEY_MIME_TYPE).getAsString();
- int fileSize = o.get(KEY_FILE_SIZE).getAsInt();
- byte[] blobId = DataUtils.hexStringToByteArray(o.get(KEY_BLOB_ID).getAsString());
-
- String fileName;
- byte[] thumbnailBlobId = null;
-
- // optional field
- if (o.has(KEY_THUMBNAIL_BLOB_ID)) {
- thumbnailBlobId = DataUtils.hexStringToByteArray(o.get(KEY_THUMBNAIL_BLOB_ID).getAsString());
- }
-
- if (o.has(KEY_FILE_NAME)) {
- fileName = o.get(KEY_FILE_NAME).getAsString();
- } else {
- fileName = "unnamed";
- }
-
- return new FileMessage(blobId, encryptionKey, mimeType, fileName, fileSize, thumbnailBlobId);
- } catch (JsonSyntaxException e) {
- throw new BadMessageException();
- }
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java b/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java
deleted file mode 100644
index 65284c9..0000000
--- a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.results;
-
-/**
- * Result of a capability lookup
- */
-public class CapabilityResult {
- private final String key;
- private final String[] capabilities;
-
- public CapabilityResult(String key, String[] capabilities) {
- this.key = key;
- this.capabilities = capabilities;
- }
-
- /**
- * Get all capabilities as a string array.
- */
- public String[] getCapabilities() {
- return capabilities;
- }
-
- /**
- * Check whether the Threema ID can receive text
- */
- public boolean canText() {
- return this.can("text");
- }
-
- /**
- * Check whether the Threema ID can receive images
- */
- public boolean canImage() {
- return this.can("image");
- }
-
- /**
- * Check whether the Threema ID can receive videos
- */
- public boolean canVideo() {
- return this.can("video");
- }
-
- /**
- * Check whether the Threema ID can receive audio
- */
- public boolean canAudio() {
- return this.can("audio");
- }
-
- /**
- * Check whether the Threema ID can receive files
- */
- public boolean canFile() {
- return this.can("file");
- }
-
- private boolean can(String key) {
- for(String k: this.capabilities) {
- if(k.equals(key)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder();
- b.append(this.key).append(": ");
- for(int n = 0; n < this.capabilities.length; n++) {
- if(n > 0) {
- b.append(",");
- }
- b.append(this.capabilities[n]);
- }
- return b.toString();
- }
-
- public String getKey() {
- return key;
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java b/source/src/main/java/ch/threema/apitool/results/EncryptResult.java
deleted file mode 100644
index 71fb91a..0000000
--- a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.results;
-
-/**
- * Result of a data encryption
- */
-public class EncryptResult {
- private final byte[] result;
- private final byte[] secret;
- private final byte[] nonce;
-
- public EncryptResult(byte[] result, byte[] secret, byte[] nonce) {
- this.result = result;
- this.secret = secret;
- this.nonce = nonce;
- }
-
- /**
- * @return the encrypted data
- */
- public byte[] getResult() {
- return this.result;
- }
-
- /**
- * @return the size (in bytes) of the encrypted data
- */
- public int getSize() {
- return this.result.length;
- }
-
- /**
- * @return the nonce that was used for encryption
- */
- public byte[] getNonce() {
- return this.nonce;
- }
-
- /**
- * @return the secret that was used for encryption (only for symmetric encryption, e.g. files)
- */
- public byte[] getSecret() {
- return secret;
- }
-}
diff --git a/source/src/main/java/ch/threema/apitool/results/UploadResult.java b/source/src/main/java/ch/threema/apitool/results/UploadResult.java
deleted file mode 100644
index 055b9ad..0000000
--- a/source/src/main/java/ch/threema/apitool/results/UploadResult.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool.results;
-
-/**
- * Result of a file upload
- */
-public class UploadResult {
- private final int responseCode;
- private final byte[] blobId;
-
- public UploadResult(int responseCode, byte[] blobId) {
- this.responseCode = responseCode;
- this.blobId = blobId;
- }
-
- /**
- * @return the blob ID that has been created
- */
- public byte[] getBlobId() {
- return this.blobId;
- }
-
- /**
- * @return whether the upload succeeded
- */
- public boolean isSuccess() {
- return this.responseCode == 200;
- }
-
- /**
- * @return the response code of the upload
- */
- public int getResponseCode() {
- return this.responseCode;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/NaCl.java b/source/src/main/java/com/neilalexander/jnacl/NaCl.java
deleted file mode 100644
index 5cddd5d..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/NaCl.java
+++ /dev/null
@@ -1,330 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl;
-
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Formatter;
-
-import com.neilalexander.jnacl.crypto.curve25519xsalsa20poly1305;
-import com.neilalexander.jnacl.crypto.xsalsa20;
-import com.neilalexander.jnacl.crypto.xsalsa20poly1305;
-
-public class NaCl {
- public static final int PUBLICKEYBYTES = 32;
- public static final int SECRETKEYBYTES = 32;
- public static final int BEFORENMBYTES = 32;
- public static final int NONCEBYTES = 24;
- public static final int ZEROBYTES = 32;
- public static final int BOXZEROBYTES = 16;
- public static final int BOXOVERHEAD = ZEROBYTES - BOXZEROBYTES;
- public static final int SYMMKEYBYTES = 32;
- public static final int STREAMKEYBYTES = 32;
-
- private final byte[] precomputed = new byte[BEFORENMBYTES];
-
- /* Perform self test before anything else */
- static {
- selfTest();
- }
-
- public NaCl(byte[] privatekey, byte[] publickey) {
- if (privatekey.length != SECRETKEYBYTES)
- throw new Error("Invalid private key length");
-
- if (publickey.length != PUBLICKEYBYTES)
- throw new Error("Invalid public key length");
-
- curve25519xsalsa20poly1305.crypto_box_beforenm(this.precomputed, publickey, privatekey);
- }
-
- public NaCl(String privatekey, String publickey) {
- this(getBinary(privatekey), getBinary(publickey));
- }
-
- public byte[] encrypt(byte[] input, byte[] nonce) {
- return encrypt(input, input.length, nonce);
- }
-
- public byte[] encrypt(byte[] input, int inputlength, byte[] nonce) {
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- byte[] output = new byte[inputlength + BOXOVERHEAD];
- curve25519xsalsa20poly1305.crypto_box_afternm_nopad(output, 0, input, 0, input.length, nonce, this.precomputed);
-
- return output;
- }
-
- public byte[] decrypt(byte[] input, byte[] nonce) {
- return decrypt(input, input.length, nonce);
- }
-
- public byte[] decrypt(byte[] input, int inputlength, byte[] nonce) {
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- if (inputlength < BOXOVERHEAD)
- return null;
-
- byte[] output = new byte[inputlength - BOXOVERHEAD];
- if (curve25519xsalsa20poly1305.crypto_box_open_afternm_nopad(output, 0, input, 0, input.length, nonce,
- this.precomputed) != 0)
- return null;
-
- return output;
- }
-
- public static void genkeypair(byte[] publickey, byte[] privatekey) {
- genkeypair(publickey, privatekey, null);
- }
-
- public static void genkeypair(byte[] publickey, byte[] privatekey, byte[] seed) {
- SecureRandom random = new SecureRandom();
-
- random.nextBytes(privatekey);
-
- if (seed != null) {
- if (seed.length != SECRETKEYBYTES)
- throw new Error("Invalid seed length");
-
- for (int i = 0; i < SECRETKEYBYTES; i++)
- privatekey[i] ^= seed[i];
- }
-
- curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey);
- }
-
- public static byte[] derivePublicKey(byte[] privatekey) {
- if (privatekey.length != SECRETKEYBYTES)
- throw new Error("Invalid private key length");
-
- byte[] publickey = new byte[PUBLICKEYBYTES];
- curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey);
- return publickey;
- }
-
- public static byte[] symmetricEncryptData(byte[] input, byte[] key, byte[] nonce) {
- if (key.length != SYMMKEYBYTES)
- throw new Error("Invalid symmetric key length");
-
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- byte[] output = new byte[input.length + BOXOVERHEAD];
- xsalsa20poly1305.crypto_secretbox_nopad(output, 0, input, 0, input.length, nonce, key);
-
- return output;
- }
-
- /**
- * In-place version of {@link #symmetricEncryptData(byte[], byte[], byte[])} that stores the output
- * in the same byte array as the input. The input data must begin at offset {@link #BOXOVERHEAD} in
- * the array (the first BOXOVERHEAD bytes are ignored and will be overwritten with the message
- * authentication code during encryption).
- *
- * @param io plaintext on input (starting at offset BOXOVERHEAD), ciphertext on return (full array)
- * @param key encryption key
- * @param nonce encryption nonce
- */
- public static void symmetricEncryptDataInplace(byte[] io, byte[] key, byte[] nonce) {
-
- if (key.length != SYMMKEYBYTES)
- throw new Error("Invalid symmetric key length");
-
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- if (io.length < BOXOVERHEAD)
- throw new Error("Invalid I/O length");
-
- xsalsa20poly1305.crypto_secretbox_nopad(io, 0, io, BOXOVERHEAD, io.length - BOXOVERHEAD, nonce, key);
- }
-
- public static byte[] symmetricDecryptData(byte[] input, byte[] key, byte[] nonce) {
- if (key.length != SYMMKEYBYTES)
- throw new Error("Invalid symmetric key length");
-
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- byte[] output = new byte[input.length - BOXOVERHEAD];
- if (xsalsa20poly1305.crypto_secretbox_open_nopad(output, 0, input, 0, input.length, nonce, key) != 0)
- return null;
-
- return output;
- }
-
- /**
- * In-place version of {@link #symmetricDecryptData(byte[], byte[], byte[])} that stores the output in
- * the same byte array as the input. Note that the decrypted output is shorter than the input, so the
- * last {@link #BOXOVERHEAD} bytes should be ignored in the decrypted output.
- *
- * @param io ciphertext on input (full array), plaintext on output (last BOXOVERHEAD bytes set to zero)
- * @param key encryption key
- * @param nonce encryption nonce
- * @return decryption successful true/false
- */
- public static boolean symmetricDecryptDataInplace(byte[] io, byte[] key, byte[] nonce) {
- if (key.length != SYMMKEYBYTES)
- throw new Error("Invalid symmetric key length");
-
- if (nonce.length != NONCEBYTES)
- throw new Error("Invalid nonce length");
-
- if (io.length < BOXOVERHEAD)
- throw new Error("Invalid I/O length");
-
- if (xsalsa20poly1305.crypto_secretbox_open_nopad(io, 0, io, 0, io.length, nonce, key) != 0)
- return false;
-
- /* zeroize last bytes */
- for (int i = io.length - BOXOVERHEAD; i < io.length; i++)
- io[i] = 0;
-
- return true;
- }
-
- public static byte[] streamCryptData(byte[] input, byte[] key, byte[] nonce) {
- if (key.length != STREAMKEYBYTES)
- throw new Error("Invalid symmetric key length");
-
- byte[] output = new byte[input.length];
- xsalsa20.crypto_stream_xor(output, input, input.length, nonce, key);
-
- return output;
- }
-
- public static byte[] getBinary(String s) {
- int len = s.length();
- byte[] data = new byte[len / 2];
-
- for (int i = 0; i < len; i += 2)
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
-
- return data;
- }
-
- public static String asHex(byte[] buf) {
- try (Formatter formatter = new Formatter()) {
- for (byte b : buf)
- formatter.format("%02x", b);
- return formatter.toString();
- }
- }
-
- public static String asHex(int[] buf) {
- try (Formatter formatter = new Formatter()) {
- for (int b : buf)
- formatter.format("%02x", b);
- return formatter.toString();
- }
- }
-
- public static void selfTest() {
- /* test vectors from tests/box.* in nacl distribution */
- byte alicepk[] = new byte[] { (byte) 0x85, (byte) 0x20, (byte) 0xf0, (byte) 0x09, (byte) 0x89, (byte) 0x30,
- (byte) 0xa7, (byte) 0x54, (byte) 0x74, (byte) 0x8b, (byte) 0x7d, (byte) 0xdc, (byte) 0xb4, (byte) 0x3e,
- (byte) 0xf7, (byte) 0x5a, (byte) 0x0d, (byte) 0xbf, (byte) 0x3a, (byte) 0x0d, (byte) 0x26, (byte) 0x38,
- (byte) 0x1a, (byte) 0xf4, (byte) 0xeb, (byte) 0xa4, (byte) 0xa9, (byte) 0x8e, (byte) 0xaa, (byte) 0x9b,
- (byte) 0x4e, (byte) 0x6a };
-
- byte alicesk[] = { (byte) 0x77, (byte) 0x07, (byte) 0x6d, (byte) 0x0a, (byte) 0x73, (byte) 0x18, (byte) 0xa5,
- (byte) 0x7d, (byte) 0x3c, (byte) 0x16, (byte) 0xc1, (byte) 0x72, (byte) 0x51, (byte) 0xb2, (byte) 0x66,
- (byte) 0x45, (byte) 0xdf, (byte) 0x4c, (byte) 0x2f, (byte) 0x87, (byte) 0xeb, (byte) 0xc0, (byte) 0x99,
- (byte) 0x2a, (byte) 0xb1, (byte) 0x77, (byte) 0xfb, (byte) 0xa5, (byte) 0x1d, (byte) 0xb9, (byte) 0x2c,
- (byte) 0x2a };
-
- byte bobpk[] = { (byte) 0xde, (byte) 0x9e, (byte) 0xdb, (byte) 0x7d, (byte) 0x7b, (byte) 0x7d, (byte) 0xc1,
- (byte) 0xb4, (byte) 0xd3, (byte) 0x5b, (byte) 0x61, (byte) 0xc2, (byte) 0xec, (byte) 0xe4, (byte) 0x35,
- (byte) 0x37, (byte) 0x3f, (byte) 0x83, (byte) 0x43, (byte) 0xc8, (byte) 0x5b, (byte) 0x78, (byte) 0x67,
- (byte) 0x4d, (byte) 0xad, (byte) 0xfc, (byte) 0x7e, (byte) 0x14, (byte) 0x6f, (byte) 0x88, (byte) 0x2b,
- (byte) 0x4f };
-
- byte bobsk[] = { (byte) 0x5d, (byte) 0xab, (byte) 0x08, (byte) 0x7e, (byte) 0x62, (byte) 0x4a, (byte) 0x8a,
- (byte) 0x4b, (byte) 0x79, (byte) 0xe1, (byte) 0x7f, (byte) 0x8b, (byte) 0x83, (byte) 0x80, (byte) 0x0e,
- (byte) 0xe6, (byte) 0x6f, (byte) 0x3b, (byte) 0xb1, (byte) 0x29, (byte) 0x26, (byte) 0x18, (byte) 0xb6,
- (byte) 0xfd, (byte) 0x1c, (byte) 0x2f, (byte) 0x8b, (byte) 0x27, (byte) 0xff, (byte) 0x88, (byte) 0xe0,
- (byte) 0xeb };
-
- byte nonce[] = { (byte) 0x69, (byte) 0x69, (byte) 0x6e, (byte) 0xe9, (byte) 0x55, (byte) 0xb6, (byte) 0x2b,
- (byte) 0x73, (byte) 0xcd, (byte) 0x62, (byte) 0xbd, (byte) 0xa8, (byte) 0x75, (byte) 0xfc, (byte) 0x73,
- (byte) 0xd6, (byte) 0x82, (byte) 0x19, (byte) 0xe0, (byte) 0x03, (byte) 0x6b, (byte) 0x7a, (byte) 0x0b,
- (byte) 0x37 };
-
- byte m[] = { (byte) 0xbe, (byte) 0x07, (byte) 0x5f, (byte) 0xc5, (byte) 0x3c, (byte) 0x81, (byte) 0xf2,
- (byte) 0xd5, (byte) 0xcf, (byte) 0x14, (byte) 0x13, (byte) 0x16, (byte) 0xeb, (byte) 0xeb, (byte) 0x0c,
- (byte) 0x7b, (byte) 0x52, (byte) 0x28, (byte) 0xc5, (byte) 0x2a, (byte) 0x4c, (byte) 0x62, (byte) 0xcb,
- (byte) 0xd4, (byte) 0x4b, (byte) 0x66, (byte) 0x84, (byte) 0x9b, (byte) 0x64, (byte) 0x24, (byte) 0x4f,
- (byte) 0xfc, (byte) 0xe5, (byte) 0xec, (byte) 0xba, (byte) 0xaf, (byte) 0x33, (byte) 0xbd, (byte) 0x75,
- (byte) 0x1a, (byte) 0x1a, (byte) 0xc7, (byte) 0x28, (byte) 0xd4, (byte) 0x5e, (byte) 0x6c, (byte) 0x61,
- (byte) 0x29, (byte) 0x6c, (byte) 0xdc, (byte) 0x3c, (byte) 0x01, (byte) 0x23, (byte) 0x35, (byte) 0x61,
- (byte) 0xf4, (byte) 0x1d, (byte) 0xb6, (byte) 0x6c, (byte) 0xce, (byte) 0x31, (byte) 0x4a, (byte) 0xdb,
- (byte) 0x31, (byte) 0x0e, (byte) 0x3b, (byte) 0xe8, (byte) 0x25, (byte) 0x0c, (byte) 0x46, (byte) 0xf0,
- (byte) 0x6d, (byte) 0xce, (byte) 0xea, (byte) 0x3a, (byte) 0x7f, (byte) 0xa1, (byte) 0x34, (byte) 0x80,
- (byte) 0x57, (byte) 0xe2, (byte) 0xf6, (byte) 0x55, (byte) 0x6a, (byte) 0xd6, (byte) 0xb1, (byte) 0x31,
- (byte) 0x8a, (byte) 0x02, (byte) 0x4a, (byte) 0x83, (byte) 0x8f, (byte) 0x21, (byte) 0xaf, (byte) 0x1f,
- (byte) 0xde, (byte) 0x04, (byte) 0x89, (byte) 0x77, (byte) 0xeb, (byte) 0x48, (byte) 0xf5, (byte) 0x9f,
- (byte) 0xfd, (byte) 0x49, (byte) 0x24, (byte) 0xca, (byte) 0x1c, (byte) 0x60, (byte) 0x90, (byte) 0x2e,
- (byte) 0x52, (byte) 0xf0, (byte) 0xa0, (byte) 0x89, (byte) 0xbc, (byte) 0x76, (byte) 0x89, (byte) 0x70,
- (byte) 0x40, (byte) 0xe0, (byte) 0x82, (byte) 0xf9, (byte) 0x37, (byte) 0x76, (byte) 0x38, (byte) 0x48,
- (byte) 0x64, (byte) 0x5e, (byte) 0x07, (byte) 0x05 };
-
- byte c_expected[] = { (byte) 0xf3, (byte) 0xff, (byte) 0xc7, (byte) 0x70, (byte) 0x3f, (byte) 0x94, (byte) 0x00,
- (byte) 0xe5, (byte) 0x2a, (byte) 0x7d, (byte) 0xfb, (byte) 0x4b, (byte) 0x3d, (byte) 0x33, (byte) 0x05,
- (byte) 0xd9, (byte) 0x8e, (byte) 0x99, (byte) 0x3b, (byte) 0x9f, (byte) 0x48, (byte) 0x68, (byte) 0x12,
- (byte) 0x73, (byte) 0xc2, (byte) 0x96, (byte) 0x50, (byte) 0xba, (byte) 0x32, (byte) 0xfc, (byte) 0x76,
- (byte) 0xce, (byte) 0x48, (byte) 0x33, (byte) 0x2e, (byte) 0xa7, (byte) 0x16, (byte) 0x4d, (byte) 0x96,
- (byte) 0xa4, (byte) 0x47, (byte) 0x6f, (byte) 0xb8, (byte) 0xc5, (byte) 0x31, (byte) 0xa1, (byte) 0x18,
- (byte) 0x6a, (byte) 0xc0, (byte) 0xdf, (byte) 0xc1, (byte) 0x7c, (byte) 0x98, (byte) 0xdc, (byte) 0xe8,
- (byte) 0x7b, (byte) 0x4d, (byte) 0xa7, (byte) 0xf0, (byte) 0x11, (byte) 0xec, (byte) 0x48, (byte) 0xc9,
- (byte) 0x72, (byte) 0x71, (byte) 0xd2, (byte) 0xc2, (byte) 0x0f, (byte) 0x9b, (byte) 0x92, (byte) 0x8f,
- (byte) 0xe2, (byte) 0x27, (byte) 0x0d, (byte) 0x6f, (byte) 0xb8, (byte) 0x63, (byte) 0xd5, (byte) 0x17,
- (byte) 0x38, (byte) 0xb4, (byte) 0x8e, (byte) 0xee, (byte) 0xe3, (byte) 0x14, (byte) 0xa7, (byte) 0xcc,
- (byte) 0x8a, (byte) 0xb9, (byte) 0x32, (byte) 0x16, (byte) 0x45, (byte) 0x48, (byte) 0xe5, (byte) 0x26,
- (byte) 0xae, (byte) 0x90, (byte) 0x22, (byte) 0x43, (byte) 0x68, (byte) 0x51, (byte) 0x7a, (byte) 0xcf,
- (byte) 0xea, (byte) 0xbd, (byte) 0x6b, (byte) 0xb3, (byte) 0x73, (byte) 0x2b, (byte) 0xc0, (byte) 0xe9,
- (byte) 0xda, (byte) 0x99, (byte) 0x83, (byte) 0x2b, (byte) 0x61, (byte) 0xca, (byte) 0x01, (byte) 0xb6,
- (byte) 0xde, (byte) 0x56, (byte) 0x24, (byte) 0x4a, (byte) 0x9e, (byte) 0x88, (byte) 0xd5, (byte) 0xf9,
- (byte) 0xb3, (byte) 0x79, (byte) 0x73, (byte) 0xf6, (byte) 0x22, (byte) 0xa4, (byte) 0x3d, (byte) 0x14,
- (byte) 0xa6, (byte) 0x59, (byte) 0x9b, (byte) 0x1f, (byte) 0x65, (byte) 0x4c, (byte) 0xb4, (byte) 0x5a,
- (byte) 0x74, (byte) 0xe3, (byte) 0x55, (byte) 0xa5 };
-
- /* encrypt data and compare with expected result */
- NaCl nacl = new NaCl(alicesk, bobpk);
- byte[] c = nacl.encrypt(m, nonce);
- if (!Arrays.equals(c, c_expected))
- throw new RuntimeException("Crypto self-test failed (1)");
-
- /* decrypt data and compare with plaintext */
- nacl = new NaCl(bobsk, alicepk);
- byte[] p_d = nacl.decrypt(c, nonce);
- if (!Arrays.equals(p_d, m))
- throw new RuntimeException("Crypto self-test failed (2)");
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java
deleted file mode 100644
index 3079e04..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java
+++ /dev/null
@@ -1,466 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class curve25519
-{
- final int CRYPTO_BYTES = 32;
- final int CRYPTO_SCALARBYTES = 32;
-
- static byte[] basev = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- static int[] minusp = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
-
- public static int crypto_scalarmult_base(byte[] q, byte[] n)
- {
- byte[] basevp = basev;
- return crypto_scalarmult(q, n, basevp);
- }
-
- static void add(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset)
- {
- int u = 0;
-
- for (int j = 0; j < 31; ++j)
- {
- u += a[aoffset + j] + b[boffset + j];
- outv[outvoffset + j] = u & 255;
- u >>>= 8;
- }
-
- u += a[aoffset + 31] + b[boffset + 31];
- outv[outvoffset + 31] = u;
- }
-
- static void sub(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset)
- {
- int u = 218;
-
- for (int j = 0; j < 31; ++j)
- {
- u += a[aoffset + j] + 65280 - b[boffset + j];
- outv[outvoffset + j] = u & 255;
- u >>>= 8;
- }
-
- u += a[aoffset + 31] - b[boffset + 31];
- outv[outvoffset + 31] = u;
- }
-
- static void squeeze(int[] a, int aoffset)
- {
- int u = 0;
-
- for (int j = 0; j < 31; ++j)
- {
- u += a[aoffset + j];
- a[aoffset + j] = u & 255;
- u >>>= 8;
- }
-
- u += a[aoffset + 31];
- a[aoffset + 31] = u & 127;
- u = 19 * (u >>> 7);
-
- for (int j = 0; j < 31; ++j)
- {
- u += a[aoffset + j];
- a[aoffset + j] = u & 255;
- u >>>= 8;
- }
-
- u += a[aoffset + 31];
- a[aoffset + 31] = u;
- }
-
- static void freeze(int[] a, int aoffset)
- {
- int[] aorig = new int[32];
-
- for (int j = 0; j < 32; ++j)
- aorig[j] = a[aoffset + j];
-
- int[] minuspp = minusp;
-
- add(a, 0, a, 0, minuspp, 0);
-
- int negative = (int) (-((a[aoffset + 31] >>> 7) & 1));
-
- for (int j = 0; j < 32; ++j)
- a[aoffset + j] ^= negative & (aorig[j] ^ a[aoffset + j]);
- }
-
- static void mult(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset)
- {
- int j;
-
- for (int i = 0; i < 32; ++i)
- {
- int u = 0;
-
- for (j = 0; j <= i; ++j)
- u += a[aoffset + j] * b[boffset + i - j];
-
- for (j = i + 1; j < 32; ++j)
- u += 38 * a[aoffset + j] * b[boffset + i + 32 - j];
-
- outv[outvoffset + i] = u;
- }
-
- squeeze(outv, outvoffset);
- }
-
- static void mult121665(int[] outv, int[] a)
- {
- int j;
- int u = 0;
-
- for (j = 0; j < 31; ++j)
- {
- u += 121665 * a[j];
- outv[j] = u & 255;
- u >>>= 8;
- }
-
- u += 121665 * a[31];
- outv[31] = u & 127;
- u = 19 * (u >>> 7);
-
- for (j = 0; j < 31; ++j)
- {
- u += outv[j];
- outv[j] = u & 255;
- u >>>= 8;
- }
-
- u += outv[j];
- outv[j] = u;
- }
-
- static void square(int[] outv, int outvoffset, int[] a, int aoffset)
- {
- int j;
-
- for (int i = 0; i < 32; ++i)
- {
- int u = 0;
-
- for (j = 0; j < i - j; ++j)
- u += a[aoffset + j] * a[aoffset + i - j];
-
- for (j = i + 1; j < i + 32 - j; ++j)
- u += 38 * a[aoffset + j] * a[aoffset + i + 32 - j];
-
- u *= 2;
-
- if ((i & 1) == 0)
- {
- u += a[aoffset + i / 2] * a[aoffset + i / 2];
- u += 38 * a[aoffset + i / 2 + 16] * a[aoffset + i / 2 + 16];
- }
-
- outv[outvoffset + i] = u;
- }
-
- squeeze(outv, outvoffset);
- }
-
- static void select(int[] p, int[] q, int[] r, int[] s, int b)
- {
- int bminus1 = b - 1;
-
- for (int j = 0; j < 64; ++j)
- {
- int t = bminus1 & (r[j] ^ s[j]);
- p[j] = s[j] ^ t;
- q[j] = r[j] ^ t;
- }
- }
-
- static void mainloop(int[] work, byte[] e)
- {
- int[] xzm1 = new int[64];
- int[] xzm = new int[64];
- int[] xzmb = new int[64];
- int[] xzm1b = new int[64];
- int[] xznb = new int[64];
- int[] xzn1b = new int[64];
- int[] a0 = new int[64];
- int[] a1 = new int[64];
- int[] b0 = new int[64];
- int[] b1 = new int[64];
- int[] c1 = new int[64];
- int[] r = new int[32];
- int[] s = new int[32];
- int[] t = new int[32];
- int[] u = new int[32];
-
- for (int j = 0; j < 32; ++j)
- xzm1[j] = work[j];
-
- xzm1[32] = 1;
-
- for (int j = 33; j < 64; ++j)
- xzm1[j] = 0;
-
- xzm[0] = 1;
-
- for (int j = 1; j < 64; ++j)
- xzm[j] = 0;
-
- int[] xzmbp = xzmb, a0p = a0, xzm1bp = xzm1b;
- int[] a1p = a1, b0p = b0, b1p = b1, c1p = c1;
- int[] xznbp = xznb, up = u, xzn1bp = xzn1b;
- int[] workp = work, sp = s, rp = r;
-
- for (int pos = 254; pos >= 0; --pos)
- {
- int b = ((int) ((e[pos / 8] & 0xFF) >>> (pos & 7)));
- b &= 1;
- select(xzmb, xzm1b, xzm, xzm1, b);
- add(a0, 0, xzmb, 0, xzmbp, 32);
- sub(a0p, 32, xzmb, 0, xzmbp, 32);
- add(a1, 0, xzm1b, 0, xzm1bp, 32);
- sub(a1p, 32, xzm1b, 0, xzm1bp, 32);
- square(b0p, 0, a0p, 0);
- square(b0p, 32, a0p, 32);
- mult(b1p, 0, a1p, 0, a0p, 32);
- mult(b1p, 32, a1p, 32, a0p, 0);
- add(c1, 0, b1, 0, b1p, 32);
- sub(c1p, 32, b1, 0, b1p, 32);
- square(rp, 0, c1p, 32);
- sub(sp, 0, b0, 0, b0p, 32);
- mult121665(t, s);
- add(u, 0, t, 0, b0p, 0);
- mult(xznbp, 0, b0p, 0, b0p, 32);
- mult(xznbp, 32, sp, 0, up, 0);
- square(xzn1bp, 0, c1p, 0);
- mult(xzn1bp, 32, rp, 0, workp, 0);
- select(xzm, xzm1, xznb, xzn1b, b);
- }
-
- for (int j = 0; j < 64; ++j)
- work[j] = xzm[j];
- }
-
- static void recip(int[] outv, int outvoffset, int[] z, int zoffset)
- {
- int[] z2 = new int[32];
- int[] z9 = new int[32];
- int[] z11 = new int[32];
- int[] z2_5_0 = new int[32];
- int[] z2_10_0 = new int[32];
- int[] z2_20_0 = new int[32];
- int[] z2_50_0 = new int[32];
- int[] z2_100_0 = new int[32];
- int[] t0 = new int[32];
- int[] t1 = new int[32];
-
- /* 2 */
- int[] z2p = z2;
- square(z2p, 0, z, zoffset);
-
- /* 4 */
- square(t1, 0, z2, 0);
-
- /* 8 */
- square(t0, 0, t1, 0);
-
- /* 9 */
- int[] z9p = z9, t0p = t0;
- mult(z9p, 0, t0p, 0, z, zoffset);
-
- /* 11 */
- mult(z11, 0, z9, 0, z2, 0);
-
- /* 22 */
- square(t0, 0, z11, 0);
-
- /* 2^5 - 2^0 = 31 */
- mult(z2_5_0, 0, t0, 0, z9, 0);
-
- /* 2^6 - 2^1 */
- square(t0, 0, z2_5_0, 0);
-
- /* 2^7 - 2^2 */
- square(t1, 0, t0, 0);
-
- /* 2^8 - 2^3 */
- square(t0, 0, t1, 0);
-
- /* 2^9 - 2^4 */
- square(t1, 0, t0, 0);
-
- /* 2^10 - 2^5 */
- square(t0, 0, t1, 0);
-
- /* 2^10 - 2^0 */
- mult(z2_10_0, 0, t0, 0, z2_5_0, 0);
-
- /* 2^11 - 2^1 */
- square(t0, 0, z2_10_0, 0);
-
- /* 2^12 - 2^2 */
- square(t1, 0, t0, 0);
-
- /* 2^20 - 2^10 */
- for (int i = 2; i < 10; i += 2)
- {
- square(t0, 0, t1, 0);
- square(t1, 0, t0, 0);
- }
-
- /* 2^20 - 2^0 */
- mult(z2_20_0, 0, t1, 0, z2_10_0, 0);
-
- /* 2^21 - 2^1 */
- square(t0, 0, z2_20_0, 0);
-
- /* 2^22 - 2^2 */
- square(t1, 0, t0, 0);
-
- /* 2^40 - 2^20 */
- for (int i = 2; i < 20; i += 2)
- {
- square(t0, 0, t1, 0);
- square(t1, 0, t0, 0);
- }
-
- /* 2^40 - 2^0 */
- mult(t0, 0, t1, 0, z2_20_0, 0);
-
- /* 2^41 - 2^1 */
- square(t1, 0, t0, 0);
-
- /* 2^42 - 2^2 */
- square(t0, 0, t1, 0);
-
- /* 2^50 - 2^10 */
- for (int i = 2; i < 10; i += 2)
- {
- square(t1, 0, t0, 0);
- square(t0, 0, t1, 0);
- }
-
- /* 2^50 - 2^0 */
- mult(z2_50_0, 0, t0, 0, z2_10_0, 0);
-
- /* 2^51 - 2^1 */
- square(t0, 0, z2_50_0, 0);
-
- /* 2^52 - 2^2 */
- square(t1, 0, t0, 0);
-
- /* 2^100 - 2^50 */
- for (int i = 2; i < 50; i += 2)
- {
- square(t0, 0, t1, 0);
- square(t1, 0, t0, 0);
- }
-
- /* 2^100 - 2^0 */
- mult(z2_100_0, 0, t1, 0, z2_50_0, 0);
-
- /* 2^101 - 2^1 */
- square(t1, 0, z2_100_0, 0);
-
- /* 2^102 - 2^2 */
- square(t0, 0, t1, 0);
-
- /* 2^200 - 2^100 */
- for (int i = 2; i < 100; i += 2)
- {
- square(t1, 0, t0, 0);
- square(t0, 0, t1, 0);
- }
-
- /* 2^200 - 2^0 */
- mult(t1, 0, t0, 0, z2_100_0, 0);
-
- /* 2^201 - 2^1 */
- square(t0, 0, t1, 0);
-
- /* 2^202 - 2^2 */
- square(t1, 0, t0, 0);
-
- /* 2^250 - 2^50 */
- for (int i = 2; i < 50; i += 2)
- {
- square(t0, 0, t1, 0);
- square(t1, 0, t0, 0);
- }
-
- /* 2^250 - 2^0 */
- mult(t0, 0, t1, 0, z2_50_0, 0);
-
- /* 2^251 - 2^1 */
- square(t1, 0, t0, 0);
-
- /* 2^252 - 2^2 */
- square(t0, 0, t1, 0);
-
- /* 2^253 - 2^3 */
- square(t1, 0, t0, 0);
-
- /* 2^254 - 2^4 */
- square(t0, 0, t1, 0);
-
- /* 2^255 - 2^5 */
- square(t1, 0, t0, 0);
-
- /* 2^255 - 21 */
- int[] t1p = t1, z11p = z11;
- mult(outv, outvoffset, t1p, 0, z11p, 0);
- }
-
- public static int crypto_scalarmult(byte[] q, byte[] n, byte[] p)
- {
- int[] work = new int[96];
- byte[] e = new byte[32];
-
- for (int i = 0; i < 32; ++i)
- e[i] = n[i];
-
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
-
- for (int i = 0; i < 32; ++i)
- work[i] = p[i] & 0xFF;
-
- mainloop(work, e);
-
- recip(work, 32, work, 32);
- mult(work, 64, work, 0, work, 32);
- freeze(work, 64);
-
- for (int i = 0; i < 32; ++i)
- q[i] = (byte) work[64 + i];
-
- return 0;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java
deleted file mode 100644
index b952deb..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class curve25519xsalsa20poly1305
-{
- public static final int crypto_box_PUBLICKEYBYTES = 32;
- public static final int crypto_box_SECRETKEYBYTES = 32;
- public static final int crypto_box_BEFORENMBYTES = 32;
- public static final int crypto_box_NONCEBYTES = 24;
- public static final int crypto_box_ZEROBYTES = 32;
- public static final int crypto_box_BOXZEROBYTES = 16;
-
- public static int crypto_box_getpublickey(byte[] pk, byte[] sk)
- {
- return curve25519.crypto_scalarmult_base(pk, sk);
- }
-
- public static int crypto_box_afternm(byte[] c, byte[] m, long mlen, byte[] n, byte[] k)
- {
- return xsalsa20poly1305.crypto_secretbox(c, m, mlen, n, k);
- }
-
- public static int crypto_box_afternm_nopad(byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k)
- {
- return xsalsa20poly1305.crypto_secretbox_nopad(c, coffset, m, moffset, mlen, n, k);
- }
-
- public static int crypto_box_beforenm(byte[] k, byte[] pk, byte[] sk)
- {
- byte[] s = new byte[32];
- byte[] sp = s, sigmap = xsalsa20.sigma;
-
- curve25519.crypto_scalarmult(sp, sk, pk);
- return hsalsa20.crypto_core(k, null, sp, sigmap);
- }
-
- public static int crypto_box(byte[] c, byte[] m, long mlen, byte[] n, byte[] pk, byte[] sk)
- {
- byte[] k = new byte[crypto_box_BEFORENMBYTES];
- byte[] kp = k;
-
- crypto_box_beforenm(kp, pk, sk);
- return crypto_box_afternm(c, m, mlen, n, kp);
- }
-
- public static int crypto_box_open(byte[] m, byte[] c, long clen, byte[] n, byte[] pk, byte[] sk)
- {
- byte[] k = new byte[crypto_box_BEFORENMBYTES];
- byte[] kp = k;
-
- crypto_box_beforenm(kp, pk, sk);
- return crypto_box_open_afternm(m, c, clen, n, kp);
- }
-
- public static int crypto_box_open_afternm(byte[] m, byte[] c, long clen, byte[] n, byte[] k)
- {
- return xsalsa20poly1305.crypto_secretbox_open(m, c, clen, n, k);
- }
-
- public static int crypto_box_open_afternm_nopad(byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k)
- {
- return xsalsa20poly1305.crypto_secretbox_open_nopad(m, moffset, c, coffset, clen, n, k);
- }
-
- public static int crypto_box_afternm(byte[] c, byte[] m, byte[] n, byte[] k)
- {
- return crypto_box_afternm(c, m, (long)m.length, n, k);
- }
-
- public static int crypto_box_open_afternm(byte[] m, byte[] c, byte[] n, byte[] k)
- {
- return crypto_box_open_afternm(m, c, (long) c.length, n, k);
- }
-
- public static int crypto_box(byte[] c, byte[] m, byte[] n, byte[] pk, byte[] sk)
- {
- return crypto_box(c, m, (long) m.length, n, pk, sk);
- }
-
- public static int crypto_box_open(byte[] m, byte[] c, byte[] n, byte[] pk, byte[] sk)
- {
- return crypto_box_open(m, c, (long) c.length, n, pk, sk);
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java b/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java
deleted file mode 100644
index ae5e429..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java
+++ /dev/null
@@ -1,164 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class hsalsa20
-{
- static final int ROUNDS = 20;
-
- static int rotate(int u, int c)
- {
- return (u << c) | (u >>> (32 - c));
- }
-
- static int load_littleendian(byte[] x, int offset)
- {
- return ((int)(x[offset])&0xff) |
- ((((int)(x[offset + 1])&0xff)) << 8) |
- ((((int)(x[offset + 2])&0xff)) << 16) |
- ((((int)(x[offset + 3])&0xff)) << 24);
- }
-
- static void store_littleendian(byte[] x, int offset, int u)
- {
- x[offset] = (byte) u; u >>>= 8;
- x[offset + 1] = (byte) u; u >>>= 8;
- x[offset + 2] = (byte) u; u >>>= 8;
- x[offset + 3] = (byte) u;
- }
-
- public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
- {
- int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
- int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
- int i;
-
- j0 = x0 = load_littleendian(c, 0);
- j1 = x1 = load_littleendian(k, 0);
- j2 = x2 = load_littleendian(k, 4);
- j3 = x3 = load_littleendian(k, 8);
- j4 = x4 = load_littleendian(k, 12);
- j5 = x5 = load_littleendian(c, 4);
-
- if (inv != null)
- {
- j6 = x6 = load_littleendian(inv, 0);
- j7 = x7 = load_littleendian(inv, 4);
- j8 = x8 = load_littleendian(inv, 8);
- j9 = x9 = load_littleendian(inv, 12);
- }
- else
- {
- j6 = x6 = j7 = x7 = j8 = x8 = j9 = x9 = 0;
- }
-
- j10 = x10 = load_littleendian(c, 8);
- j11 = x11 = load_littleendian(k, 16);
- j12 = x12 = load_littleendian(k, 20);
- j13 = x13 = load_littleendian(k, 24);
- j14 = x14 = load_littleendian(k, 28);
- j15 = x15 = load_littleendian(c, 12);
-
- for (i = ROUNDS; i > 0; i -= 2)
- {
- x4 ^= rotate(x0 + x12, 7);
- x8 ^= rotate(x4 + x0, 9);
- x12 ^= rotate(x8 + x4, 13);
- x0 ^= rotate(x12 + x8, 18);
- x9 ^= rotate(x5 + x1, 7);
- x13 ^= rotate(x9 + x5, 9);
- x1 ^= rotate(x13 + x9, 13);
- x5 ^= rotate(x1 + x13, 18);
- x14 ^= rotate(x10 + x6, 7);
- x2 ^= rotate(x14 + x10, 9);
- x6 ^= rotate(x2 + x14, 13);
- x10 ^= rotate(x6 + x2, 18);
- x3 ^= rotate(x15 + x11, 7);
- x7 ^= rotate(x3 + x15, 9);
- x11 ^= rotate(x7 + x3, 13);
- x15 ^= rotate(x11 + x7, 18);
- x1 ^= rotate(x0 + x3, 7);
- x2 ^= rotate(x1 + x0, 9);
- x3 ^= rotate(x2 + x1, 13);
- x0 ^= rotate(x3 + x2, 18);
- x6 ^= rotate(x5 + x4, 7);
- x7 ^= rotate(x6 + x5, 9);
- x4 ^= rotate(x7 + x6, 13);
- x5 ^= rotate(x4 + x7, 18);
- x11 ^= rotate(x10 + x9, 7);
- x8 ^= rotate(x11 + x10, 9);
- x9 ^= rotate(x8 + x11, 13);
- x10 ^= rotate(x9 + x8, 18);
- x12 ^= rotate(x15 + x14, 7);
- x13 ^= rotate(x12 + x15, 9);
- x14 ^= rotate(x13 + x12, 13);
- x15 ^= rotate(x14 + x13, 18);
- }
-
- x0 += j0;
- x1 += j1;
- x2 += j2;
- x3 += j3;
- x4 += j4;
- x5 += j5;
- x6 += j6;
- x7 += j7;
- x8 += j8;
- x9 += j9;
- x10 += j10;
- x11 += j11;
- x12 += j12;
- x13 += j13;
- x14 += j14;
- x15 += j15;
-
- x0 -= load_littleendian(c, 0);
- x5 -= load_littleendian(c, 4);
- x10 -= load_littleendian(c, 8);
- x15 -= load_littleendian(c, 12);
-
- if (inv != null)
- {
- x6 -= load_littleendian(inv, 0);
- x7 -= load_littleendian(inv, 4);
- x8 -= load_littleendian(inv, 8);
- x9 -= load_littleendian(inv, 12);
- }
-
- store_littleendian(outv, 0, x0);
- store_littleendian(outv, 4, x5);
- store_littleendian(outv, 8, x10);
- store_littleendian(outv, 12, x15);
- store_littleendian(outv, 16, x6);
- store_littleendian(outv, 20, x7);
- store_littleendian(outv, 24, x8);
- store_littleendian(outv, 28, x9);
-
- return 0;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java
deleted file mode 100644
index 7db9951..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java
+++ /dev/null
@@ -1,178 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class poly1305
-{
- final int CRYPTO_BYTES = 16;
- final int CRYPTO_KEYBYTES = 32;
-
- static final int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252};
-
- public static int crypto_onetimeauth_verify(byte[] h, int hoffset, byte[] inv, int invoffset, long inlen, byte[] k)
- {
- byte[] correct = new byte[16];
-
- crypto_onetimeauth(correct, 0, inv, invoffset, inlen, k);
- return verify_16.crypto_verify(h, hoffset, correct);
- }
-
- static void add(int[] h, int[] c)
- {
- int j;
- int u = 0;
-
- for (j = 0; j < 17; ++j)
- {
- u += h[j] + c[j];
- h[j] = u & 255;
- u >>>= 8;
- }
- }
-
- static void squeeze(int[] h)
- {
- int u = 0;
-
- for (int j = 0; j < 16; ++j)
- {
- u += h[j];
- h[j] = u & 255;
- u >>>= 8;
- }
-
- u += h[16];
- h[16] = u & 3;
- u = 5 * (u >>> 2);
-
- for (int j = 0; j < 16; ++j)
- {
- u += h[j];
- h[j] = u & 255;
- u >>>= 8;
- }
-
- u += h[16];
- h[16] = u;
- }
-
- static void freeze(int[] h)
- {
- int[] horig = new int[17];
-
- for (int j = 0; j < 17; ++j)
- horig[j] = h[j];
-
- add(h, minusp);
-
- int negative = (int)(-(h[16] >>> 7));
-
- for (int j = 0; j < 17; ++j)
- h[j] ^= negative & (horig[j] ^ h[j]);
- }
-
- static void mulmod(int[] h, int[] r)
- {
- int[] hr = new int[17];
-
- for (int i = 0; i < 17; ++i)
- {
- int u = 0;
-
- for (int j = 0; j <= i; ++j)
- u += h[j] * r[i - j];
-
- for (int j = i + 1; j < 17; ++j)
- u += 320 * h[j] * r[i + 17 - j];
-
- hr[i] = u;
- }
-
- for (int i = 0; i < 17; ++i)
- h[i] = hr[i];
-
- squeeze(h);
- }
-
- public static int crypto_onetimeauth(byte[] outv, int outvoffset, byte[] inv, int invoffset, long inlen, byte[] k)
- {
- int j;
- int[] r = new int[17];
- int[] h = new int[17];
- int[] c = new int[17];
-
- r[0] = k[0] & 0xFF;
- r[1] = k[1] & 0xFF;
- r[2] = k[2] & 0xFF;
- r[3] = k[3] & 15;
- r[4] = k[4] & 252;
- r[5] = k[5] & 0xFF;
- r[6] = k[6] & 0xFF;
- r[7] = k[7] & 15;
- r[8] = k[8] & 252;
- r[9] = k[9] & 0xFF;
- r[10] = k[10] & 0xFF;
- r[11] = k[11] & 15;
- r[12] = k[12] & 252;
- r[13] = k[13] & 0xFF;
- r[14] = k[14] & 0xFF;
- r[15] = k[15] & 15;
- r[16] = 0;
-
- for (j = 0; j < 17; ++j)
- h[j] = 0;
-
- while (inlen > 0)
- {
- for (j = 0; j < 17; ++j)
- c[j] = 0;
-
- for (j = 0; (j < 16) && (j < inlen); ++j)
- c[j] = inv[invoffset + j]&0xff;
-
- c[j] = 1;
- invoffset += j;
- inlen -= j;
- add(h, c);
- mulmod(h, r);
- }
-
- freeze(h);
-
- for (j = 0; j < 16; ++j)
- c[j] = k[j + 16] & 0xFF;
-
- c[16] = 0;
- add(h, c);
-
- for (j = 0; j < 16; ++j)
- outv[j + outvoffset] = (byte)h[j];
-
- return 0;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java b/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java
deleted file mode 100644
index 62c58d3..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java
+++ /dev/null
@@ -1,324 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class salsa20
-{
- final int crypto_core_salsa20_ref_OUTPUTBYTES = 64;
- final int crypto_core_salsa20_ref_INPUTBYTES = 16;
- final int crypto_core_salsa20_ref_KEYBYTES = 32;
- final int crypto_core_salsa20_ref_CONSTBYTES = 16;
- final int crypto_stream_salsa20_ref_KEYBYTES = 32;
- final int crypto_stream_salsa20_ref_NONCEBYTES = 8;
-
- final static int ROUNDS = 20;
-
- static long rotate(int u, int c)
- {
- return (u << c) | (u >>> (32 - c));
- }
-
- static int load_littleendian(byte[] x, int offset)
- {
- return ((int)(x[offset])&0xff) |
- ((((int)(x[offset + 1])&0xff)) << 8) |
- ((((int)(x[offset + 2])&0xff)) << 16) |
- ((((int)(x[offset + 3])&0xff)) << 24);
- }
-
- static void store_littleendian(byte[] x, int offset, int u)
- {
- x[offset] = (byte) u; u >>>= 8;
- x[offset + 1] = (byte) u; u >>>= 8;
- x[offset + 2] = (byte) u; u >>>= 8;
- x[offset + 3] = (byte) u;
- }
-
- public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
- {
- int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
- int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
- int i;
-
- j0 = x0 = load_littleendian(c, 0);
- j1 = x1 = load_littleendian(k, 0);
- j2 = x2 = load_littleendian(k, 4);
- j3 = x3 = load_littleendian(k, 8);
- j4 = x4 = load_littleendian(k, 12);
- j5 = x5 = load_littleendian(c, 4);
- j6 = x6 = load_littleendian(inv, 0);
- j7 = x7 = load_littleendian(inv, 4);
- j8 = x8 = load_littleendian(inv, 8);
- j9 = x9 = load_littleendian(inv, 12);
- j10 = x10 = load_littleendian(c, 8);
- j11 = x11 = load_littleendian(k, 16);
- j12 = x12 = load_littleendian(k, 20);
- j13 = x13 = load_littleendian(k, 24);
- j14 = x14 = load_littleendian(k, 28);
- j15 = x15 = load_littleendian(c, 12);
-
- for (i = ROUNDS; i > 0; i -= 2)
- {
- x4 ^= rotate(x0 + x12, 7);
- x8 ^= rotate(x4 + x0, 9);
- x12 ^= rotate(x8 + x4, 13);
- x0 ^= rotate(x12 + x8, 18);
- x9 ^= rotate(x5 + x1, 7);
- x13 ^= rotate(x9 + x5, 9);
- x1 ^= rotate(x13 + x9, 13);
- x5 ^= rotate(x1 + x13, 18);
- x14 ^= rotate(x10 + x6, 7);
- x2 ^= rotate(x14 + x10, 9);
- x6 ^= rotate(x2 + x14, 13);
- x10 ^= rotate(x6 + x2, 18);
- x3 ^= rotate(x15 + x11, 7);
- x7 ^= rotate(x3 + x15, 9);
- x11 ^= rotate(x7 + x3, 13);
- x15 ^= rotate(x11 + x7, 18);
- x1 ^= rotate(x0 + x3, 7);
- x2 ^= rotate(x1 + x0, 9);
- x3 ^= rotate(x2 + x1, 13);
- x0 ^= rotate(x3 + x2, 18);
- x6 ^= rotate(x5 + x4, 7);
- x7 ^= rotate(x6 + x5, 9);
- x4 ^= rotate(x7 + x6, 13);
- x5 ^= rotate(x4 + x7, 18);
- x11 ^= rotate(x10 + x9, 7);
- x8 ^= rotate(x11 + x10, 9);
- x9 ^= rotate(x8 + x11, 13);
- x10 ^= rotate(x9 + x8, 18);
- x12 ^= rotate(x15 + x14, 7);
- x13 ^= rotate(x12 + x15, 9);
- x14 ^= rotate(x13 + x12, 13);
- x15 ^= rotate(x14 + x13, 18);
- }
-
- x0 += j0;
- x1 += j1;
- x2 += j2;
- x3 += j3;
- x4 += j4;
- x5 += j5;
- x6 += j6;
- x7 += j7;
- x8 += j8;
- x9 += j9;
- x10 += j10;
- x11 += j11;
- x12 += j12;
- x13 += j13;
- x14 += j14;
- x15 += j15;
-
- store_littleendian(outv, 0, x0);
- store_littleendian(outv, 4, x1);
- store_littleendian(outv, 8, x2);
- store_littleendian(outv, 12, x3);
- store_littleendian(outv, 16, x4);
- store_littleendian(outv, 20, x5);
- store_littleendian(outv, 24, x6);
- store_littleendian(outv, 28, x7);
- store_littleendian(outv, 32, x8);
- store_littleendian(outv, 36, x9);
- store_littleendian(outv, 40, x10);
- store_littleendian(outv, 44, x11);
- store_littleendian(outv, 48, x12);
- store_littleendian(outv, 52, x13);
- store_littleendian(outv, 56, x14);
- store_littleendian(outv, 60, x15);
-
- return 0;
- }
-
- public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[] k)
- {
- byte[] inv = new byte[16];
- byte[] block = new byte[64];
-
- int coffset = 0;
-
- if (clen == 0)
- return 0;
-
- for (int i = 0; i < 8; ++i)
- inv[i] = n[noffset + i];
-
- for (int i = 8; i < 16; ++i)
- inv[i] = 0;
-
- while (clen >= 64)
- {
- salsa20.crypto_core(c, inv, k, xsalsa20.sigma);
-
- int u = 1;
-
- for (int i = 8; i < 16; ++i)
- {
- u += inv[i]&0xff;
- inv[i] = (byte) u;
- u >>>= 8;
- }
-
- clen -= 64;
- coffset += 64;
- }
-
- if (clen != 0)
- {
- salsa20.crypto_core(block, inv, k, xsalsa20.sigma);
-
- for (int i = 0; i < clen; ++i)
- c[coffset + i] = block[i];
- }
-
- return 0;
- }
-
- public static int crypto_stream_xor(byte[] c, byte[] m, int mlen, byte[] n, int noffset, byte[] k)
- {
- byte[] inv = new byte[16];
- byte[] block = new byte[64];
-
- int coffset = 0;
- int moffset = 0;
-
- if (mlen == 0)
- return 0;
-
- for (int i = 0; i < 8; ++i)
- inv[i] = n[noffset + i];
-
- for (int i = 8; i < 16; ++i)
- inv[i] = 0;
-
- while (mlen >= 64)
- {
- salsa20.crypto_core(block, inv, k, xsalsa20.sigma);
-
- for (int i = 0; i < 64; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ block[i]);
-
- int u = 1;
-
- for (int i = 8; i < 16; ++i)
- {
- u += inv[i]&0xff;
- inv[i] = (byte) u;
- u >>>= 8;
- }
-
- mlen -= 64;
- coffset += 64;
- moffset += 64;
- }
-
- if (mlen != 0)
- {
- salsa20.crypto_core(block, inv, k, xsalsa20.sigma);
-
- for (int i = 0; i < mlen; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ block[i]);
- }
-
- return 0;
- }
-
- public static int crypto_stream_xor_skip32(byte[] c0, byte[] c, int coffset, byte[] m, int moffset, int mlen, byte[] n, int noffset, byte[] k)
- {
- /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */
-
- int u;
- byte[] inv = new byte[16];
- byte[] prevblock = new byte[64];
- byte[] curblock = new byte[64];
-
- if (mlen == 0)
- return 0;
-
- for (int i = 0; i < 8; ++i)
- inv[i] = n[noffset + i];
-
- for (int i = 8; i < 16; ++i)
- inv[i] = 0;
-
- /* calculate first block */
- salsa20.crypto_core(prevblock, inv, k, xsalsa20.sigma);
-
- /* extract first 32 bytes of cipherstream into c0 */
- if (c0 != null)
- System.arraycopy(prevblock, 0, c0, 0, 32);
-
- while (mlen >= 64)
- {
- u = 1;
- for (int i = 8; i < 16; ++i)
- {
- u += inv[i]&0xff;
- inv[i] = (byte) u;
- u >>>= 8;
- }
-
- salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma);
-
- for (int i = 0; i < 32; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ prevblock[i+32]);
-
- for (int i = 32; i < 64; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ curblock[i-32]);
-
- mlen -= 64;
- coffset += 64;
- moffset += 64;
-
- byte[] tmpblock = prevblock;
- prevblock = curblock;
- curblock = tmpblock;
- }
-
- if (mlen != 0)
- {
- u = 1;
- for (int i = 8; i < 16; ++i)
- {
- u += inv[i]&0xff;
- inv[i] = (byte) u;
- u >>>= 8;
- }
-
- salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma);
-
- for (int i = 0; i < mlen && i < 32; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ prevblock[i+32]);
-
- for (int i = 32; i < mlen && i < 64; ++i)
- c[coffset + i] = (byte)(m[moffset + i] ^ curblock[i-32]);
- }
-
- return 0;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java b/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java
deleted file mode 100644
index 443c641..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class verify_16
-{
- final int crypto_verify_16_ref_BYTES = 16;
-
- public static int crypto_verify(byte[] x, int xoffset, byte[] y)
- {
- int differentbits = 0;
-
- for (int i = 0; i < 15; i++)
- differentbits |= ((int)(x[xoffset + i] ^ y[i])) & 0xff;
-
- return (1 & (((int)differentbits - 1) >>> 8)) - 1;
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java
deleted file mode 100644
index f755787..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class xsalsa20
-{
- final int crypto_stream_xsalsa20_ref_KEYBYTES = 32;
- final int crypto_stream_xsalsa20_ref_NONCEBYTES = 24;
-
- public final static byte[] sigma = {(byte) 'e', (byte) 'x', (byte) 'p', (byte) 'a',
- (byte) 'n', (byte) 'd', (byte) ' ', (byte) '3',
- (byte) '2', (byte) '-', (byte) 'b', (byte) 'y',
- (byte) 't', (byte) 'e', (byte) ' ', (byte) 'k'};
-
- public static int crypto_stream(byte[] c, int clen, byte[] n, byte[] k)
- {
- byte[] subkey = new byte[32];
-
- hsalsa20.crypto_core(subkey, n, k, sigma);
- return salsa20.crypto_stream(c, clen, n, 16, subkey);
- }
-
- public static int crypto_stream_xor(byte[] c, byte[] m, long mlen, byte[] n, byte[] k)
- {
- byte[] subkey = new byte[32];
-
- hsalsa20.crypto_core(subkey, n, k, sigma);
- return salsa20.crypto_stream_xor(c, m, (int) mlen, n, 16, subkey);
- }
-
- public static int crypto_stream_xor_skip32(byte[] c0, byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k)
- {
- /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */
-
- byte[] subkey = new byte[32];
-
- hsalsa20.crypto_core(subkey, n, k, sigma);
- return salsa20.crypto_stream_xor_skip32(c0, c, coffset, m, moffset, (int) mlen, n, 16, subkey);
- }
-}
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java
deleted file mode 100644
index 7f94532..0000000
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// Copyright (c) 2011, Neil Alexander T.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with
-// or without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-
-package com.neilalexander.jnacl.crypto;
-
-public class xsalsa20poly1305
-{
- final int crypto_secretbox_KEYBYTES = 32;
- final int crypto_secretbox_NONCEBYTES = 24;
- final int crypto_secretbox_ZEROBYTES = 32;
- final int crypto_secretbox_BOXZEROBYTES = 16;
-
- static public int crypto_secretbox(byte[] c, byte[] m, long mlen, byte[] n, byte[] k)
- {
- if (mlen < 32)
- return -1;
-
- xsalsa20.crypto_stream_xor(c, m, mlen, n, k);
- poly1305.crypto_onetimeauth(c, 16, c, 32, mlen - 32, c);
-
- for (int i = 0; i < 16; ++i)
- c[i] = 0;
-
- return 0;
- }
-
- static public int crypto_secretbox_nopad(byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k)
- {
- /* variant of crypto_secretbox that doesn't require 32 zero bytes before m and doesn't output
- * 16 zero bytes before c */
- byte[] c0 = new byte[32];
-
- xsalsa20.crypto_stream_xor_skip32(c0, c, coffset+16, m, moffset, mlen, n, k);
- poly1305.crypto_onetimeauth(c, coffset, c, coffset+16, mlen, c0);
-
- return 0;
- }
-
- static public int crypto_secretbox_open(byte[] m, byte[] c, long clen, byte[] n, byte[] k)
- {
- if (clen < 32)
- return -1;
-
- byte[] subkeyp = new byte[32];
-
- xsalsa20.crypto_stream(subkeyp, 32, n, k);
-
- if (poly1305.crypto_onetimeauth_verify(c, 16, c, 32, clen - 32, subkeyp) != 0)
- return -1;
-
- xsalsa20.crypto_stream_xor(m, c, clen, n, k);
-
- for (int i = 0; i < 32; ++i)
- m[i] = 0;
-
- return 0;
- }
-
- static public int crypto_secretbox_open_nopad(byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k)
- {
- /* variant of crypto_secretbox_open that doesn't require 16 zero bytes before c and doesn't output
- * 32 zero bytes before m */
-
- if (clen < 16)
- return -1;
-
- byte[] subkeyp = new byte[32];
-
- xsalsa20.crypto_stream(subkeyp, 32, n, k);
-
- if (poly1305.crypto_onetimeauth_verify(c, coffset, c, coffset+16, clen - 16, subkeyp) != 0)
- return -1;
-
- xsalsa20.crypto_stream_xor_skip32(null, m, moffset, c, coffset+16, clen - 16, n, k);
-
- return 0;
- }
-}
diff --git a/source/src/test/java/ch/threema/apitool/Common.java b/source/src/test/java/ch/threema/apitool/Common.java
deleted file mode 100644
index c3af9a0..0000000
--- a/source/src/test/java/ch/threema/apitool/Common.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-/**
- * Common Stuff
- */
-public abstract class Common {
-
- public static final String myPrivateKey = "private:94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
- public static final String myPrivateKeyExtract = "94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
-
- public static final String myPublicKey = "public:3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531";
- public static final String myPublicKeyExtract = "3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531";
-
- public static final String otherPrivateKey = "private:8318e05220acd38e97ba41a9a6318688214219916075ca060f9339a6d1f7fc29";
- public static final String otherPublicKey = "public:10ac7fd937eafb806f9a05bf9afa340a99387b0063cc9cb0d1ea5505d39cc076";
-
- public static final String echochoPublicKey = "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b";
- public static final String randomNonce = "516f4f1562dda0704a7bae8997cf0b354c6980181152ac32";
-
- public static boolean isEmpty(byte[] byteArray) {
- if(byteArray == null) {
- return true;
- }
- else {
- for(byte b: byteArray) {
- if(b != 0) {
- return false;
- }
- }
- }
-
- return true;
- }
-}
diff --git a/source/src/test/java/ch/threema/apitool/CryptToolTest.java b/source/src/test/java/ch/threema/apitool/CryptToolTest.java
deleted file mode 100644
index 141f12f..0000000
--- a/source/src/test/java/ch/threema/apitool/CryptToolTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import org.junit.Test;
-
-import com.neilalexander.jnacl.NaCl;
-
-import ch.threema.apitool.messages.TextMessage;
-import ch.threema.apitool.messages.ThreemaMessage;
-import ch.threema.apitool.results.EncryptResult;
-
-public class CryptToolTest {
-
- @Test
- public void testRandomNonce() throws Exception {
- byte[] randomNonce = CryptTool.randomNonce();
-
- // random nonce should be a byte array
- Assert.assertNotNull("random nonce", randomNonce);
-
- // with a length of 24
- Assert.assertSame("nonce length", randomNonce.length, NaCl.NONCEBYTES);
- }
-
- @Test
- public void testCreateKeyPair() {
- byte[] privateKey = new byte[NaCl.SECRETKEYBYTES];
- byte[] publicKey = new byte[NaCl.PUBLICKEYBYTES];
-
- CryptTool.generateKeyPair(privateKey, publicKey);
-
- Assert.assertFalse("empty private key", Common.isEmpty(privateKey));
- Assert.assertFalse("empty public key", Common.isEmpty(publicKey));
- }
-
- @Test
- public void testDecrypt() throws Exception {
- String nonce = "0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0";
- String box = "45181c7aed95a1c100b1b559116c61b43ce15d04014a805288b7d14bf3a993393264fe554794ce7d6007233e8ef5a0f1ccdd704f34e7c7b77c72c239182caf1d061d6fff6ffbbfe8d3b8f3475c2fe352e563aa60290c666b2e627761e32155e62f048b52ef2f39c13ac229f393c67811749467396ecd09f42d32a4eb419117d0451056ac18fac957c52b0cca67568e2d97e5a3fd829a77f914a1ad403c5909fd510a313033422ea5db71eaf43d483238612a54cb1ecfe55259b1de5579e67c6505df7d674d34a737edf721ea69d15b567bc2195ec67e172f3cb8d6842ca88c29138cc33e9351dbc1e4973a82e1cf428c1c763bb8f3eb57770f914a";
-
- Key privateKey = Key.decodeKey(Common.otherPrivateKey);
- Key publicKey = Key.decodeKey(Common.myPublicKey);
-
- ThreemaMessage message = CryptTool.decryptMessage(DataUtils.hexStringToByteArray(box), privateKey.key,
- publicKey.key, DataUtils.hexStringToByteArray(nonce));
-
- Assert.assertNotNull(message);
- Assert.assertTrue("message is not a instance of text message", message instanceof TextMessage);
- Assert.assertEquals(((TextMessage) message).getText(), "Dies ist eine Testnachricht. äöü");
- }
-
- @Test
- public void testEncrypt() throws Exception {
- String text = "Dies ist eine Testnachricht. äöü";
-
- Key privateKey = Key.decodeKey(Common.myPrivateKey);
- Key publicKey = Key.decodeKey(Common.otherPublicKey);
-
- EncryptResult res = CryptTool.encryptTextMessage(text, privateKey.key, publicKey.key);
- Assert.assertNotNull(res);
- Assert.assertNotNull(res.getNonce());
- Assert.assertNotNull(res.getResult());
- Assert.assertFalse(Common.isEmpty(res.getNonce()));
- Assert.assertFalse(Common.isEmpty(res.getResult()));
- }
-
- @Test
- public void testDerivePublicKey() throws Exception {
- Key privateKey = Key.decodeKey(Common.myPrivateKey);
- Key publicKey = Key.decodeKey(Common.myPublicKey);
- byte[] derivedPublicKey = CryptTool.derivePublicKey(privateKey.key);
- Assert.assertNotNull("derived public key", derivedPublicKey);
- Assert.assertEquals(derivedPublicKey, publicKey.key);
- }
-}
\ No newline at end of file
diff --git a/source/src/test/java/ch/threema/apitool/KeyTest.java b/source/src/test/java/ch/threema/apitool/KeyTest.java
deleted file mode 100644
index 104188b..0000000
--- a/source/src/test/java/ch/threema/apitool/KeyTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * $Id$
- *
- * The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE
- */
-
-package ch.threema.apitool;
-
-import ch.threema.apitool.exceptions.InvalidKeyException;
-import org.junit.Test;
-
-public class KeyTest {
-
- @Test
- public void testDecodeWrongKey() {
- try {
- Key.decodeKey("imnotarealkey");
- } catch (InvalidKeyException e) {
- return;
- }
- Assert.assertFalse("could parse invalid key", true);
- }
-
- @Test
- public void testDecodeKeyPrivate() throws Exception {
- Key key = Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234");
- Assert.assertNotNull("key instance", key);
-
- Assert.assertEquals(key.type, Key.KeyType.PRIVATE);
- Assert.assertEquals(key.key, DataUtils.hexStringToByteArray("1234567890123456789012345678901234567890123456789012345678901234"));
- }
- @Test
- public void testDecodeKeyPublic() throws Exception {
- Key key = Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234");
- Assert.assertNotNull("key instance", key);
-
- Assert.assertEquals(key.type, Key.KeyType.PUBLIC);
- Assert.assertEquals(key.key, DataUtils.hexStringToByteArray("1234567890123456789012345678901234567890123456789012345678901234"));
- }
-
- @Test
- public void testEncodePrivate() throws Exception {
- byte[] keyAsByte = DataUtils.hexStringToByteArray(Common.myPrivateKeyExtract);
-
- Key key = new Key(Key.KeyType.PRIVATE, keyAsByte);
- Assert.assertNotNull("key instance", key);
-
- Assert.assertEquals(Key.KeyType.PRIVATE, key.type);
- Assert.assertEquals(key.key, keyAsByte);
-
- Assert.assertEquals(key.encode(), Common.myPrivateKey);
- }
-}
\ No newline at end of file