> getHeaders() {
+ return headers;
+ }
+
+ public T getData() {
+ return data;
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/DataUtils.java b/source/src/main/java/ch/threema/apitool/utils/DataUtils.java
similarity index 64%
rename from source/src/main/java/ch/threema/apitool/DataUtils.java
rename to source/src/main/java/ch/threema/apitool/utils/DataUtils.java
index f9ffa68..cfbdd20 100644
--- a/source/src/main/java/ch/threema/apitool/DataUtils.java
+++ b/source/src/main/java/ch/threema/apitool/utils/DataUtils.java
@@ -1,8 +1,14 @@
/*
- * $Id$
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
*
* The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2015-2024 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
@@ -20,29 +26,49 @@
* 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;
+package ch.threema.apitool.utils;
+import ch.threema.apitool.exceptions.InvalidHexException;
import ch.threema.apitool.exceptions.InvalidKeyException;
+import ch.threema.apitool.types.Key;
+import ch.threema.apitool.types.QuotePart;
import java.io.*;
+import java.util.regex.Pattern;
public class DataUtils {
+ public static final String QUOTE_PATTERN = "^> quote #([0-9a-f]{16})(?:\\r?\\n){2}(.+)$";
+
/**
* Convert a string in hexadecimal representation to a byte array.
+ *
+ * Whitespace (RegEx \s) is stripped before decoding, but if other invalid characters are
+ * contained, an error is thrown.
*
* @param s hex string
* @return decoded byte array
+ * @throws InvalidHexException if the string is not a valid hex string
*/
- public static byte[] hexStringToByteArray(String s) {
- String sc = s.replaceAll("[^0-9a-fA-F]", "");
+ public static byte[] hexStringToByteArray(String s) throws InvalidHexException {
+ String sc = s.replaceAll("\\s", "");
int len = sc.length();
+ if (len % 2 != 0) {
+ throw new InvalidHexException("Hex string length is not divisible by 2");
+ }
+ if (sc.matches(".*[^0-9a-fA-F].*")) {
+ throw new InvalidHexException("Hex string contains non-hex characters");
+ }
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));
+ + Character.digit(sc.charAt(i + 1), 16));
}
return data;
}
@@ -51,10 +77,12 @@ public static byte[] hexStringToByteArray(String s) {
* 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'};
+ 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++) {
@@ -65,11 +93,31 @@ public static String byteArrayToHexString(byte[] bytes) {
return new String(hexChars);
}
+ public static byte[] longToByteArrayBigEndian(long value) {
+ byte[] result = new byte[8];
+ for (int i = 7; i >= 0; i--) {
+ result[i] = (byte) (value & 0xFF);
+ value >>= 8;
+ }
+ return result;
+ }
+
+ public static long byteArrayToLongBigEndian(final byte[] bytes) {
+ long result = 0;
+ for (int i = 0; i < 8; i++) {
+ result <<= 8;
+ result |= (bytes[i] & 0xFF);
+ }
+ return result;
+ }
+
/**
* 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 {
@@ -96,7 +144,9 @@ public static void writeHexFile(File outFile, byte[] data) throws IOException {
* 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 {
@@ -114,7 +164,8 @@ public static Key readKeyFile(File inFile) throws IOException, InvalidKeyExcepti
* @return the decoded key
* @throws java.io.IOException
*/
- public static Key readKeyFile(File inFile, String expectedKeyType) throws IOException, InvalidKeyException {
+ 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();
@@ -122,8 +173,7 @@ public static Key readKeyFile(File inFile, String expectedKeyType) throws IOExce
}
/**
- * Write an encoded key to a file
- * Encoded key format: type:hex_key.
+ * 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
@@ -134,4 +184,15 @@ public static void writeKeyFile(File outFile, Key key) throws IOException {
fw.write('\n');
fw.close();
}
+
+ public static String extractQuote(String text, QuotePart part) {
+ var pattern = Pattern.compile(QUOTE_PATTERN, Pattern.DOTALL);
+ var matcher = pattern.matcher(text);
+
+ if (matcher.matches()) {
+ return matcher.group(part.ordinal() + 1);
+ }
+
+ return null;
+ }
}
diff --git a/source/src/main/java/ch/threema/apitool/utils/ProtocolConstants.java b/source/src/main/java/ch/threema/apitool/utils/ProtocolConstants.java
new file mode 100644
index 0000000..aacab70
--- /dev/null
+++ b/source/src/main/java/ch/threema/apitool/utils/ProtocolConstants.java
@@ -0,0 +1,68 @@
+/*
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015-2024 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.utils;
+
+public final class ProtocolConstants {
+ // Taken from Android repo's `ProtocolDefines.java`
+
+ /* object lengths */
+ public static final int PUSH_FROM_LEN = 32;
+ public static final int IDENTITY_LEN = 8;
+ public static final int MESSAGE_ID_LEN = 8;
+ public static final int BLOB_ID_LEN = 16;
+ public static final int BLOB_KEY_LEN = 32;
+ public static final int GROUP_ID_LEN = 8;
+ public static final int GROUP_INVITE_TOKEN_LEN = 16;
+ public static final int BALLOT_ID_LEN = 8;
+ public static final int GROUP_JOIN_MESSAGE_LEN = 100;
+
+ /* max message size */
+ public static final int MAX_PKT_LEN = 8192;
+ public static final int OVERHEAD_NACL_BOX = 16; // Excluding nonce
+ public static final int OVERHEAD_PKT_HDR = 4;
+ public static final int OVERHEAD_MSG_HDR = 88;
+ public static final int OVERHEAD_BOX_HDR = 1;
+ public static final int OVERHEAD_MAXPADDING = 255;
+ public static final int MAX_MESSAGE_LEN = MAX_PKT_LEN - OVERHEAD_NACL_BOX * 2 // Both
+ // app-to-server
+ // and
+ // end-to-end
+ - OVERHEAD_PKT_HDR - OVERHEAD_MSG_HDR - OVERHEAD_BOX_HDR - OVERHEAD_MAXPADDING;
+ public static final int MIN_MESSAGE_PADDED_LEN = 32;
+
+ private ProtocolConstants() {
+
+ }
+}
diff --git a/source/src/main/java/ch/threema/apitool/utils/StringUtils.java b/source/src/main/java/ch/threema/apitool/utils/StringUtils.java
new file mode 100644
index 0000000..aa52dcd
--- /dev/null
+++ b/source/src/main/java/ch/threema/apitool/utils/StringUtils.java
@@ -0,0 +1,15 @@
+package ch.threema.apitool.utils;
+
+public final class StringUtils {
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ public static String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+}
diff --git a/source/src/main/java/com/neilalexander/jnacl/NaCl.java b/source/src/main/java/com/neilalexander/jnacl/NaCl.java
index 5cddd5d..f671d33 100644
--- a/source/src/main/java/com/neilalexander/jnacl/NaCl.java
+++ b/source/src/main/java/com/neilalexander/jnacl/NaCl.java
@@ -1,28 +1,28 @@
//
-// 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.
+// 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;
@@ -31,300 +31,318 @@
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;
+import com.neilalexander.jnacl.crypto.*;
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 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;
- public byte[] decrypt(byte[] input, byte[] nonce) {
- return decrypt(input, input.length, nonce);
- }
+ private final byte[] precomputed = new byte[BEFORENMBYTES];
- 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();
+ /* 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);
+ }
- 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];
- }
+ public NaCl(String privatekey, String publickey) {
+ this(getBinary(privatekey), getBinary(publickey));
+ }
- curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey);
- }
+ public byte[] encrypt(byte[] input, byte[] nonce) {
+ return encrypt(input, input.length, nonce);
+ }
- public static byte[] derivePublicKey(byte[] privatekey) {
- if (privatekey.length != SECRETKEYBYTES)
- throw new Error("Invalid private key length");
+ public byte[] encrypt(byte[] input, int inputlength, byte[] nonce) {
+ if (nonce.length != NONCEBYTES)
+ throw new Error("Invalid nonce length");
- byte[] publickey = new byte[PUBLICKEYBYTES];
- curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey);
- return publickey;
- }
+ byte[] output = new byte[inputlength + BOXOVERHEAD];
+ curve25519xsalsa20poly1305.crypto_box_afternm_nopad(output, 0, input, 0, input.length,
+ nonce, this.precomputed);
- public static byte[] symmetricEncryptData(byte[] input, byte[] key, byte[] nonce) {
- if (key.length != SYMMKEYBYTES)
- throw new Error("Invalid symmetric key length");
+ return output;
+ }
- 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);
+ public byte[] decrypt(byte[] input, byte[] nonce) {
+ return decrypt(input, input.length, nonce);
+ }
- return output;
- }
+ 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)");
- }
+ /**
+ * 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) {
+ Formatter formatter = new Formatter();
+ for (byte b : buf)
+ formatter.format("%02x", b);
+ return formatter.toString();
+ }
+
+ public static String asHex(int[] buf) {
+ 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
index 3079e04..298e344 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java
@@ -1,208 +1,189 @@
//
-// 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.
+// 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
-{
+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)
- {
+ 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)
- {
+
+ 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)
- {
+
+ 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)
- {
+ 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)
- {
+
+ 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)
- {
+ static void squeeze(int[] a, int aoffset) {
int u = 0;
-
- for (int j = 0; j < 31; ++j)
- {
+
+ 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)
- {
+
+ 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)
- {
+ 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)
- {
+ static void mult(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) {
int j;
-
- for (int i = 0; i < 32; ++i)
- {
+
+ 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)
- {
+ static void mult121665(int[] outv, int[] a) {
int j;
int u = 0;
-
- for (j = 0; j < 31; ++j)
- {
+
+ 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)
- {
+
+ 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)
- {
+
+ static void square(int[] outv, int outvoffset, int[] a, int aoffset) {
int j;
-
- for (int i = 0; i < 32; ++i)
- {
+
+ 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)
- {
+
+ 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)
- {
+ static void select(int[] p, int[] q, int[] r, int[] s, int b) {
int bminus1 = b - 1;
-
- for (int j = 0; j < 64; ++j)
- {
+
+ 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)
- {
+ static void mainloop(int[] work, byte[] e) {
int[] xzm1 = new int[64];
int[] xzm = new int[64];
int[] xzmb = new int[64];
@@ -221,14 +202,14 @@ static void mainloop(int[] work, byte[] e)
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;
@@ -237,29 +218,28 @@ static void mainloop(int[] work, byte[] e)
int[] xznbp = xznb, up = u, xzn1bp = xzn1b;
int[] workp = work, sp = s, rp = r;
- for (int pos = 254; pos >= 0; --pos)
- {
+ 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);
+ 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);
+ 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);
}
@@ -267,8 +247,7 @@ static void mainloop(int[] work, byte[] e)
work[j] = xzm[j];
}
- static void recip(int[] outv, int outvoffset, int[] z, int zoffset)
- {
+ 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];
@@ -283,184 +262,177 @@ static void recip(int[] outv, int outvoffset, int[] z, int zoffset)
/* 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)
- {
+ 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);
+ 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);
+ 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);
+ 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)
- {
+ 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)
- {
+ 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)
- {
+ 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);
+ 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
index b952deb..4290a5b 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java
@@ -1,110 +1,100 @@
//
-// 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.
+// 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 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)
- {
+
+ 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)
- {
+
+ 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)
- {
+ 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)
- {
+
+ 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)
- {
+
+ 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)
- {
+
+ 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_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_open_afternm(byte[] m, byte[] c, byte[] n, byte[] 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)
- {
+
+ 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)
- {
+
+ 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
index ae5e429..0fc7d8f 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java
@@ -1,59 +1,56 @@
//
-// 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.
+// 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
-{
+public class hsalsa20 {
static final int ROUNDS = 20;
- static int rotate(int u, int c)
- {
+ 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 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;
+ 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)
- {
+ 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;
@@ -65,15 +62,12 @@ public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
j4 = x4 = load_littleendian(k, 12);
j5 = x5 = load_littleendian(c, 4);
- if (inv != null)
- {
+ 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
- {
+ } else {
j6 = x6 = j7 = x7 = j8 = x8 = j9 = x9 = 0;
}
@@ -84,8 +78,7 @@ public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
j14 = x14 = load_littleendian(k, 28);
j15 = x15 = load_littleendian(c, 12);
- for (i = ROUNDS; i > 0; i -= 2)
- {
+ for (i = ROUNDS; i > 0; i -= 2) {
x4 ^= rotate(x0 + x12, 7);
x8 ^= rotate(x4 + x0, 9);
x12 ^= rotate(x8 + x4, 13);
@@ -142,8 +135,7 @@ public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
x10 -= load_littleendian(c, 8);
x15 -= load_littleendian(c, 12);
- if (inv != null)
- {
+ if (inv != null) {
x6 -= load_littleendian(inv, 0);
x7 -= load_littleendian(inv, 4);
x8 -= load_littleendian(inv, 8);
diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java
index 7db9951..f534c73 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java
@@ -1,126 +1,117 @@
//
-// 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.
+// 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
-{
+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)
- {
+ 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)
- {
+ static void add(int[] h, int[] c) {
int j;
int u = 0;
-
- for (j = 0; j < 17; ++j)
- {
+
+ for (j = 0; j < 17; ++j) {
u += h[j] + c[j];
h[j] = u & 255;
u >>>= 8;
}
}
- static void squeeze(int[] h)
- {
+ static void squeeze(int[] h) {
int u = 0;
-
- for (int j = 0; j < 16; ++j)
- {
- u += h[j];
- h[j] = u & 255;
+
+ 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)
- {
+
+ 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)
- {
+ 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));
-
+
+ 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)
- {
+ static void mulmod(int[] h, int[] r) {
int[] hr = new int[17];
-
- for (int i = 0; i < 17; ++i)
- {
+
+ for (int i = 0; i < 17; ++i) {
int u = 0;
-
- for (int j = 0; j <= i; ++j)
+
+ for (int j = 0; j <= i; ++j)
u += h[j] * r[i - j];
-
- for (int j = i + 1; j < 17; ++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)
- {
+ 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];
@@ -147,14 +138,13 @@ public static int crypto_onetimeauth(byte[] outv, int outvoffset, byte[] inv, in
for (j = 0; j < 17; ++j)
h[j] = 0;
- while (inlen > 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] = inv[invoffset + j] & 0xff;
+
c[j] = 1;
invoffset += j;
inlen -= j;
@@ -164,15 +154,15 @@ public static int crypto_onetimeauth(byte[] outv, int outvoffset, byte[] inv, in
freeze(h);
- for (j = 0; j < 16; ++j)
+ 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];
-
+
+ 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
index 62c58d3..4fea1c7 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java
@@ -1,34 +1,33 @@
//
-// 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.
+// 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
-{
+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;
@@ -38,29 +37,27 @@ public class salsa20
final static int ROUNDS = 20;
- static long rotate(int u, int c)
- {
+ 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 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;
+ 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)
- {
+ 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;
@@ -82,8 +79,7 @@ public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
j14 = x14 = load_littleendian(k, 28);
j15 = x15 = load_littleendian(c, 12);
- for (i = ROUNDS; i > 0; i -= 2)
- {
+ for (i = ROUNDS; i > 0; i -= 2) {
x4 ^= rotate(x0 + x12, 7);
x8 ^= rotate(x4 + x0, 9);
x12 ^= rotate(x8 + x4, 13);
@@ -154,32 +150,29 @@ public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c)
return 0;
}
-
- public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[] k)
- {
+
+ 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)
- {
+ 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;
+
+ for (int i = 8; i < 16; ++i) {
+ u += inv[i] & 0xff;
inv[i] = (byte) u;
u >>>= 8;
}
@@ -188,46 +181,43 @@ public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[
coffset += 64;
}
- if (clen != 0)
- {
+ 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)
- {
+ 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)
- {
+ 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]);
+ c[coffset + i] = (byte) (m[moffset + i] ^ block[i]);
int u = 1;
-
- for (int i = 8; i < 16; ++i)
- {
- u += inv[i]&0xff;
+
+ for (int i = 8; i < 16; ++i) {
+ u += inv[i] & 0xff;
inv[i] = (byte) u;
u >>>= 8;
}
@@ -237,88 +227,83 @@ public static int crypto_stream_xor(byte[] c, byte[] m, int mlen, byte[] n, int
moffset += 64;
}
- if (mlen != 0)
- {
+ 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]);
+ 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 */
+ 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];
+ int u;
+ byte[] inv = new byte[16];
+ byte[] prevblock = new byte[64];
+ byte[] curblock = new byte[64];
- if (mlen == 0)
- return 0;
+ if (mlen == 0)
+ return 0;
- for (int i = 0; i < 8; ++i)
- inv[i] = n[noffset + i];
+ for (int i = 0; i < 8; ++i)
+ inv[i] = n[noffset + i];
- for (int i = 8; i < 16; ++i)
- inv[i] = 0;
+ for (int i = 8; i < 16; ++i)
+ inv[i] = 0;
- /* calculate first block */
- salsa20.crypto_core(prevblock, inv, k, xsalsa20.sigma);
+ /* 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);
+ /* 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;
- }
+ 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);
+ 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 = 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]);
+ for (int i = 32; i < 64; ++i)
+ c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]);
- mlen -= 64;
- coffset += 64;
- moffset += 64;
+ mlen -= 64;
+ coffset += 64;
+ moffset += 64;
- byte[] tmpblock = prevblock;
- prevblock = curblock;
- curblock = tmpblock;
- }
+ 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;
- }
+ 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);
+ 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 = 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]);
- }
+ for (int i = 32; i < mlen && i < 64; ++i)
+ c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]);
+ }
- return 0;
- }
+ 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
index 443c641..4f637cb 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java
@@ -1,43 +1,41 @@
//
-// 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.
+// 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
-{
+public class verify_16 {
final int crypto_verify_16_ref_BYTES = 16;
- public static int crypto_verify(byte[] x, int xoffset, byte[] y)
- {
+ 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;
+ 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
index f755787..aae813b 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java
@@ -1,65 +1,62 @@
//
-// 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.
+// 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
-{
+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)
- {
+
+ 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)
- {
+
+ 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 */
+ 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];
+ 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);
- }
+ 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
index 7f94532..5ad44d3 100644
--- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java
+++ b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java
@@ -1,102 +1,104 @@
//
-// 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.
+// 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
-{
+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)
- {
+ 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];
+ 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);
+ 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;
- }
+ return 0;
+ }
- static public int crypto_secretbox_open(byte[] m, byte[] c, long clen, byte[] n, byte[] k)
- {
+ 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 */
+ 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;
+ if (clen < 16)
+ return -1;
- byte[] subkeyp = new byte[32];
+ byte[] subkeyp = new byte[32];
- xsalsa20.crypto_stream(subkeyp, 32, n, k);
+ xsalsa20.crypto_stream(subkeyp, 32, n, k);
- if (poly1305.crypto_onetimeauth_verify(c, coffset, c, coffset+16, clen - 16, subkeyp) != 0)
- return -1;
+ 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);
+ xsalsa20.crypto_stream_xor_skip32(null, m, moffset, c, coffset + 16, clen - 16, n, k);
- return 0;
- }
+ return 0;
+ }
}
diff --git a/source/src/test/java/ch/threema/apitool/Assert.java b/source/src/test/java/ch/threema/apitool/Assert.java
index e3dce18..1d396fe 100644
--- a/source/src/test/java/ch/threema/apitool/Assert.java
+++ b/source/src/test/java/ch/threema/apitool/Assert.java
@@ -1,8 +1,14 @@
/*
- * $Id$
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
*
* The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2015-2024 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
@@ -20,6 +26,10 @@
* 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;
@@ -32,6 +42,7 @@ 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));
}
diff --git a/source/src/test/java/ch/threema/apitool/Common.java b/source/src/test/java/ch/threema/apitool/Common.java
index c3af9a0..0d3d8b9 100644
--- a/source/src/test/java/ch/threema/apitool/Common.java
+++ b/source/src/test/java/ch/threema/apitool/Common.java
@@ -1,8 +1,14 @@
/*
- * $Id$
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
*
* The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2015-2024 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
@@ -20,34 +26,53 @@
* 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.types.GroupId;
+import com.neilalexander.jnacl.NaCl;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
/**
* Common Stuff
*/
public abstract class Common {
- public static final String myPrivateKey = "private:94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
- public static final String myPrivateKeyExtract = "94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2";
+ 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 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 otherPrivateKey =
+ "private:8318e05220acd38e97ba41a9a6318688214219916075ca060f9339a6d1f7fc29";
+ public static final String otherPublicKey =
+ "public:10ac7fd937eafb806f9a05bf9afa340a99387b0063cc9cb0d1ea5505d39cc076";
- public static final String echochoPublicKey = "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b";
+ public static final String echochoPublicKey =
+ "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b";
public static final String randomNonce = "516f4f1562dda0704a7bae8997cf0b354c6980181152ac32";
+ public static final GroupId groupId = new GroupId("*ASDFGHI", "DEADBEEF");
public static boolean isEmpty(byte[] byteArray) {
- if(byteArray == null) {
+ if (byteArray == null) {
return true;
- }
- else {
- for(byte b: byteArray) {
- if(b != 0) {
+ } else {
+ for (byte b : byteArray) {
+ if (b != 0) {
return false;
}
}
@@ -55,4 +80,11 @@ public static boolean isEmpty(byte[] byteArray) {
return true;
}
+
+ public static 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/test/java/ch/threema/apitool/CryptToolTest.java b/source/src/test/java/ch/threema/apitool/CryptToolTest.java
index 141f12f..47fbbbc 100644
--- a/source/src/test/java/ch/threema/apitool/CryptToolTest.java
+++ b/source/src/test/java/ch/threema/apitool/CryptToolTest.java
@@ -1,8 +1,14 @@
/*
- * $Id$
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
*
* The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2015-2024 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
@@ -20,79 +26,85 @@
* 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;
+import ch.threema.apitool.types.Key;
+import ch.threema.apitool.utils.DataUtils;
+import com.neilalexander.jnacl.NaCl;
+import org.junit.Test;
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
+ @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. äöü";
+ String nonce = "0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0";
+
+ 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);
+ }
+}
diff --git a/source/src/test/java/ch/threema/apitool/DataUtilsTest.java b/source/src/test/java/ch/threema/apitool/DataUtilsTest.java
new file mode 100644
index 0000000..6c969f2
--- /dev/null
+++ b/source/src/test/java/ch/threema/apitool/DataUtilsTest.java
@@ -0,0 +1,76 @@
+/*
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015-2024 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.InvalidHexException;
+import ch.threema.apitool.types.QuotePart;
+import ch.threema.apitool.utils.DataUtils;
+import org.junit.Test;
+
+public class DataUtilsTest {
+ @Test
+ public void testHexStringToByteArraySuccess() throws InvalidHexException {
+ final byte[] decoded = DataUtils.hexStringToByteArray("0011AAff");
+ Assert.assertEquals(new byte[] {(byte) 0x00, (byte) 0x11, (byte) 0xaa, (byte) 0xff},
+ decoded);
+ }
+
+ @Test
+ public void testHexStringToByteArrayStripWhitespace() throws InvalidHexException {
+ final byte[] decoded = DataUtils.hexStringToByteArray("0011 \n\rAAff");
+ Assert.assertEquals(new byte[] {(byte) 0x00, (byte) 0x11, (byte) 0xaa, (byte) 0xff},
+ decoded);
+ }
+
+ @Test(expected = InvalidHexException.class)
+ public void testHexStringToByteArrayRejectOddLength() {
+ DataUtils.hexStringToByteArray("00112");
+ }
+
+ @Test(expected = InvalidHexException.class)
+ public void testHexStringToByteArrayRejectInvalid() {
+ DataUtils.hexStringToByteArray("0011aaffgg");
+ }
+
+ @Test
+ public void testExtractQuote() {
+ String msgText = "> quote #f053a613ff24aeb8\n\nTest";
+ String quotedMessageId = DataUtils.extractQuote(msgText, QuotePart.QUOTED_MESSAGE_ID);
+ String quoteText = DataUtils.extractQuote(msgText, QuotePart.QUOTE_TEXT);
+
+ Assert.assertEquals("f053a613ff24aeb8", quotedMessageId);
+ Assert.assertEquals("Test", quoteText);
+ }
+}
diff --git a/source/src/test/java/ch/threema/apitool/E2ETest.java b/source/src/test/java/ch/threema/apitool/E2ETest.java
new file mode 100644
index 0000000..71ae155
--- /dev/null
+++ b/source/src/test/java/ch/threema/apitool/E2ETest.java
@@ -0,0 +1,527 @@
+package ch.threema.apitool;
+
+import ch.threema.apitool.exceptions.ApiException;
+import ch.threema.apitool.exceptions.InvalidKeyException;
+import ch.threema.apitool.helpers.E2EHelper;
+import ch.threema.apitool.messages.DeliveryReceipt;
+import ch.threema.apitool.types.FileRenderingType;
+import ch.threema.apitool.types.GroupId;
+import ch.threema.apitool.types.Key;
+import ch.threema.apitool.types.MessageId;
+import ch.threema.apitool.types.voting.*;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.json.JSONArray;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class E2ETest {
+
+ private static final byte[] randomGroupId = new byte[8];
+ private static APIConnector connector;
+ private static E2EHelper e2EHelper;
+ private static final Random random = new Random();
+
+ public static void main(String[] args) {
+ if (args == null || args.length < 4 || args.length > 5) {
+ System.out.printf("Usage: %s Threema-ID Gateway-ID Secret PrivateKey [ApiUrl]%n",
+ new java.io.File(E2ETest.class.getProtectionDomain().getCodeSource()
+ .getLocation().getPath()).getName());
+ System.exit(-1);
+ }
+ random.nextBytes(randomGroupId);
+ var threemaId = args[0];
+ String gatewayId = args[1];
+ var secret = args[2];
+ var pkey = args[3];
+ var apiUrl = args.length > 4 ? args[4] : null;
+ var reader = new MavenXpp3Reader();
+
+ try {
+ connector = new APIConnector(gatewayId, secret, apiUrl, new PublicKeyStore() {
+ @Override
+ protected byte[] fetchPublicKey(String threemaId) {
+ return null;
+ }
+
+ @Override
+ protected void save(String threemaId, byte[] publicKey) {
+
+ }
+ });
+ connector.setUserAgent(String.format("threema-msgapi-sdk-java/%s-test",
+ reader.read(new FileReader("pom.xml")).getVersion()));
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ Key privateKey;
+ try {
+ privateKey = new Key(Key.KeyType.PRIVATE, Key.decodeKey(pkey).key);
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException(e);
+ }
+ List threemaIds = List.of(gatewayId, threemaId);
+ e2EHelper = new E2EHelper(connector, privateKey.key);
+ try {
+ /* Lookups */
+ testLookupPhone("41790000000"); // OK
+ testLookupEmail("abc@example.com"); // OK
+ /* 1:1 Messages */
+ var msgId = sendE2ETextMsg(threemaId); // OK, OK
+ sendE2EFileMsg(threemaId); // OK, OK
+ var ballotId = sendE2EBallotCreateMessage(threemaId); // OK, OK
+ sendE2EBallotVoteMsg(threemaId, gatewayId, ballotId); // OK, OK
+ sendE2EBallotCloseMessage(threemaId, gatewayId, ballotId); // OK, OK
+ sendE2ELocationMsg(threemaId); // OK, OK
+ sendDeliveryReceipt(threemaId, msgId, DeliveryReceipt.Type.RECEIVED); // OK, OK
+ sendDeliveryReceipt(threemaId, msgId, DeliveryReceipt.Type.USER_ACK); // OK, OK
+
+ /* Group messages */
+ sendE2EGroupCreateMsg(
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8)),
+ threemaIds, List.of(threemaId)); // OK, OK
+ sendE2EGroupRenameMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ sendE2EGroupSetPhotoMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ var msgIds = sendE2EGroupTextMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ sendE2EGroupFileMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ sendE2EGroupLocationMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ var groupBallotId = sendE2EGroupBallotCreateMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ sendE2EGroupBallotVoteMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8)),
+ groupBallotId); // OK, OK
+ sendGroupDeliveryReceipt(threemaIds, msgIds.get(1),
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK,
+ // OK
+ sendE2EGroupRequestSyncMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK
+ // (?),
+ // OK
+ // (?)
+ sendE2EGroupDeletePhotoMsg(threemaIds,
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8))); // OK(,
+ // no
+ // receive?)
+ sendE2EGroupLeaveMsg(
+ new GroupId(randomGroupId, gatewayId.getBytes(StandardCharsets.UTF_8)),
+ threemaIds, List.of(gatewayId)); // OK,
+ } catch (ApiException e) {
+ System.out.println(e.getMessage());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void sendDeliveryReceipt(String threemaId, String messageId,
+ DeliveryReceipt.Type receiptType) {
+ try {
+ var response = e2EHelper.sendDeliveryReceipt(threemaId,
+ List.of(new MessageId(messageId)), receiptType);
+
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending delivery receipt failed");
+ }
+ }
+
+ private static void sendGroupDeliveryReceipt(List threemaIds, String msgId,
+ GroupId groupId) {
+ try {
+ var response = e2EHelper.sendGroupDeliveryReceipt(threemaIds, groupId,
+ List.of(new MessageId(msgId)), DeliveryReceipt.Type.USER_DEC);
+
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending group delivery receipt failed");
+ }
+ }
+
+ private static String sendE2ETextMsg(String threemaId) {
+ try {
+ var response = e2EHelper.sendTextMessage(threemaId, "Test Message");
+
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ return response.getData();
+ } catch (Exception e) {
+ System.err.println("Sending text message failed");
+ return "";
+ }
+ }
+
+ private static void sendE2ELocationMsg(String threemaId) {
+ try {
+ var response = e2EHelper.sendLocationMessage(threemaId, "47.8", "8.3", null,
+ "Test Building", "Nowhere");
+
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending location message failed");
+ }
+ }
+
+ private static List sendE2EGroupLocationMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupLocationMessage(threemaIds, groupId, "47.4", "8.1",
+ null, "Test Building", "Nowhere");
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group location message failed");
+ return List.of();
+ }
+ }
+
+ private static byte[] sendE2EBallotCreateMessage(String threemaId) {
+ try {
+ byte[] ballotId = new byte[8];
+ random.nextBytes(ballotId);
+ var response = e2EHelper.sendBallotCreateMessage(threemaId, ballotId, "Test poll",
+ State.OPEN, VotingMode.SINGLE_CHOICE,
+ ResultsDisclosureType.INTERMEDIATE, DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, null, null),
+ new BallotChoice(1, "Ananas", 1, null, null)),
+ null);
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ return ballotId;
+ } catch (Exception e) {
+ System.err.println("Sending ballot create message failed");
+ return new byte[0];
+ }
+ }
+
+ private static void sendE2EBallotCloseMessage(String threemaId, String gatewayId,
+ byte[] ballotId) {
+ try {
+ var response = e2EHelper.sendBallotCreateMessage(threemaId, ballotId, "Test poll",
+ State.CLOSED, VotingMode.SINGLE_CHOICE,
+ ResultsDisclosureType.INTERMEDIATE, DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, List.of(1, 0), null),
+ new BallotChoice(1, "Ananas", 1, List.of(0, 1), null)),
+ List.of(threemaId, gatewayId));
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending ballot close message failed");
+ }
+ }
+
+ private static byte[] sendE2EGroupBallotCreateMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ byte[] ballotId = new byte[8];
+ random.nextBytes(ballotId);
+ System.out.println("BallotID: " + Arrays.toString(ballotId));
+ var resArr = e2EHelper.sendGroupBallotCreateMessage(threemaIds, groupId, ballotId,
+ "Test Poll", State.OPEN, VotingMode.SINGLE_CHOICE,
+ ResultsDisclosureType.INTERMEDIATE, DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, null, null),
+ new BallotChoice(1, "Ananas", 1, null, null)),
+ null);
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ sendE2EGroupBallotVoteMsg(threemaIds,
+ new GroupId(randomGroupId,
+ threemaIds.get(0).getBytes(StandardCharsets.UTF_8)),
+ ballotId); // OK
+ sendE2EGroupBallotCloseMsg(threemaIds,
+ new GroupId(randomGroupId,
+ threemaIds.get(0).getBytes(StandardCharsets.UTF_8)),
+ ballotId); // OK
+ return ballotId;
+ } catch (Exception e) {
+ System.err.println("Sending group ballot create message failed");
+ return new byte[0];
+ }
+ }
+
+ private static List sendE2EGroupBallotCloseMsg(List threemaIds, GroupId groupId,
+ byte[] ballotId) {
+ try {
+ var resArr = e2EHelper.sendGroupBallotCreateMessage(threemaIds, groupId, ballotId,
+ "Test poll", State.CLOSED, VotingMode.SINGLE_CHOICE,
+ ResultsDisclosureType.INTERMEDIATE, DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, List.of(1, 0), null),
+ new BallotChoice(1, "Ananas", 1, List.of(0, 1), null)),
+ threemaIds);
+
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ }
+ return new ArrayList<>(resArr.getData().length());
+ } catch (Exception e) {
+ System.err.println("Sending group ballot close message failed");
+ return List.of();
+ }
+ }
+
+ private static void sendE2EBallotVoteMsg(String threemaId, String creator, byte[] ballotId)
+ throws ApiException {
+ try {
+ try {
+ var response = e2EHelper.sendBallotVoteMessage(threemaId,
+ creator.getBytes(StandardCharsets.UTF_8), ballotId,
+ List.of(new VoteChoice(0, false), new VoteChoice(1, true)));
+ System.out.println("Message ID: " + response.getData() + " "
+ + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending text message failed");
+ }
+ } catch (Exception e) {
+ System.err.println("Sending ballot vote message failed");
+ }
+ }
+
+ private static List sendE2EGroupBallotVoteMsg(List threemaIds, GroupId groupId,
+ byte[] ballotId) throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupBallotVoteMessage(threemaIds, groupId.getGroupCreator(),
+ groupId, ballotId,
+ List.of(new VoteChoice(0, false), new VoteChoice(1, true)));
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group ballot vote message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupTextMsg(List threemaId, GroupId groupId)
+ throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupTextMessage(threemaId, groupId, "Group Test Message");
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group text message failed");
+ return List.of();
+ }
+ }
+
+ private static void sendE2EFileMsg(String threemaId) throws ApiException {
+ try {
+ var file = new File("./threema.jpg");
+ var thumb = new File("./thumb.png");
+ var response = e2EHelper.sendFileMessage(threemaId, file, thumb,
+ "End-To-End Encrypted Caption", FileRenderingType.FILE, null,
+ Map.of("q", 0xff));
+
+ System.out.println(
+ "Message ID: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending file message failed");
+ }
+ }
+
+ private static List sendE2EGroupFileMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ var file = new File("./threema.jpg");
+ var thumb = new File("./thumb.png");
+ var resArr = e2EHelper.sendGroupFileMessage(threemaIds, groupId, file, thumb,
+ "End-To-End Encrypted Caption", FileRenderingType.MEDIA, null,
+ Map.of("q", 0xff));
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group file message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupCreateMsg(GroupId groupId, List threemaIds,
+ List members) throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupCreateMessage(threemaIds, members, groupId);
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group create message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupLeaveMsg(GroupId groupId, List threemaIds,
+ List members) throws ApiException {
+ try {
+ e2EHelper.sendGroupCreateMessage(threemaIds, members, groupId);
+ var resArr = e2EHelper.sendGroupLeaveMessage(threemaIds, groupId);
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group leave message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupRenameMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupRenameMessage(threemaIds, groupId, "Java SDK Test");
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group rename message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupSetPhotoMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ var file = new File("./threema.jpg");
+ var resArr = e2EHelper.sendGroupSetPhotoMessage(threemaIds, groupId, file);
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group set photo message failed");
+ return List.of();
+ }
+ }
+
+ private static List sendE2EGroupDeletePhotoMsg(List threemaIds, GroupId groupId)
+ throws ApiException {
+ try {
+ var resArr = e2EHelper.sendGroupDeletePhotoMessage(threemaIds, groupId);
+
+ var res = new ArrayList(resArr.getData().length());
+ for (int i = 0; i < resArr.getData().length(); i++) {
+ System.out.println("Message ID: "
+ + resArr.getData().getJSONObject(i).getString("messageId") + " "
+ + resArr.getStatusCode());
+ res.add(resArr.getData().getJSONObject(i).getString("messageId"));
+ }
+ return res;
+ } catch (Exception e) {
+ System.err.println("Sending group delete photo message failed");
+ return List.of();
+ }
+ }
+
+ private static void sendE2EGroupRequestSyncMsg(List threemaIds, GroupId groupId) {
+ try {
+ var response = e2EHelper.sendGroupRequestSyncMsg(threemaIds, groupId);
+
+ System.out.println(
+ "Message IDs: " + response.getData() + " " + response.getStatusCode());
+ } catch (Exception e) {
+ System.err.println("Sending group request sync message failed");
+ }
+ }
+
+ // WARNING: This test can only be confirmed to work with real data
+ private static void testLookupPhone(String phoneNo) {
+ try {
+ var response = connector.lookupPhone(phoneNo);
+
+ System.out.println(
+ "Threema IDs: " + response.getData() + " " + response.getStatusCode());
+ } catch (ApiException e) {
+ if (e.getCode() == 404) {
+ System.out.println("No associated Threema ID was found for phone no: " + phoneNo);
+ } else {
+ System.err.println("Looking up phone number failed");
+ }
+ }
+ }
+
+ // WARNING: This test can only be confirmed to work with real data
+ private static void testLookupEmail(String emailAddr) {
+ try {
+ var response = connector.lookupEmail(emailAddr);
+
+ System.out.println(
+ "Threema IDs: " + response.getData() + " " + response.getStatusCode());
+ } catch (ApiException e) {
+ if (e.getCode() == 404) {
+ System.out.println("No associated Threema ID was found for phone no: " + emailAddr);
+ } else {
+ System.err.println("Looking up email address failed");
+ }
+ }
+ }
+}
diff --git a/source/src/test/java/ch/threema/apitool/IntegrationTest.java b/source/src/test/java/ch/threema/apitool/IntegrationTest.java
new file mode 100644
index 0000000..3135f6a
--- /dev/null
+++ b/source/src/test/java/ch/threema/apitool/IntegrationTest.java
@@ -0,0 +1,478 @@
+/*
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
+ *
+ * The MIT License (MIT)
+ * Copyright (c) 2015-2024 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 ch.threema.apitool.exceptions.MessageParseException;
+import ch.threema.apitool.messages.*;
+import ch.threema.apitool.types.FileRenderingType;
+import ch.threema.apitool.types.GroupId;
+import ch.threema.apitool.types.Key;
+import ch.threema.apitool.types.voting.*;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+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.junit.jupiter.MockitoExtension;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThatNoException;
+
+@ExtendWith(MockitoExtension.class)
+public class IntegrationTest {
+
+ /*
+ * Generating unit tests is difficult as there is no guarantee the expected result is generated
+ * correctly. These tests could pass erroneously when both en- and decryption are flawed in the
+ * same way.
+ */
+
+ @Test
+ public void testGroupTextMessage() throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedText = "> quote #a0a0a0a0a0a0a0a0\n\nTest Message";
+ var expectedQuotedMessageId = "a0a0a0a0a0a0a0a0";
+ var expectedQuoteText = "Test Message";
+
+ var encryptResult = CryptTool.encryptGroupTextMessage(Common.groupId,
+ "> quote #a0a0a0a0a0a0a0a0\n\nTest Message", privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupTextMessage.class, actual,
+ "message is not an instance of group text message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupTextMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupTextMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedText, ((GroupTextMessage) actual).getText());
+ Assertions.assertEquals(expectedQuotedMessageId,
+ ((GroupTextMessage) actual).getQuotedMessageId());
+ Assertions.assertEquals(expectedQuoteText, ((GroupTextMessage) actual).getQuoteText());
+ }
+
+ @Test
+ public void testFileMessage() throws MessageParseException, InvalidKeyException {
+ for (int i = 0; i < 2; i++) {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var file = new File("./threema.jpg");
+ var expectedCaption = i != 1 ? "My caption" : "";
+ var expectedBlobId = "abcdeffffffedcba".getBytes(StandardCharsets.UTF_8);
+ var expectedRenderingType = FileRenderingType.MEDIA.getValue();
+ var expectedFilename = "logo.jpg";
+ var expectedMimeType = "image/jpg";
+ var expectedThumbnailBlobId = new byte[0];
+ int expectedFileSize;
+ try {
+ expectedFileSize = Common.readFile(file).length;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ var encryptedData = CryptTool.encryptFileData(Common.readFile(file));
+
+ var encryptResult = CryptTool.encryptFileMessage(
+ "abcdeffffffedcba".getBytes(StandardCharsets.UTF_8), null, null,
+ encryptedData.getSecret(), "image/jpg", "logo.jpg",
+ Common.readFile(file).length, i != 1 ? "My caption" : null,
+ FileRenderingType.MEDIA, null, null, privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(FileMessage.class, actual,
+ "message is not an instance of file message");
+ Assertions.assertEquals(expectedCaption, ((FileMessage) actual).getCaption());
+ Assertions.assertEquals(expectedRenderingType,
+ ((FileMessage) actual).getRenderingType().getValue());
+ Assertions.assertEquals(expectedMimeType, ((FileMessage) actual).getMimeType());
+ Assertions.assertArrayEquals(expectedThumbnailBlobId,
+ ((FileMessage) actual).getThumbnailBlobId());
+ Assertions.assertEquals(expectedFileSize, ((FileMessage) actual).getSize());
+ Assertions.assertEquals(expectedFilename, ((FileMessage) actual).getFilename());
+ Assertions.assertArrayEquals(expectedBlobId, ((FileMessage) actual).getBlobId());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Test
+ public void testGroupFileMessage() throws MessageParseException, InvalidKeyException {
+ for (int i = 0; i < 2; i++) {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var file = new File("./threema.jpg");
+ var expectedCaption = i != 1 ? "My caption" : "";
+ var expectedBlobId = "abcdeffffffedcba".getBytes(StandardCharsets.UTF_8);
+ var expectedRenderingType = FileRenderingType.MEDIA.getValue();
+ var expectedFilename = "logo.jpg";
+ var expectedMimeType = "image/jpg";
+ var expectedThumbnailBlobId = new byte[0];
+ int expectedFileSize;
+ try {
+ expectedFileSize = Common.readFile(file).length;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ var encryptedData = CryptTool.encryptFileData(Common.readFile(file));
+
+ var encryptResult = CryptTool.encryptGroupFileMessage(Common.groupId,
+ "abcdeffffffedcba".getBytes(StandardCharsets.UTF_8), null, null,
+ encryptedData.getSecret(), "image/jpg", "logo.jpg",
+ Common.readFile(file).length, i != 1 ? "My caption" : null,
+ FileRenderingType.MEDIA, null, null, privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupFileMessage.class, actual,
+ "message is not an instance of group file message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupFileMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupFileMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedCaption, ((GroupFileMessage) actual).getCaption());
+ Assertions.assertEquals(expectedRenderingType,
+ ((GroupFileMessage) actual).getRenderingType().getValue());
+ Assertions.assertEquals(expectedMimeType,
+ ((GroupFileMessage) actual).getMimeType());
+ Assertions.assertArrayEquals(expectedThumbnailBlobId,
+ ((GroupFileMessage) actual).getThumbnailBlobId());
+ Assertions.assertEquals(expectedFileSize, ((GroupFileMessage) actual).getSize());
+ Assertions.assertEquals(expectedFilename,
+ ((GroupFileMessage) actual).getFilename());
+ Assertions.assertArrayEquals(expectedBlobId,
+ ((GroupFileMessage) actual).getBlobId());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @DisplayName("Should encrypt and then decrypt a BallotCreateMessage")
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testBallotCreateMessage(boolean isDisplayModeNull)
+ throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expected = "Pizza";
+ var expectedState = State.OPEN.getValue();
+ var expectedVotingMode = VotingMode.SINGLE_CHOICE.getValue();
+ var expectedDisclosureType = ResultsDisclosureType.CLOSED.getValue();
+ var expectedDisplayMode = DisplayMode.LIST;
+
+ var message = new BallotCreateMessage("BALL0TID".getBytes(StandardCharsets.UTF_8),
+ "[JAVA SDK] Test Poll", State.OPEN, VotingMode.SINGLE_CHOICE,
+ ResultsDisclosureType.CLOSED, 0,
+ isDisplayModeNull ? null : DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, List.of(), 0),
+ new BallotChoice(1, "Ananas", 1, List.of(), 0)),
+ List.of());
+
+ var encryptResult = CryptTool.encryptBallotCreateMessage(message.getBallotId(),
+ message.getDescription(), message.getState(), message.getVotingMode(),
+ message.getResultsDisclosureType(), message.getOrder(),
+ message.getDisplayMode(), message.getChoices(), List.of(), privateKey.key,
+ publicKey.key);
+
+ assertThatNoException().isThrownBy(message::getData);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(BallotCreateMessage.class, actual,
+ "message is not an instance of ballot create message");
+ Assertions.assertEquals(expected,
+ ((BallotCreateMessage) actual).getChoices().get(0).getName());
+ Assertions.assertEquals(expectedState,
+ ((BallotCreateMessage) actual).getState().getValue());
+ Assertions.assertEquals(expectedVotingMode,
+ ((BallotCreateMessage) actual).getVotingMode().getValue());
+ Assertions.assertEquals(expectedDisclosureType,
+ ((BallotCreateMessage) actual).getResultsDisclosureType().getValue());
+ Assertions.assertEquals(expectedDisplayMode,
+ ((BallotCreateMessage) actual).getDisplayMode());
+ }
+
+ @DisplayName("Should encrypt and then decrypt a GroupBallotCreateMessage")
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testGroupBallotCreateMessage(boolean isDisplayModeNull)
+ throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expected = "Pizza";
+ var expectedState = State.OPEN.getValue();
+ var expectedVotingMode = VotingMode.SINGLE_CHOICE.getValue();
+ var expectedDisclosureType = ResultsDisclosureType.CLOSED.getValue();
+ var expectedDisplayMode = DisplayMode.LIST.getValue();
+
+ var message = new GroupBallotCreateMessage(new GroupId("ASDFGHHJ", "*ASDFGHI"),
+ "BALL0TID".getBytes(StandardCharsets.UTF_8), "[JAVA SDK] Test Poll",
+ State.OPEN, VotingMode.SINGLE_CHOICE, ResultsDisclosureType.CLOSED, 0,
+ isDisplayModeNull ? null : DisplayMode.LIST,
+ List.of(new BallotChoice(0, "Pizza", 0, List.of(), 0),
+ new BallotChoice(1, "Ananas", 1, List.of(), 0)),
+ List.of());
+
+ var encryptResult = CryptTool.encryptGroupBallotCreateMessage(message.getGroupId(),
+ message.getBallotId(), message.getDescription(), message.getState(),
+ message.getVotingMode(), message.getResultsDisclosureType(),
+ message.getOrder(), message.getDisplayMode(), message.getChoices(),
+ List.of(), privateKey.key, publicKey.key);
+
+ assertThatNoException().isThrownBy(message::getData);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupBallotCreateMessage.class, actual,
+ "message is not an instance of group ballot create message");
+ Assertions.assertEquals(expected,
+ ((GroupBallotCreateMessage) actual).getChoices().get(0).getName());
+ Assertions.assertEquals(expectedState,
+ ((GroupBallotCreateMessage) actual).getState().getValue());
+ Assertions.assertEquals(expectedVotingMode,
+ ((GroupBallotCreateMessage) actual).getVotingMode().getValue());
+ Assertions.assertEquals(expectedDisclosureType,
+ ((GroupBallotCreateMessage) actual).getResultsDisclosureType().getValue());
+ Assertions.assertEquals(expectedDisplayMode,
+ ((GroupBallotCreateMessage) actual).getDisplayMode().getValue());
+ }
+
+ @Test
+ public void testGroupBallotVoteMessage() throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedSelection = false;
+ var expectedBallotId = 1;
+
+ var encryptResult = CryptTool.encryptGroupBallotVoteMessage(Common.groupId,
+ Common.groupId.getGroupCreator(),
+ "ffffffff".getBytes(StandardCharsets.UTF_8),
+ List.of(new VoteChoice(0, false), new VoteChoice(1, true)), privateKey.key,
+ publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupBallotVoteMessage.class, actual,
+ "message is not an instance of group ballot vote message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupBallotVoteMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupBallotVoteMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedSelection,
+ ((GroupBallotVoteMessage) actual).getVotes().get(0).getSelected());
+ Assertions.assertEquals(expectedBallotId,
+ ((GroupBallotVoteMessage) actual).getVotes().get(1).getBallotId());
+ }
+
+ @Test
+ public void testLocationMessage() throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedLatitude = "47.4";
+ var expectedLongitude = "8.1";
+ var expectedAccuracy = Float.valueOf(10.0f);
+ var expectedPoiName = "Rupperswil, A";
+ var expectedPoiAddress = "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil";
+
+ var encryptResult = CryptTool.encryptLocationMessage("47.4", "8.1", 10.0f, "Rupperswil, A",
+ "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil",
+ privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(LocationMessage.class, actual,
+ "message is not an instance of location message");
+ Assertions.assertEquals(expectedLatitude, ((LocationMessage) actual).getLatitude());
+ Assertions.assertEquals(expectedLongitude, ((LocationMessage) actual).getLongitude());
+ Assertions.assertEquals(expectedAccuracy, ((LocationMessage) actual).getAccuracy());
+ Assertions.assertEquals(expectedPoiName, ((LocationMessage) actual).getPoiName());
+ Assertions.assertEquals(expectedPoiAddress, ((LocationMessage) actual).getPoiAddress());
+ }
+
+ @Test
+ public void testGroupLocationMessage() throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedLatitude = "47.4";
+ var expectedLongitude = "8.1";
+ var expectedAccuracy = Float.valueOf(10.0f);
+ var expectedPoiName = "Rupperswil, A";
+ var expectedPoiAddress = "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil";
+
+ var encryptResult = CryptTool.encryptGroupLocationMessage(Common.groupId, "47.4", "8.1",
+ 10.0f, "Rupperswil, A",
+ "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil",
+ privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupLocationMessage.class, actual,
+ "message is not an instance of group location message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedLatitude, ((GroupLocationMessage) actual).getLatitude());
+ Assertions.assertEquals(expectedLongitude, ((GroupLocationMessage) actual).getLongitude());
+ Assertions.assertEquals(expectedAccuracy, ((GroupLocationMessage) actual).getAccuracy());
+ Assertions.assertEquals(expectedPoiName, ((GroupLocationMessage) actual).getPoiName());
+ Assertions.assertEquals(expectedPoiAddress,
+ ((GroupLocationMessage) actual).getPoiAddress());
+ }
+
+ @Test
+ public void testGroupLocationMessageNoAccuracy()
+ throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedLatitude = "47.4";
+ var expectedLongitude = "8.1";
+ Float expectedAccuracy = null;
+ var expectedPoiName = "Rupperswil";
+ var expectedPoiAddress = "Switzerland";
+
+ var encryptResult = CryptTool.encryptGroupLocationMessage(Common.groupId, "47.4", "8.1",
+ null, "Rupperswil", "Switzerland", privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupLocationMessage.class, actual,
+ "message is not an instance of group location message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedLatitude, ((GroupLocationMessage) actual).getLatitude());
+ Assertions.assertEquals(expectedLongitude, ((GroupLocationMessage) actual).getLongitude());
+ Assertions.assertEquals(expectedAccuracy, ((GroupLocationMessage) actual).getAccuracy());
+ Assertions.assertEquals(expectedPoiName, ((GroupLocationMessage) actual).getPoiName());
+ Assertions.assertEquals(expectedPoiAddress,
+ ((GroupLocationMessage) actual).getPoiAddress());
+ }
+
+ @Test
+ public void testGroupLocationMessageNoName() throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedLatitude = "47.4";
+ var expectedLongitude = "8.1";
+ var expectedAccuracy = Float.valueOf(10.0f);
+ String expectedPoiName = null;
+ var expectedPoiAddress = "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil";
+
+ var encryptResult = CryptTool.encryptGroupLocationMessage(Common.groupId, "47.4", "8.1",
+ 10.0f, null, "Bahnhofstrasse 4, 5222 Rupperswil, Switzerland, Rupperswil",
+ privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupLocationMessage.class, actual,
+ "message is not an instance of group location message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedLatitude, ((GroupLocationMessage) actual).getLatitude());
+ Assertions.assertEquals(expectedLongitude, ((GroupLocationMessage) actual).getLongitude());
+ Assertions.assertEquals(expectedAccuracy, ((GroupLocationMessage) actual).getAccuracy());
+ Assertions.assertEquals(expectedPoiName, ((GroupLocationMessage) actual).getPoiName());
+ Assertions.assertEquals(expectedPoiAddress,
+ ((GroupLocationMessage) actual).getPoiAddress());
+ }
+
+ @Test
+ public void testGroupLocationMessageNoNameAndAddress()
+ throws MessageParseException, InvalidKeyException {
+ Key privateKey = Key.decodeKey(Common.otherPrivateKey);
+ Key publicKey = Key.decodeKey(Common.myPublicKey);
+ var expectedLatitude = "47.4";
+ var expectedLongitude = "8.1";
+ var expectedAccuracy = Float.valueOf(10.0f);
+ String expectedPoiName = null;
+ String expectedPoiAddress = null;
+
+ var encryptResult = CryptTool.encryptGroupLocationMessage(Common.groupId, "47.4", "8.1",
+ 10.0f, null, null, privateKey.key, publicKey.key);
+
+ var actual = CryptTool.decryptMessage(encryptResult.getResult(), privateKey.key,
+ publicKey.key, encryptResult.getNonce());
+
+ Assertions.assertNotNull(actual);
+ Assertions.assertInstanceOf(GroupLocationMessage.class, actual,
+ "message is not an instance of group location message");
+ Assertions.assertArrayEquals(Common.groupId.getGroupCreator(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupCreator());
+ Assertions.assertArrayEquals(Common.groupId.getGroupId(),
+ ((GroupLocationMessage) actual).getGroupId().getGroupId());
+ Assertions.assertEquals(expectedLatitude, ((GroupLocationMessage) actual).getLatitude());
+ Assertions.assertEquals(expectedLongitude, ((GroupLocationMessage) actual).getLongitude());
+ Assertions.assertEquals(expectedAccuracy, ((GroupLocationMessage) actual).getAccuracy());
+ Assertions.assertEquals(expectedPoiName, ((GroupLocationMessage) actual).getPoiName());
+ Assertions.assertEquals(expectedPoiAddress,
+ ((GroupLocationMessage) actual).getPoiAddress());
+ }
+}
diff --git a/source/src/test/java/ch/threema/apitool/KeyTest.java b/source/src/test/java/ch/threema/apitool/KeyTest.java
index 104188b..fd3dd58 100644
--- a/source/src/test/java/ch/threema/apitool/KeyTest.java
+++ b/source/src/test/java/ch/threema/apitool/KeyTest.java
@@ -1,8 +1,14 @@
/*
- * $Id$
+ * _____ _
+ * |_ _| |_ _ _ ___ ___ _ __ __ _
+ * | | | ' \| '_/ -_) -_) ' \/ _` |_
+ * |_| |_||_|_| \___\___|_|_|_\__,_(_)
+ *
+ * Threema Gateway Java SDK
+ * This SDK allows for preparing, sending and receiving of Threema Messages via Threema Gateway.
*
* The MIT License (MIT)
- * Copyright (c) 2015 Threema GmbH
+ * Copyright (c) 2015-2024 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
@@ -20,11 +26,17 @@
* 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 ch.threema.apitool.types.Key;
+import ch.threema.apitool.utils.DataUtils;
import org.junit.Test;
public class KeyTest {
@@ -41,19 +53,24 @@ public void testDecodeWrongKey() {
@Test
public void testDecodeKeyPrivate() throws Exception {
- Key key = Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234");
+ 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"));
+ Assert.assertEquals(key.key, DataUtils.hexStringToByteArray(
+ "1234567890123456789012345678901234567890123456789012345678901234"));
}
+
@Test
public void testDecodeKeyPublic() throws Exception {
- Key key = Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234");
+ 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"));
+ Assert.assertEquals(key.key, DataUtils.hexStringToByteArray(
+ "1234567890123456789012345678901234567890123456789012345678901234"));
}
@Test
@@ -68,4 +85,4 @@ public void testEncodePrivate() throws Exception {
Assert.assertEquals(key.encode(), Common.myPrivateKey);
}
-}
\ No newline at end of file
+}
diff --git a/source/src/test/java/ch/threema/apitool/SimpleTest.java b/source/src/test/java/ch/threema/apitool/SimpleTest.java
new file mode 100644
index 0000000..6efe329
--- /dev/null
+++ b/source/src/test/java/ch/threema/apitool/SimpleTest.java
@@ -0,0 +1,46 @@
+package ch.threema.apitool;
+
+import ch.threema.apitool.exceptions.ApiException;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+import java.io.FileReader;
+import java.io.IOException;
+
+public class SimpleTest {
+
+ public static void main(String[] args) {
+ if (args == null || args.length < 3 || args.length > 4) {
+ System.out.printf("Usage: %s Threema-ID Gateway-ID Secret [ApiUrl]%n",
+ new java.io.File(E2ETest.class.getProtectionDomain().getCodeSource()
+ .getLocation().getPath()).getName());
+ System.exit(-1);
+ }
+ var threemaId = args[0];
+ String gatewayId = args[1];
+ var secret = args[2];
+ var apiUrl = args.length > 3 ? args[3] : null;
+ var reader = new MavenXpp3Reader();
+ APIConnector connector;
+ try {
+ connector = new APIConnector(gatewayId, secret, apiUrl, new PublicKeyStore() {
+ @Override
+ protected byte[] fetchPublicKey(String threemaId) {
+ return null;
+ }
+
+ @Override
+ protected void save(String threemaId, byte[] publicKey) {
+
+ }
+ });
+ connector.setUserAgent(String.format("threema-msgapi-sdk-java/%s-test",
+ reader.read(new FileReader("pom.xml")).getVersion()));
+
+ var res = connector.sendTextMessageSimple(threemaId, "Simple text message");
+ System.out.println(res.getData() + " " + res.getStatusCode()); // OK (, no receive)
+ } catch (IOException | XmlPullParserException | ApiException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/source/threema.jpg b/source/threema.jpg
new file mode 100644
index 0000000..e779292
Binary files /dev/null and b/source/threema.jpg differ
diff --git a/source/thumb.png b/source/thumb.png
new file mode 100644
index 0000000..c2d2a6c
Binary files /dev/null and b/source/thumb.png differ