From 38f5e7dd3a88f7f32e1b031120477f6ee293ff53 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 26 Oct 2022 22:19:14 +0200 Subject: [PATCH 01/11] Increases compiler level to Java 17, uses JUnit5 and Assertj --- source/pom.xml | 21 ++- .../test/java/ch/threema/apitool/Assert.java | 38 ----- .../test/java/ch/threema/apitool/Common.java | 57 ++++---- .../ch/threema/apitool/CryptToolTest.java | 137 +++++++++--------- .../test/java/ch/threema/apitool/KeyTest.java | 80 +++++----- 5 files changed, 152 insertions(+), 181 deletions(-) delete mode 100644 source/src/test/java/ch/threema/apitool/Assert.java diff --git a/source/pom.xml b/source/pom.xml index 4fafe59..4e93c92 100644 --- a/source/pom.xml +++ b/source/pom.xml @@ -26,11 +26,18 @@ - junit - junit - 4.13.1 + org.junit.jupiter + junit-jupiter-api + 5.9.1 test + + org.assertj + assertj-core + 3.23.1 + test + + commons-io commons-io @@ -52,18 +59,18 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 ${source.encoding} - 11 - 11 + 17 + 17 org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.3.0 diff --git a/source/src/test/java/ch/threema/apitool/Assert.java b/source/src/test/java/ch/threema/apitool/Assert.java deleted file mode 100644 index e3dce18..0000000 --- a/source/src/test/java/ch/threema/apitool/Assert.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id$ - * - * The MIT License (MIT) - * Copyright (c) 2015 Threema GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE - */ -package ch.threema.apitool; - -import java.util.Arrays; - -/** - * Assert - */ -public class Assert extends org.junit.Assert { - public static void assertEquals(byte[] expected, byte[] actual) { - assertEquals(null, expected, actual); - } - public static void assertEquals(String message, byte[] expected, byte[] actual) { - assertEquals(message, Arrays.toString(expected), Arrays.toString(actual)); - } -} diff --git a/source/src/test/java/ch/threema/apitool/Common.java b/source/src/test/java/ch/threema/apitool/Common.java index c3af9a0..90f6f38 100644 --- a/source/src/test/java/ch/threema/apitool/Common.java +++ b/source/src/test/java/ch/threema/apitool/Common.java @@ -24,35 +24,34 @@ package ch.threema.apitool; -/** - * Common Stuff - */ +/** Common Stuff */ public abstract class Common { - public static final String myPrivateKey = "private:94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2"; - public static final String myPrivateKeyExtract = "94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2"; - - public static final String myPublicKey = "public:3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531"; - public static final String myPublicKeyExtract = "3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531"; - - public static final String otherPrivateKey = "private:8318e05220acd38e97ba41a9a6318688214219916075ca060f9339a6d1f7fc29"; - public static final String otherPublicKey = "public:10ac7fd937eafb806f9a05bf9afa340a99387b0063cc9cb0d1ea5505d39cc076"; - - public static final String echochoPublicKey = "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b"; - public static final String randomNonce = "516f4f1562dda0704a7bae8997cf0b354c6980181152ac32"; - - public static boolean isEmpty(byte[] byteArray) { - if(byteArray == null) { - return true; - } - else { - for(byte b: byteArray) { - if(b != 0) { - return false; - } - } - } - - return true; - } + public static final String myPrivateKey = + "private:94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2"; + public static final String myPrivateKeyExtract = + "94af3260fa2a19adc8e82e82be598be15bc6ad6f47c8ee303cb185ef860e16d2"; + + public static final String myPublicKey = + "public:3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531"; + public static final String myPublicKeyExtract = + "3851ad59c96146a05b32e41c0ccd0fd639dc8cd66bf6e1cbd3c8d67e4e8f5531"; + + public static final String otherPrivateKey = + "private:8318e05220acd38e97ba41a9a6318688214219916075ca060f9339a6d1f7fc29"; + public static final String otherPublicKey = + "public:10ac7fd937eafb806f9a05bf9afa340a99387b0063cc9cb0d1ea5505d39cc076"; + + public static final String echochoPublicKey = + "public:4a6a1b34dcef15d43cb74de2fd36091be99fbbaf126d099d47d83d919712c72b"; + public static final String randomNonce = "516f4f1562dda0704a7bae8997cf0b354c6980181152ac32"; + + public static boolean hasContent(byte[] byteArray) { + for (byte b : byteArray) { + if (b != 0) { + return true; + } + } + return false; + } } diff --git a/source/src/test/java/ch/threema/apitool/CryptToolTest.java b/source/src/test/java/ch/threema/apitool/CryptToolTest.java index 141f12f..f724b3f 100644 --- a/source/src/test/java/ch/threema/apitool/CryptToolTest.java +++ b/source/src/test/java/ch/threema/apitool/CryptToolTest.java @@ -24,75 +24,76 @@ package ch.threema.apitool; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; import com.neilalexander.jnacl.NaCl; import ch.threema.apitool.messages.TextMessage; -import ch.threema.apitool.messages.ThreemaMessage; -import ch.threema.apitool.results.EncryptResult; - -public class CryptToolTest { - - @Test - public void testRandomNonce() throws Exception { - byte[] randomNonce = CryptTool.randomNonce(); - - // random nonce should be a byte array - Assert.assertNotNull("random nonce", randomNonce); - - // with a length of 24 - Assert.assertSame("nonce length", randomNonce.length, NaCl.NONCEBYTES); - } - - @Test - public void testCreateKeyPair() { - byte[] privateKey = new byte[NaCl.SECRETKEYBYTES]; - byte[] publicKey = new byte[NaCl.PUBLICKEYBYTES]; - - CryptTool.generateKeyPair(privateKey, publicKey); - - Assert.assertFalse("empty private key", Common.isEmpty(privateKey)); - Assert.assertFalse("empty public key", Common.isEmpty(publicKey)); - } - - @Test - public void testDecrypt() throws Exception { - String nonce = "0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0"; - String box = "45181c7aed95a1c100b1b559116c61b43ce15d04014a805288b7d14bf3a993393264fe554794ce7d6007233e8ef5a0f1ccdd704f34e7c7b77c72c239182caf1d061d6fff6ffbbfe8d3b8f3475c2fe352e563aa60290c666b2e627761e32155e62f048b52ef2f39c13ac229f393c67811749467396ecd09f42d32a4eb419117d0451056ac18fac957c52b0cca67568e2d97e5a3fd829a77f914a1ad403c5909fd510a313033422ea5db71eaf43d483238612a54cb1ecfe55259b1de5579e67c6505df7d674d34a737edf721ea69d15b567bc2195ec67e172f3cb8d6842ca88c29138cc33e9351dbc1e4973a82e1cf428c1c763bb8f3eb57770f914a"; - - Key privateKey = Key.decodeKey(Common.otherPrivateKey); - Key publicKey = Key.decodeKey(Common.myPublicKey); - - ThreemaMessage message = CryptTool.decryptMessage(DataUtils.hexStringToByteArray(box), privateKey.key, - publicKey.key, DataUtils.hexStringToByteArray(nonce)); - - Assert.assertNotNull(message); - Assert.assertTrue("message is not a instance of text message", message instanceof TextMessage); - Assert.assertEquals(((TextMessage) message).getText(), "Dies ist eine Testnachricht. äöü"); - } - - @Test - public void testEncrypt() throws Exception { - String text = "Dies ist eine Testnachricht. äöü"; - - Key privateKey = Key.decodeKey(Common.myPrivateKey); - Key publicKey = Key.decodeKey(Common.otherPublicKey); - - EncryptResult res = CryptTool.encryptTextMessage(text, privateKey.key, publicKey.key); - Assert.assertNotNull(res); - Assert.assertNotNull(res.getNonce()); - Assert.assertNotNull(res.getResult()); - Assert.assertFalse(Common.isEmpty(res.getNonce())); - Assert.assertFalse(Common.isEmpty(res.getResult())); - } - - @Test - public void testDerivePublicKey() throws Exception { - Key privateKey = Key.decodeKey(Common.myPrivateKey); - Key publicKey = Key.decodeKey(Common.myPublicKey); - byte[] derivedPublicKey = CryptTool.derivePublicKey(privateKey.key); - Assert.assertNotNull("derived public key", derivedPublicKey); - Assert.assertEquals(derivedPublicKey, publicKey.key); - } -} \ No newline at end of file + +class CryptToolTest { + + @Test + void testRandomNonce() throws Exception { + var randomNonce = CryptTool.randomNonce(); + + // with a length of 24 + assertThat(randomNonce).hasSize(NaCl.NONCEBYTES); + } + + @Test + void testCreateKeyPair() { + var privateKey = new byte[NaCl.SECRETKEYBYTES]; + var publicKey = new byte[NaCl.PUBLICKEYBYTES]; + + CryptTool.generateKeyPair(privateKey, publicKey); + + assertThat(privateKey).isNotNull().satisfies(Common::hasContent); + assertThat(publicKey).isNotNull().satisfies(Common::hasContent); + } + + @Test + void testDecrypt() throws Exception { + var nonce = "0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0"; + var box = + "45181c7aed95a1c100b1b559116c61b43ce15d04014a805288b7d14bf3a993393264fe554794ce7d6007233e8ef5a0f1ccdd704f34e7c7b77c72c239182caf1d061d6fff6ffbbfe8d3b8f3475c2fe352e563aa60290c666b2e627761e32155e62f048b52ef2f39c13ac229f393c67811749467396ecd09f42d32a4eb419117d0451056ac18fac957c52b0cca67568e2d97e5a3fd829a77f914a1ad403c5909fd510a313033422ea5db71eaf43d483238612a54cb1ecfe55259b1de5579e67c6505df7d674d34a737edf721ea69d15b567bc2195ec67e172f3cb8d6842ca88c29138cc33e9351dbc1e4973a82e1cf428c1c763bb8f3eb57770f914a"; + + var privateKey = Key.decodeKey(Common.otherPrivateKey); + var publicKey = Key.decodeKey(Common.myPublicKey); + + var message = + CryptTool.decryptMessage( + DataUtils.hexStringToByteArray(box), + privateKey.key, + publicKey.key, + DataUtils.hexStringToByteArray(nonce)); + + assertThat(message) + .isInstanceOf(TextMessage.class) + .extracting(msg -> ((TextMessage) msg).getText()) + .isEqualTo("Dies ist eine Testnachricht. äöü"); + } + + @Test + void testEncrypt() throws Exception { + var text = "Dies ist eine Testnachricht. äöü"; + + var privateKey = Key.decodeKey(Common.myPrivateKey); + var publicKey = Key.decodeKey(Common.otherPublicKey); + + var res = CryptTool.encryptTextMessage(text, privateKey.key, publicKey.key); + + assertThat(res).isNotNull(); + assertThat(res.getNonce()).isNotNull().satisfies(Common::hasContent); + assertThat(res.getResult()).isNotNull().satisfies(Common::hasContent); + } + + @Test + void testDerivePublicKey() throws Exception { + var privateKey = Key.decodeKey(Common.myPrivateKey); + var publicKey = Key.decodeKey(Common.myPublicKey); + var derivedPublicKey = CryptTool.derivePublicKey(privateKey.key); + assertThat(derivedPublicKey).isEqualTo(publicKey.key); + } +} diff --git a/source/src/test/java/ch/threema/apitool/KeyTest.java b/source/src/test/java/ch/threema/apitool/KeyTest.java index 104188b..7bb5d5f 100644 --- a/source/src/test/java/ch/threema/apitool/KeyTest.java +++ b/source/src/test/java/ch/threema/apitool/KeyTest.java @@ -1,6 +1,4 @@ /* - * $Id$ - * * The MIT License (MIT) * Copyright (c) 2015 Threema GmbH * @@ -24,48 +22,52 @@ package ch.threema.apitool; -import ch.threema.apitool.exceptions.InvalidKeyException; -import org.junit.Test; - -public class KeyTest { +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - @Test - public void testDecodeWrongKey() { - try { - Key.decodeKey("imnotarealkey"); - } catch (InvalidKeyException e) { - return; - } - Assert.assertFalse("could parse invalid key", true); - } +import org.junit.jupiter.api.Test; - @Test - public void testDecodeKeyPrivate() throws Exception { - Key key = Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234"); - Assert.assertNotNull("key instance", key); +import ch.threema.apitool.exceptions.InvalidKeyException; - Assert.assertEquals(key.type, Key.KeyType.PRIVATE); - Assert.assertEquals(key.key, DataUtils.hexStringToByteArray("1234567890123456789012345678901234567890123456789012345678901234")); - } - @Test - public void testDecodeKeyPublic() throws Exception { - Key key = Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234"); - Assert.assertNotNull("key instance", key); +class KeyTest { - Assert.assertEquals(key.type, Key.KeyType.PUBLIC); - Assert.assertEquals(key.key, DataUtils.hexStringToByteArray("1234567890123456789012345678901234567890123456789012345678901234")); - } + @Test + void testDecodeWrongKey() { + assertThatExceptionOfType(InvalidKeyException.class) + .isThrownBy(() -> Key.decodeKey("imnotarealkey")); + } - @Test - public void testEncodePrivate() throws Exception { - byte[] keyAsByte = DataUtils.hexStringToByteArray(Common.myPrivateKeyExtract); + @Test + void testDecodeKeyPrivate() throws Exception { + var key = + Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234"); - Key key = new Key(Key.KeyType.PRIVATE, keyAsByte); - Assert.assertNotNull("key instance", key); + assertThat(key).isNotNull(); + assertThat(key.type).isEqualTo(Key.KeyType.PRIVATE); + assertThat(key.key) + .isEqualTo( + DataUtils.hexStringToByteArray( + "1234567890123456789012345678901234567890123456789012345678901234")); + } - Assert.assertEquals(Key.KeyType.PRIVATE, key.type); - Assert.assertEquals(key.key, keyAsByte); + @Test + void testDecodeKeyPublic() throws Exception { + var key = + Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234"); + assertThat(key).isNotNull(); + assertThat(key.type).isEqualTo(Key.KeyType.PUBLIC); + assertThat(key.key) + .isEqualTo( + DataUtils.hexStringToByteArray( + "1234567890123456789012345678901234567890123456789012345678901234")); + } - Assert.assertEquals(key.encode(), Common.myPrivateKey); - } -} \ No newline at end of file + @Test + void testEncodePrivate() throws Exception { + var keyAsByte = DataUtils.hexStringToByteArray(Common.myPrivateKeyExtract); + var key = new Key(Key.KeyType.PRIVATE, keyAsByte); + assertThat(key.type).isEqualTo(Key.KeyType.PRIVATE); + assertThat(key.key).isEqualTo(keyAsByte); + assertThat(key.encode()).isEqualTo(Common.myPrivateKey); + } +} From 228f79e4e176dd3670ce09445fdfd9674f7e2eaa Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 26 Oct 2022 22:19:31 +0200 Subject: [PATCH 02/11] Formats code with Google formatter --- .../java/ch/threema/apitool/APIConnector.java | 765 ++++++++-------- .../java/ch/threema/apitool/ConsoleMain.java | 259 +++--- .../java/ch/threema/apitool/CryptTool.java | 751 ++++++++------- .../java/ch/threema/apitool/DataUtils.java | 198 ++-- .../src/main/java/ch/threema/apitool/Key.java | 117 ++- .../java/ch/threema/apitool/MessageId.java | 44 +- .../ch/threema/apitool/PublicKeyStore.java | 101 +- .../console/commands/CapabilityCommand.java | 47 +- .../apitool/console/commands/Command.java | 332 +++---- .../console/commands/CreditsCommand.java | 36 +- .../commands/DecryptAndDownloadCommand.java | 69 +- .../console/commands/DecryptCommand.java | 41 +- .../commands/DerivePublicKeyCommand.java | 26 +- .../console/commands/EncryptCommand.java | 37 +- .../console/commands/FetchPublicKey.java | 41 +- .../commands/GenerateKeyPairCommand.java | 45 +- .../console/commands/HashEmailCommand.java | 22 +- .../console/commands/HashPhoneCommand.java | 23 +- .../console/commands/IDLookupByEmail.java | 43 +- .../console/commands/IDLookupByPhoneNo.java | 43 +- .../commands/SendE2EFileMessageCommand.java | 59 +- .../commands/SendE2EImageMessageCommand.java | 51 +- .../commands/SendE2ETextMessageCommand.java | 47 +- .../commands/SendSimpleMessageCommand.java | 51 +- .../commands/fields/ByteArrayField.java | 12 +- .../console/commands/fields/Field.java | 58 +- .../console/commands/fields/FileField.java | 28 +- .../console/commands/fields/FolderField.java | 12 +- .../console/commands/fields/KeyField.java | 35 +- .../commands/fields/PrivateKeyField.java | 20 +- .../commands/fields/PublicKeyField.java | 21 +- .../console/commands/fields/TextField.java | 12 +- .../commands/fields/ThreemaIDField.java | 12 +- .../exceptions/BadMessageException.java | 6 +- .../exceptions/DecryptionFailedException.java | 5 +- .../InvalidCommandFieldValueException.java | 12 +- .../exceptions/InvalidKeyException.java | 12 +- .../exceptions/MessageParseException.java | 2 +- .../exceptions/NotAllowedException.java | 6 +- .../RequiredCommandFieldMissingException.java | 12 +- .../UnsupportedMessageTypeException.java | 6 +- .../ch/threema/apitool/helpers/E2EHelper.java | 514 ++++++----- .../apitool/messages/DeliveryReceipt.java | 156 ++-- .../threema/apitool/messages/FileMessage.java | 228 ++--- .../apitool/messages/ImageMessage.java | 102 +-- .../threema/apitool/messages/TextMessage.java | 52 +- .../apitool/messages/ThreemaMessage.java | 18 +- .../apitool/results/CapabilityResult.java | 122 ++- .../apitool/results/EncryptResult.java | 60 +- .../threema/apitool/results/UploadResult.java | 46 +- .../java/com/neilalexander/jnacl/NaCl.java | 860 +++++++++++++----- .../jnacl/crypto/curve25519.java | 840 ++++++++--------- .../crypto/curve25519xsalsa20poly1305.java | 155 ++-- .../neilalexander/jnacl/crypto/hsalsa20.java | 241 +++-- .../neilalexander/jnacl/crypto/poly1305.java | 282 +++--- .../neilalexander/jnacl/crypto/salsa20.java | 571 ++++++------ .../neilalexander/jnacl/crypto/verify_16.java | 27 +- .../neilalexander/jnacl/crypto/xsalsa20.java | 81 +- .../jnacl/crypto/xsalsa20poly1305.java | 141 ++- 59 files changed, 4178 insertions(+), 3837 deletions(-) diff --git a/source/src/main/java/ch/threema/apitool/APIConnector.java b/source/src/main/java/ch/threema/apitool/APIConnector.java index a310191..8e80538 100644 --- a/source/src/main/java/ch/threema/apitool/APIConnector.java +++ b/source/src/main/java/ch/threema/apitool/APIConnector.java @@ -36,385 +36,392 @@ import java.util.HashMap; import java.util.Map; -/** - * Facilitates HTTPS communication with the Threema Message API. - */ +/** Facilitates HTTPS communication with the Threema Message API. */ public class APIConnector { - private static final int BUFFER_SIZE = 16384; - - public interface ProgressListener { - - /** - * Update the progress of an upload/download process. - * - * @param progress in percent (0..100) - */ - void updateProgress(int progress); - } - - public class InputStreamLength { - public final InputStream inputStream; - public final int length; - - public InputStreamLength(InputStream inputStream, int length) { - this.inputStream = inputStream; - this.length = length; - } - } - - private final String apiUrl; - private final PublicKeyStore publicKeyStore; - private final String apiIdentity; - private final String secret; - - public APIConnector(String apiIdentity, String secret, PublicKeyStore publicKeyStore) { - this(apiIdentity, secret, "https://msgapi.threema.ch/", publicKeyStore); - } - - public APIConnector(String apiIdentity, String secret, String apiUrl, PublicKeyStore publicKeyStore) { - this.apiIdentity = apiIdentity; - this.secret = secret; - this.apiUrl = apiUrl; - this.publicKeyStore = publicKeyStore; - } - - /** - * Send a text message with server-side encryption. - * - * @param to recipient ID - * @param text message text (max. 3500 bytes) - * @return message ID - * @throws IOException if a communication or server error occurs - */ - public String sendTextMessageSimple(String to, String text) throws IOException { - - Map postParams = makeRequestParams(); - postParams.put("to", to); - postParams.put("text", text); - - return doPost(new URL(this.apiUrl + "send_simple"), postParams); - } - - /** - * Send an end-to-end encrypted message. - * - * @param to recipient ID - * @param nonce nonce used for encryption (24 bytes) - * @param box encrypted message data (max. 4000 bytes) - * @return message ID - * @throws IOException if a communication or server error occurs - */ - public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOException { - - Map postParams = makeRequestParams(); - postParams.put("to", to); - postParams.put("nonce", DataUtils.byteArrayToHexString(nonce)); - postParams.put("box", DataUtils.byteArrayToHexString(box)); - - return doPost(new URL(this.apiUrl + "send_e2e"), postParams); - } - - /** - * Lookup an ID by phone number. The phone number will be hashed before - * being sent to the server. - * - * @param phoneNumber the phone number in E.164 format - * @return the ID, or null if not found - * @throws IOException if a communication or server error occurs - */ - public String lookupPhone(String phoneNumber) throws IOException { - - try { - Map getParams = makeRequestParams(); - - byte[] phoneHash = CryptTool.hashPhoneNo(phoneNumber); - - return doGet(new URL(this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)), getParams); - } catch (FileNotFoundException e) { - return null; - } - } - - /** - * Lookup an ID by email address. The email address will be hashed before - * being sent to the server. - * - * @param email the email address - * @return the ID, or null if not found - * @throws IOException if a communication or server error occurs - */ - public String lookupEmail(String email) throws IOException { - - try { - Map getParams = makeRequestParams(); - - byte[] emailHash = CryptTool.hashEmail(email); - - return doGet(new URL(this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)), getParams); - } catch (FileNotFoundException e) { - return null; - } - } - - /** - * Lookup a public key by ID. - * - * @param id the ID whose public key is desired - * @return the corresponding public key, or null if not found - * @throws IOException if a communication or server error occurs - */ - public byte[] lookupKey(String id) throws IOException { - byte[] key = this.publicKeyStore.getPublicKey(id); - if(key == null) { - try { - Map getParams = makeRequestParams(); - String pubkeyHex = doGet(new URL(this.apiUrl + "pubkeys/" + id), getParams); - key = DataUtils.hexStringToByteArray(pubkeyHex); - - if(key != null) { - this.publicKeyStore.save(id, key); - } - } catch (FileNotFoundException e) { - return null; - } - } - return key; - } - - /** - * Lookup the capabilities of a ID - * - * @param threemaId The ID whose capabilities should be checked - * @return The capabilities, or null if not found - * @throws IOException - */ - public CapabilityResult lookupKeyCapability(String threemaId) throws IOException { - String res = doGet(new URL(this.apiUrl + "capabilities/" + threemaId), - makeRequestParams()); - if(res != null) { - return new CapabilityResult(threemaId, res.split(",")); - } - return null; - } - - public Integer lookupCredits() throws IOException { - String res = doGet(new URL(this.apiUrl + "credits"), - makeRequestParams()); - if(res != null) { - return Integer.valueOf(res); - } - return null; - } - /** - * Upload a file. - * - * @param fileEncryptionResult The result of the file encryption (i.e. encrypted file data) - * @return the result of the upload - * @throws IOException - */ - public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOException{ - - String attachmentName = "blob"; - String attachmentFileName = "blob.file"; - String crlf = "\r\n"; - String twoHyphens = "--"; - - char[] chars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); - String boundary = ""; - SecureRandom rand = new SecureRandom(); - int count = rand.nextInt(11) + 30; - for (int i = 0; i < count; i++) { - boundary += chars[rand.nextInt(chars.length)]; - } - - - String queryString = makeUrlEncoded(makeRequestParams()); - URL url = new URL(this.apiUrl + "upload_blob?" + queryString); - - HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setUseCaches(false); - - connection.setRequestMethod("POST"); - connection.setRequestProperty("Connection", "Keep-Alive"); - connection.setRequestProperty("Cache-Control", "no-cache"); - connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); - - DataOutputStream request = new DataOutputStream(connection.getOutputStream()); - - request.writeBytes(twoHyphens + boundary + crlf); - request.writeBytes("Content-Disposition: form-data; name=\"" + attachmentName + "\";filename=\"" + attachmentFileName + "\"" + crlf); - request.writeBytes(crlf); - request.write(fileEncryptionResult.getResult()); - request.writeBytes(crlf); - request.writeBytes(twoHyphens + boundary + twoHyphens + crlf); - - String response = null; - int responseCode = connection.getResponseCode(); - - if(responseCode == 200) { - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - response = br.readLine(); - br.close(); - } - - connection.disconnect(); - - return new UploadResult(responseCode, response != null ? DataUtils.hexStringToByteArray(response) : null); - } - - /** - * Download a file given its blob ID. - * - * @param blobId The blob ID of the file - * @return Encrypted file data - * @throws IOException - */ - public byte[] downloadFile(byte[] blobId) throws IOException { - return this.downloadFile(blobId, null); - } - - /** - * Download a file given its blob ID. - * - * @param blobId The blob ID of the file - * @param progressListener An object that will receive progress information, or null - * @return Encrypted file data - * @throws IOException - */ - public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) throws IOException { - String queryString = makeUrlEncoded(makeRequestParams()); - URL blobUrl = new URL(String.format(this.apiUrl + "blobs/%s?%s", - DataUtils.byteArrayToHexString(blobId), - queryString)); - - HttpsURLConnection connection = (HttpsURLConnection)blobUrl.openConnection(); - connection.setConnectTimeout(20*1000); - connection.setReadTimeout(20*1000); - connection.setDoOutput(false); - - InputStream inputStream = connection.getInputStream(); - int contentLength = connection.getContentLength(); - InputStreamLength isl = new InputStreamLength(inputStream, contentLength); - - - /* Content length known? */ - byte[] blob; - if (isl.length != -1) { - blob = new byte[isl.length]; - int offset = 0; - int readed; - - while (offset < isl.length && (readed = isl.inputStream.read(blob, offset, isl.length - offset)) != -1) { - offset += readed; - - if (progressListener != null) { - progressListener.updateProgress(100 * offset / isl.length); - } - } - - if (offset != isl.length) { - throw new IOException("Unexpected read size. current: " + offset + ", excepted: " + isl.length); - } - } else { - /* Content length is unknown - need to read until EOF */ - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buffer = new byte[BUFFER_SIZE]; - - int read; - while ((read = isl.inputStream.read(buffer)) != -1) { - bos.write(buffer, 0, read); - } - - blob = bos.toByteArray(); - } - if (progressListener != null) { - progressListener.updateProgress(100); - } - - return blob; - } - - private Map makeRequestParams() { - Map postParams = new HashMap(); - - postParams.put("from", apiIdentity); - postParams.put("secret", secret); - return postParams; - } - - private String doGet(URL url, Map getParams) throws IOException { - - if (getParams != null) { - String queryString = makeUrlEncoded(getParams); - - url = new URL(url.toString() + "?" + queryString); - } - - HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); - connection.setDoOutput(false); - connection.setDoInput(true); - connection.setInstanceFollowRedirects(false); - connection.setRequestMethod("GET"); - connection.setUseCaches(false); - - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String response = br.readLine(); - br.close(); - - connection.disconnect(); - - return response; - } - - private String doPost(URL url, Map postParams) throws IOException { - - byte[] postData = makeUrlEncoded(postParams).getBytes("UTF-8"); - - HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setInstanceFollowRedirects(false); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - connection.setRequestProperty("Charset", "utf-8"); - connection.setRequestProperty("Content-Length", Integer.toString(postData.length)); - connection.setUseCaches(false); - - OutputStream os = connection.getOutputStream(); - os.write(postData); - os.flush(); - os.close(); - - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String response = br.readLine(); - br.close(); - - connection.disconnect(); - - return response; - } - - private String makeUrlEncoded(Map params) { - StringBuilder s = new StringBuilder(); - - for (Map.Entry param : params.entrySet()) { - if (s.length() > 0) - s.append('&'); - - s.append(param.getKey()); - s.append('='); - try { - s.append(URLEncoder.encode(param.getValue(), "UTF-8")); - } catch (UnsupportedEncodingException ignored) {} - } - - return s.toString(); - } + private static final int BUFFER_SIZE = 16384; + + public interface ProgressListener { + + /** + * Update the progress of an upload/download process. + * + * @param progress in percent (0..100) + */ + void updateProgress(int progress); + } + + public class InputStreamLength { + public final InputStream inputStream; + public final int length; + + public InputStreamLength(InputStream inputStream, int length) { + this.inputStream = inputStream; + this.length = length; + } + } + + private final String apiUrl; + private final PublicKeyStore publicKeyStore; + private final String apiIdentity; + private final String secret; + + public APIConnector(String apiIdentity, String secret, PublicKeyStore publicKeyStore) { + this(apiIdentity, secret, "https://msgapi.threema.ch/", publicKeyStore); + } + + public APIConnector( + String apiIdentity, String secret, String apiUrl, PublicKeyStore publicKeyStore) { + this.apiIdentity = apiIdentity; + this.secret = secret; + this.apiUrl = apiUrl; + this.publicKeyStore = publicKeyStore; + } + + /** + * Send a text message with server-side encryption. + * + * @param to recipient ID + * @param text message text (max. 3500 bytes) + * @return message ID + * @throws IOException if a communication or server error occurs + */ + public String sendTextMessageSimple(String to, String text) throws IOException { + + Map postParams = makeRequestParams(); + postParams.put("to", to); + postParams.put("text", text); + + return doPost(new URL(this.apiUrl + "send_simple"), postParams); + } + + /** + * Send an end-to-end encrypted message. + * + * @param to recipient ID + * @param nonce nonce used for encryption (24 bytes) + * @param box encrypted message data (max. 4000 bytes) + * @return message ID + * @throws IOException if a communication or server error occurs + */ + public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOException { + + Map postParams = makeRequestParams(); + postParams.put("to", to); + postParams.put("nonce", DataUtils.byteArrayToHexString(nonce)); + postParams.put("box", DataUtils.byteArrayToHexString(box)); + + return doPost(new URL(this.apiUrl + "send_e2e"), postParams); + } + + /** + * Lookup an ID by phone number. The phone number will be hashed before being sent to the server. + * + * @param phoneNumber the phone number in E.164 format + * @return the ID, or null if not found + * @throws IOException if a communication or server error occurs + */ + public String lookupPhone(String phoneNumber) throws IOException { + + try { + Map getParams = makeRequestParams(); + + byte[] phoneHash = CryptTool.hashPhoneNo(phoneNumber); + + return doGet( + new URL(this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)), + getParams); + } catch (FileNotFoundException e) { + return null; + } + } + + /** + * Lookup an ID by email address. The email address will be hashed before being sent to the + * server. + * + * @param email the email address + * @return the ID, or null if not found + * @throws IOException if a communication or server error occurs + */ + public String lookupEmail(String email) throws IOException { + + try { + Map getParams = makeRequestParams(); + + byte[] emailHash = CryptTool.hashEmail(email); + + return doGet( + new URL(this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)), + getParams); + } catch (FileNotFoundException e) { + return null; + } + } + + /** + * Lookup a public key by ID. + * + * @param id the ID whose public key is desired + * @return the corresponding public key, or null if not found + * @throws IOException if a communication or server error occurs + */ + public byte[] lookupKey(String id) throws IOException { + byte[] key = this.publicKeyStore.getPublicKey(id); + if (key == null) { + try { + Map getParams = makeRequestParams(); + String pubkeyHex = doGet(new URL(this.apiUrl + "pubkeys/" + id), getParams); + key = DataUtils.hexStringToByteArray(pubkeyHex); + + if (key != null) { + this.publicKeyStore.save(id, key); + } + } catch (FileNotFoundException e) { + return null; + } + } + return key; + } + + /** + * Lookup the capabilities of a ID + * + * @param threemaId The ID whose capabilities should be checked + * @return The capabilities, or null if not found + * @throws IOException + */ + public CapabilityResult lookupKeyCapability(String threemaId) throws IOException { + String res = doGet(new URL(this.apiUrl + "capabilities/" + threemaId), makeRequestParams()); + if (res != null) { + return new CapabilityResult(threemaId, res.split(",")); + } + return null; + } + + public Integer lookupCredits() throws IOException { + String res = doGet(new URL(this.apiUrl + "credits"), makeRequestParams()); + if (res != null) { + return Integer.valueOf(res); + } + return null; + } + /** + * Upload a file. + * + * @param fileEncryptionResult The result of the file encryption (i.e. encrypted file data) + * @return the result of the upload + * @throws IOException + */ + public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOException { + + String attachmentName = "blob"; + String attachmentFileName = "blob.file"; + String crlf = "\r\n"; + String twoHyphens = "--"; + + char[] chars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + String boundary = ""; + SecureRandom rand = new SecureRandom(); + int count = rand.nextInt(11) + 30; + for (int i = 0; i < count; i++) { + boundary += chars[rand.nextInt(chars.length)]; + } + + String queryString = makeUrlEncoded(makeRequestParams()); + URL url = new URL(this.apiUrl + "upload_blob?" + queryString); + + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + + connection.setRequestMethod("POST"); + connection.setRequestProperty("Connection", "Keep-Alive"); + connection.setRequestProperty("Cache-Control", "no-cache"); + connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + + DataOutputStream request = new DataOutputStream(connection.getOutputStream()); + + request.writeBytes(twoHyphens + boundary + crlf); + request.writeBytes( + "Content-Disposition: form-data; name=\"" + + attachmentName + + "\";filename=\"" + + attachmentFileName + + "\"" + + crlf); + request.writeBytes(crlf); + request.write(fileEncryptionResult.getResult()); + request.writeBytes(crlf); + request.writeBytes(twoHyphens + boundary + twoHyphens + crlf); + + String response = null; + int responseCode = connection.getResponseCode(); + + if (responseCode == 200) { + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + response = br.readLine(); + br.close(); + } + + connection.disconnect(); + + return new UploadResult( + responseCode, response != null ? DataUtils.hexStringToByteArray(response) : null); + } + + /** + * Download a file given its blob ID. + * + * @param blobId The blob ID of the file + * @return Encrypted file data + * @throws IOException + */ + public byte[] downloadFile(byte[] blobId) throws IOException { + return this.downloadFile(blobId, null); + } + + /** + * Download a file given its blob ID. + * + * @param blobId The blob ID of the file + * @param progressListener An object that will receive progress information, or null + * @return Encrypted file data + * @throws IOException + */ + public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) throws IOException { + String queryString = makeUrlEncoded(makeRequestParams()); + URL blobUrl = + new URL( + String.format( + this.apiUrl + "blobs/%s?%s", DataUtils.byteArrayToHexString(blobId), queryString)); + + HttpsURLConnection connection = (HttpsURLConnection) blobUrl.openConnection(); + connection.setConnectTimeout(20 * 1000); + connection.setReadTimeout(20 * 1000); + connection.setDoOutput(false); + + InputStream inputStream = connection.getInputStream(); + int contentLength = connection.getContentLength(); + InputStreamLength isl = new InputStreamLength(inputStream, contentLength); + + /* Content length known? */ + byte[] blob; + if (isl.length != -1) { + blob = new byte[isl.length]; + int offset = 0; + int readed; + + while (offset < isl.length + && (readed = isl.inputStream.read(blob, offset, isl.length - offset)) != -1) { + offset += readed; + + if (progressListener != null) { + progressListener.updateProgress(100 * offset / isl.length); + } + } + + if (offset != isl.length) { + throw new IOException( + "Unexpected read size. current: " + offset + ", excepted: " + isl.length); + } + } else { + /* Content length is unknown - need to read until EOF */ + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[BUFFER_SIZE]; + + int read; + while ((read = isl.inputStream.read(buffer)) != -1) { + bos.write(buffer, 0, read); + } + + blob = bos.toByteArray(); + } + if (progressListener != null) { + progressListener.updateProgress(100); + } + + return blob; + } + + private Map makeRequestParams() { + Map postParams = new HashMap(); + + postParams.put("from", apiIdentity); + postParams.put("secret", secret); + return postParams; + } + + private String doGet(URL url, Map getParams) throws IOException { + + if (getParams != null) { + String queryString = makeUrlEncoded(getParams); + + url = new URL(url.toString() + "?" + queryString); + } + + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setDoOutput(false); + connection.setDoInput(true); + connection.setInstanceFollowRedirects(false); + connection.setRequestMethod("GET"); + connection.setUseCaches(false); + + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String response = br.readLine(); + br.close(); + + connection.disconnect(); + + return response; + } + + private String doPost(URL url, Map postParams) throws IOException { + + byte[] postData = makeUrlEncoded(postParams).getBytes("UTF-8"); + + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setInstanceFollowRedirects(false); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + connection.setRequestProperty("Charset", "utf-8"); + connection.setRequestProperty("Content-Length", Integer.toString(postData.length)); + connection.setUseCaches(false); + + OutputStream os = connection.getOutputStream(); + os.write(postData); + os.flush(); + os.close(); + + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String response = br.readLine(); + br.close(); + + connection.disconnect(); + + return response; + } + + private String makeUrlEncoded(Map params) { + StringBuilder s = new StringBuilder(); + + for (Map.Entry param : params.entrySet()) { + if (s.length() > 0) s.append('&'); + + s.append(param.getKey()); + s.append('='); + try { + s.append(URLEncoder.encode(param.getValue(), "UTF-8")); + } catch (UnsupportedEncodingException ignored) { + } + } + return s.toString(); + } } diff --git a/source/src/main/java/ch/threema/apitool/ConsoleMain.java b/source/src/main/java/ch/threema/apitool/ConsoleMain.java index fff8668..846ad8a 100644 --- a/source/src/main/java/ch/threema/apitool/ConsoleMain.java +++ b/source/src/main/java/ch/threema/apitool/ConsoleMain.java @@ -50,159 +50,170 @@ import ch.threema.apitool.console.commands.SendSimpleMessageCommand; /** - * Command line interface for {@link CryptTool} and {@link APIConnector} operations - * for testing purposes and simple invocation from other programming languages. + * Command line interface for {@link CryptTool} and {@link APIConnector} operations for testing + * purposes and simple invocation from other programming languages. */ public class ConsoleMain { - static class Commands { - protected final List commandGroups = new ArrayList<>(); + static class Commands { + protected final List commandGroups = new ArrayList<>(); - public CommandGroup create(String description) { - CommandGroup g = new CommandGroup(description); - this.commandGroups.add(g); - return g; - } + public CommandGroup create(String description) { + CommandGroup g = new CommandGroup(description); + this.commandGroups.add(g); + return g; + } - public ArgumentCommand find(String... arguments) { - if (arguments.length > 0) { - for (CommandGroup g : this.commandGroups) { - ArgumentCommand c = g.find(arguments); - if (c != null) { - return c; - } - } - } - return null; + public ArgumentCommand find(String... arguments) { + if (arguments.length > 0) { + for (CommandGroup g : this.commandGroups) { + ArgumentCommand c = g.find(arguments); + if (c != null) { + return c; + } } + } + return null; } + } - static class CommandGroup { - protected final String description; - protected List argumentCommands = new ArrayList<>(); + static class CommandGroup { + protected final String description; + protected List argumentCommands = new ArrayList<>(); - CommandGroup(String description) { - this.description = description; - } + CommandGroup(String description) { + this.description = description; + } - public CommandGroup add(Command command, String... arguments) { - this.argumentCommands.add(new ArgumentCommand(arguments, command)); - return this; + public CommandGroup add(Command command, String... arguments) { + this.argumentCommands.add(new ArgumentCommand(arguments, command)); + return this; + } + + public ArgumentCommand find(String... arguments) { + ArgumentCommand matchedArgumentCommand = null; + int argMatchedSize = -1; + for (ArgumentCommand c : this.argumentCommands) { + boolean matched = true; + int matchedSize = 0; + for (int n = 0; n < c.arguments.length; n++) { + if (n > arguments.length || !c.arguments[n].equals(arguments[n])) { + matched = false; + break; + } else { + matchedSize++; + } } - public ArgumentCommand find(String... arguments) { - ArgumentCommand matchedArgumentCommand = null; - int argMatchedSize = -1; - for (ArgumentCommand c : this.argumentCommands) { - boolean matched = true; - int matchedSize = 0; - for (int n = 0; n < c.arguments.length; n++) { - if (n > arguments.length || !c.arguments[n].equals(arguments[n])) { - matched = false; - break; - } else { - matchedSize++; - } - } - - if (matched && matchedSize > argMatchedSize) { - matchedArgumentCommand = c; - argMatchedSize = matchedSize; - } - - } - return matchedArgumentCommand; + if (matched && matchedSize > argMatchedSize) { + matchedArgumentCommand = c; + argMatchedSize = matchedSize; } + } + return matchedArgumentCommand; } + } - static class ArgumentCommand { - protected final String[] arguments; - protected final Command command; + static class ArgumentCommand { + protected final String[] arguments; + protected final Command command; - ArgumentCommand(String[] arguments, Command command) { - this.arguments = arguments; - this.command = command; - } + ArgumentCommand(String[] arguments, Command command) { + this.arguments = arguments; + this.command = command; + } - public void run(String[] givenArguments) throws Exception { - if (givenArguments.length < this.arguments.length) { - throw new Exception("invalid arguments"); - } + public void run(String[] givenArguments) throws Exception { + if (givenArguments.length < this.arguments.length) { + throw new Exception("invalid arguments"); + } - this.command - .run((String[]) ArrayUtils.subarray(givenArguments, this.arguments.length, givenArguments.length)); - } + this.command.run( + (String[]) + ArrayUtils.subarray(givenArguments, this.arguments.length, givenArguments.length)); } + } + + private static final Commands commands = new Commands(); + + public static void main(String[] args) throws Exception { + + commands + .create("Local operations (no network communication)") + .add(new EncryptCommand(), "-e") + .add(new DecryptCommand(), "-d") + .add(new HashEmailCommand(), "-h", "-e") + .add(new HashPhoneCommand(), "-h", "-p") + .add(new GenerateKeyPairCommand(), "-g") + .add(new DerivePublicKeyCommand(), "-p"); + + commands + .create("Network operations") + .add(new SendSimpleMessageCommand(), "-s") + .add(new SendE2ETextMessageCommand(), "-S") + .add(new SendE2EImageMessageCommand(), "-S", "-i") + .add(new SendE2EFileMessageCommand(), "-S", "-f") + .add(new IDLookupByEmail(), "-l", "-e") + .add(new IDLookupByPhoneNo(), "-l", "-p") + .add(new FetchPublicKey(), "-l", "-k") + .add(new CapabilityCommand(), "-c") + .add(new DecryptAndDownloadCommand(), "-D") + .add(new CreditsCommand(), "-C"); + + ArgumentCommand argumentCommand = commands.find(args); + if (argumentCommand == null) { + usage(args.length == 1 && args[0].equals("html")); + } else { + argumentCommand.run(args); + } + } - private static final Commands commands = new Commands(); - - public static void main(String[] args) throws Exception { + private static void usage(boolean htmlOutput) { + if (!htmlOutput) { + System.out.println("version:" + ConsoleMain.class.getPackage().getImplementationVersion()); - commands.create("Local operations (no network communication)").add(new EncryptCommand(), "-e") - .add(new DecryptCommand(), "-d").add(new HashEmailCommand(), "-h", "-e") - .add(new HashPhoneCommand(), "-h", "-p").add(new GenerateKeyPairCommand(), "-g") - .add(new DerivePublicKeyCommand(), "-p"); + System.out.println("usage:\n"); - commands.create("Network operations").add(new SendSimpleMessageCommand(), "-s") - .add(new SendE2ETextMessageCommand(), "-S").add(new SendE2EImageMessageCommand(), "-S", "-i") - .add(new SendE2EFileMessageCommand(), "-S", "-f").add(new IDLookupByEmail(), "-l", "-e") - .add(new IDLookupByPhoneNo(), "-l", "-p").add(new FetchPublicKey(), "-l", "-k") - .add(new CapabilityCommand(), "-c").add(new DecryptAndDownloadCommand(), "-D") - .add(new CreditsCommand(), "-C"); + System.out.println("General information"); + System.out.println("-------------------\n"); - ArgumentCommand argumentCommand = commands.find(args); - if (argumentCommand == null) { - usage(args.length == 1 && args[0].equals("html")); - } else { - argumentCommand.run(args); - } + System.out.println("Where a key needs to be specified, it can either be given directly as"); + System.out.println("a command line parameter (in hex with a prefix indicating the type;"); + System.out.println("not recommended on shared machines as other users may be able to see"); + System.out.println("the arguments), or as the path to a file that it should be read from"); + System.out.println("(file contents also in hex with the prefix).\n"); } - private static void usage(boolean htmlOutput) { - if (!htmlOutput) { - System.out.println("version:" + ConsoleMain.class.getPackage().getImplementationVersion()); + String groupDescriptionTemplate = + htmlOutput ? "

%s

\n" : "\n%s\n" + StringUtils.repeat("-", 80) + "\n\n"; + String commandTemplate = + htmlOutput ? "
java -jar threema-msgapi-tool.jar %s
\n" : "%s\n"; - System.out.println("usage:\n"); + for (CommandGroup commandGroup : commands.commandGroups) { + System.out.format(groupDescriptionTemplate, commandGroup.description); - System.out.println("General information"); - System.out.println("-------------------\n"); - - System.out.println("Where a key needs to be specified, it can either be given directly as"); - System.out.println("a command line parameter (in hex with a prefix indicating the type;"); - System.out.println("not recommended on shared machines as other users may be able to see"); - System.out.println("the arguments), or as the path to a file that it should be read from"); - System.out.println("(file contents also in hex with the prefix).\n"); + for (ArgumentCommand argumentCommand : commandGroup.argumentCommands) { + StringBuilder command = new StringBuilder(); + for (int n = 0; n < argumentCommand.arguments.length; n++) { + command.append(argumentCommand.arguments[n]).append(" "); + } + String argumentDescription = argumentCommand.command.getUsageArguments(); + if (htmlOutput) { + System.out.format("

%s

\n", argumentCommand.command.getSubject()); + argumentDescription = StringEscapeUtils.escapeHtml3(argumentDescription); } + command.append(argumentDescription); - String groupDescriptionTemplate = htmlOutput ? "

%s

\n" - : "\n%s\n" + StringUtils.repeat("-", 80) + "\n\n"; - String commandTemplate = htmlOutput ? "
java -jar threema-msgapi-tool.jar %s
\n" : "%s\n"; - - for (CommandGroup commandGroup : commands.commandGroups) { - System.out.format(groupDescriptionTemplate, commandGroup.description); - - for (ArgumentCommand argumentCommand : commandGroup.argumentCommands) { - StringBuilder command = new StringBuilder(); - for (int n = 0; n < argumentCommand.arguments.length; n++) { - command.append(argumentCommand.arguments[n]).append(" "); - } - String argumentDescription = argumentCommand.command.getUsageArguments(); - if (htmlOutput) { - System.out.format("

%s

\n", argumentCommand.command.getSubject()); - argumentDescription = StringEscapeUtils.escapeHtml3(argumentDescription); - } - command.append(argumentDescription); - - System.out.format(commandTemplate, command.toString().trim()); - - String description = argumentCommand.command.getUsageDescription(); - if (htmlOutput) { - System.out.format("

%s

\n\n", description); - } else { - System.out.println(" " + WordUtils.wrap(description, 76, "\n ", false)); - System.out.println(""); - } - } + System.out.format(commandTemplate, command.toString().trim()); + + String description = argumentCommand.command.getUsageDescription(); + if (htmlOutput) { + System.out.format("

%s

\n\n", description); + } else { + System.out.println(" " + WordUtils.wrap(description, 76, "\n ", false)); + System.out.println(""); } + } } + } } diff --git a/source/src/main/java/ch/threema/apitool/CryptTool.java b/source/src/main/java/ch/threema/apitool/CryptTool.java index 688a798..099b23d 100644 --- a/source/src/main/java/ch/threema/apitool/CryptTool.java +++ b/source/src/main/java/ch/threema/apitool/CryptTool.java @@ -41,340 +41,423 @@ import java.util.LinkedList; import java.util.List; -/** - * Contains static methods to do various Threema cryptography related tasks. - */ +/** Contains static methods to do various Threema cryptography related tasks. */ public class CryptTool { - /* HMAC-SHA256 keys for email/mobile phone hashing */ - private static final byte[] EMAIL_HMAC_KEY = new byte[] {(byte)0x30,(byte)0xa5,(byte)0x50,(byte)0x0f,(byte)0xed,(byte)0x97,(byte)0x01,(byte)0xfa,(byte)0x6d,(byte)0xef,(byte)0xdb,(byte)0x61,(byte)0x08,(byte)0x41,(byte)0x90,(byte)0x0f,(byte)0xeb,(byte)0xb8,(byte)0xe4,(byte)0x30,(byte)0x88,(byte)0x1f,(byte)0x7a,(byte)0xd8,(byte)0x16,(byte)0x82,(byte)0x62,(byte)0x64,(byte)0xec,(byte)0x09,(byte)0xba,(byte)0xd7}; - private static final byte[] PHONENO_HMAC_KEY = new byte[] {(byte)0x85,(byte)0xad,(byte)0xf8,(byte)0x22,(byte)0x69,(byte)0x53,(byte)0xf3,(byte)0xd9,(byte)0x6c,(byte)0xfd,(byte)0x5d,(byte)0x09,(byte)0xbf,(byte)0x29,(byte)0x55,(byte)0x5e,(byte)0xb9,(byte)0x55,(byte)0xfc,(byte)0xd8,(byte)0xaa,(byte)0x5e,(byte)0xc4,(byte)0xf9,(byte)0xfc,(byte)0xd8,(byte)0x69,(byte)0xe2,(byte)0x58,(byte)0x37,(byte)0x07,(byte)0x23}; - - private static final byte[] FILE_NONCE = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}; - private static final byte[] FILE_THUMBNAIL_NONCE = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}; - - private static final SecureRandom random = new SecureRandom(); - - /** - * Encrypt a text message. - * - * @param text the text to be encrypted (max. 3500 bytes) - * @param senderPrivateKey the private key of the sending ID - * @param recipientPublicKey the public key of the receiving ID - * @return encrypted result - */ - public static EncryptResult encryptTextMessage(String text, byte[] senderPrivateKey, byte[] recipientPublicKey) { - return encryptMessage(new TextMessage(text), senderPrivateKey, recipientPublicKey); - } - - - /** - * Encrypt an image message. - * - * @param encryptResult result of the image encryption - * @param uploadResult result of the upload - * @param senderPrivateKey the private key of the sending ID - * @param recipientPublicKey the public key of the receiving ID - * @return encrypted result - */ - public static EncryptResult encryptImageMessage(EncryptResult encryptResult, UploadResult uploadResult, byte[] senderPrivateKey, byte[] recipientPublicKey) { - return encryptMessage( - new ImageMessage(uploadResult.getBlobId(), - encryptResult.getSize(), - encryptResult.getNonce()), - senderPrivateKey, - recipientPublicKey); - } - - /** - * Encrypt a file message. - * - * @param encryptResult result of the file data encryption - * @param uploadResult result of the upload - * @param mimeType MIME type of the file - * @param fileName File name - * @param fileSize Size of the file, in bytes - * @param uploadResultThumbnail result of thumbnail upload - * @param senderPrivateKey Private key of sender - * @param recipientPublicKey Public key of recipient - * @return Result of the file message encryption (not the same as the file data encryption!) - */ - public static EncryptResult encryptFileMessage(EncryptResult encryptResult, - UploadResult uploadResult, - String mimeType, - String fileName, - int fileSize, - UploadResult uploadResultThumbnail, - byte[] senderPrivateKey, byte[] recipientPublicKey) { - return encryptMessage( - new FileMessage(uploadResult.getBlobId(), - encryptResult.getSecret(), - mimeType, - fileName, - fileSize, - uploadResultThumbnail != null ? uploadResultThumbnail.getBlobId() : null), - senderPrivateKey, - recipientPublicKey); - } - - - private static EncryptResult encryptMessage(ThreemaMessage threemaMessage, byte[] privateKey, byte[] publicKey) { - /* determine random amount of PKCS7 padding */ - int padbytes = random.nextInt(254) + 1; - - byte[] messageBytes; - try { - messageBytes = threemaMessage.getData(); - } catch (BadMessageException e) { - return null; - } - - /* prepend type byte (0x02) to message data */ - byte[] data = new byte[1 + messageBytes.length + padbytes]; - data[0] = (byte)threemaMessage.getTypeCode(); - - System.arraycopy(messageBytes, 0, data, 1, messageBytes.length); - - /* append padding */ - for (int i = 0; i < padbytes; i++) { - data[i + 1 + messageBytes.length] = (byte)padbytes; - } - - return encrypt(data, privateKey, publicKey); - } - - /** - * Decrypt an NaCl box using the recipient's private key and the sender's public key. - * - * @param box The box to be decrypted - * @param privateKey The private key of the recipient - * @param publicKey The public key of the sender - * @param nonce The nonce that was used for encryption - * @return The decrypted data, or null if decryption failed - */ - public static byte[] decrypt(byte[] box, byte[] privateKey, byte[] publicKey, byte[] nonce) { - return new NaCl(privateKey, publicKey).decrypt(box, nonce); - } - - /** - * Decrypt symmetrically encrypted file data. - * - * @param fileData The encrypted file data - * @param secret The symmetric key that was used for encryption - * @return The decrypted file data, or null if decryption failed - */ - public static byte[] decryptFileData(byte[] fileData, byte[] secret) { - return NaCl.symmetricDecryptData(fileData, secret, FILE_NONCE); - } - - /** - * Decrypt symmetrically encrypted file thumbnail data. - * - * @param fileData The encrypted thumbnail data - * @param secret The symmetric key that was used for encryption - * @return The decrypted thumbnail data, or null if decryption failed - */ - public static byte[] decryptFileThumbnailData(byte[] fileData, byte[] secret) { - return NaCl.symmetricDecryptData(fileData, secret, FILE_THUMBNAIL_NONCE); - } - - /** - * Decrypt a message. - * - * @param box the box to be decrypted - * @param recipientPrivateKey the private key of the receiving ID - * @param senderPublicKey the public key of the sending ID - * @param nonce the nonce that was used for the encryption - * @return decrypted message (text or delivery receipt) - */ - public static ThreemaMessage decryptMessage(byte[] box, byte[] recipientPrivateKey, byte[] senderPublicKey, byte[] nonce) throws MessageParseException { - - byte[] data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce); - if (data == null) - throw new DecryptionFailedException(); - - /* remove padding */ - int padbytes = data[data.length-1] & 0xFF; - int realDataLength = data.length - padbytes; - if (realDataLength < 1) - throw new BadMessageException(); /* Bad message padding */ - - /* first byte of data is type */ - int type = data[0] & 0xFF; - - switch (type) { - case TextMessage.TYPE_CODE: - /* Text message */ - if (realDataLength < 2) - throw new BadMessageException(); - - try { - return new TextMessage(new String(data, 1, realDataLength - 1, "UTF-8")); - } catch (UnsupportedEncodingException e) { - /* should never happen, UTF-8 is always supported */ - throw new RuntimeException(e); - } - - case DeliveryReceipt.TYPE_CODE: - /* Delivery receipt */ - if (realDataLength < MessageId.MESSAGE_ID_LEN + 2 || ((realDataLength - 2) % MessageId.MESSAGE_ID_LEN) != 0) - throw new BadMessageException(); - - DeliveryReceipt.Type receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF); - if (receiptType == null) - throw new BadMessageException(); - - List messageIds = new LinkedList(); - - int numMsgIds = ((realDataLength - 2) / MessageId.MESSAGE_ID_LEN); - for (int i = 0; i < numMsgIds; i++) { - messageIds.add(new MessageId(data, 2 + i*MessageId.MESSAGE_ID_LEN)); - } - - return new DeliveryReceipt(receiptType, messageIds); - - case ImageMessage.TYPE_CODE: - if(realDataLength != (1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)) { - System.out.println(String.valueOf(realDataLength)); - System.out.println(String.valueOf(1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)); - throw new BadMessageException(); - } - byte[] blobId = new byte[ThreemaMessage.BLOB_ID_LEN]; - System.arraycopy(data, 1, blobId, 0, ThreemaMessage.BLOB_ID_LEN); - int size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN); - byte[] fileNonce = new byte[NaCl.NONCEBYTES]; - System.arraycopy(data, 1 + 4 + ThreemaMessage.BLOB_ID_LEN, fileNonce, 0, nonce.length); - - return new ImageMessage(blobId, size, fileNonce); - - case FileMessage.TYPE_CODE: - try { - return FileMessage.fromString(new String(data, 1, realDataLength-1, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new BadMessageException(); - } - - default: - throw new UnsupportedMessageTypeException(); - } - } - - /** - * Generate a new key pair. - * - * @param privateKey is used to return the generated private key (length must be NaCl.PRIVATEKEYBYTES) - * @param publicKey is used to return the generated public key (length must be NaCl.PUBLICKEYBYTES) - */ - public static void generateKeyPair(byte[] privateKey, byte[] publicKey) { - if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) { - throw new IllegalArgumentException("Wrong key length"); - } - - NaCl.genkeypair(publicKey, privateKey); - } - - /** - * Encrypt data using NaCl asymmetric ("box") encryption. - * - * @param data the data to be encrypted - * @param privateKey is used to return the generated private key (length must be NaCl.PRIVATEKEYBYTES) - * @param publicKey is used to return the generated public key (length must be NaCl.PUBLICKEYBYTES) - */ - public static EncryptResult encrypt(byte[] data,byte[] privateKey, byte[] publicKey) { - if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) { - throw new IllegalArgumentException("Wrong key length"); - } - - byte[] nonce = randomNonce(); - NaCl naCl = new NaCl(privateKey, publicKey); - return new EncryptResult(naCl.encrypt(data, nonce), null, nonce); - } - - /** - * Encrypt file data using NaCl symmetric encryption with a random key. - * - * @param data the file contents to be encrypted - * @return the encryption result including the random key - */ - public static EncryptResult encryptFileData(byte[] data) { - //create random key - SecureRandom rnd = new SecureRandom(); - byte[] encryptionKey = new byte[NaCl.SYMMKEYBYTES]; - rnd.nextBytes(encryptionKey); - - //encrypt file data in-place - NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_NONCE); - - return new EncryptResult(data, encryptionKey, FILE_NONCE); - } - - /** - * Encrypt file thumbnail data using NaCl symmetric encryption with a random key. - * - * @param data the file contents to be encrypted - * @return the encryption result including the random key - */ - public static EncryptResult encryptFileThumbnailData(byte[] data, byte[] encryptionKey) { - // encrypt file data in-place - NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_THUMBNAIL_NONCE); - - return new EncryptResult(data, encryptionKey, FILE_THUMBNAIL_NONCE); - } - - /** - * Hashes an email address for identity lookup. - * - * @param email the email address - * @return the raw hash - */ - public static byte[] hashEmail(String email) { - try { - Mac emailMac = Mac.getInstance("HmacSHA256"); - emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, "HmacSHA256")); - String normalizedEmail = email.toLowerCase().trim(); - return emailMac.doFinal(normalizedEmail.getBytes("US-ASCII")); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * Hashes a phone number for identity lookup. - * - * @param phoneNo the phone number - * @return the raw hash - */ - public static byte[] hashPhoneNo(String phoneNo) { - try { - Mac phoneMac = Mac.getInstance("HmacSHA256"); - phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, "HmacSHA256")); - String normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", ""); - return phoneMac.doFinal(normalizedPhoneNo.getBytes("US-ASCII")); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * Generate a random nonce. - * - * @return random nonce - */ - public static byte[] randomNonce() { - byte[] nonce = new byte[NaCl.NONCEBYTES]; - random.nextBytes(nonce); - return nonce; - } - - /** - * Return the public key that corresponds with a given private key. - * - * @param privateKey The private key whose public key should be derived - * @return The corresponding public key. - */ - public static byte[] derivePublicKey(byte[] privateKey) { - return NaCl.derivePublicKey(privateKey); - } + /* HMAC-SHA256 keys for email/mobile phone hashing */ + private static final byte[] EMAIL_HMAC_KEY = + new byte[] { + (byte) 0x30, + (byte) 0xa5, + (byte) 0x50, + (byte) 0x0f, + (byte) 0xed, + (byte) 0x97, + (byte) 0x01, + (byte) 0xfa, + (byte) 0x6d, + (byte) 0xef, + (byte) 0xdb, + (byte) 0x61, + (byte) 0x08, + (byte) 0x41, + (byte) 0x90, + (byte) 0x0f, + (byte) 0xeb, + (byte) 0xb8, + (byte) 0xe4, + (byte) 0x30, + (byte) 0x88, + (byte) 0x1f, + (byte) 0x7a, + (byte) 0xd8, + (byte) 0x16, + (byte) 0x82, + (byte) 0x62, + (byte) 0x64, + (byte) 0xec, + (byte) 0x09, + (byte) 0xba, + (byte) 0xd7 + }; + private static final byte[] PHONENO_HMAC_KEY = + new byte[] { + (byte) 0x85, + (byte) 0xad, + (byte) 0xf8, + (byte) 0x22, + (byte) 0x69, + (byte) 0x53, + (byte) 0xf3, + (byte) 0xd9, + (byte) 0x6c, + (byte) 0xfd, + (byte) 0x5d, + (byte) 0x09, + (byte) 0xbf, + (byte) 0x29, + (byte) 0x55, + (byte) 0x5e, + (byte) 0xb9, + (byte) 0x55, + (byte) 0xfc, + (byte) 0xd8, + (byte) 0xaa, + (byte) 0x5e, + (byte) 0xc4, + (byte) 0xf9, + (byte) 0xfc, + (byte) 0xd8, + (byte) 0x69, + (byte) 0xe2, + (byte) 0x58, + (byte) 0x37, + (byte) 0x07, + (byte) 0x23 + }; + + private static final byte[] FILE_NONCE = + new byte[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + private static final byte[] FILE_THUMBNAIL_NONCE = + new byte[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + + private static final SecureRandom random = new SecureRandom(); + + /** + * Encrypt a text message. + * + * @param text the text to be encrypted (max. 3500 bytes) + * @param senderPrivateKey the private key of the sending ID + * @param recipientPublicKey the public key of the receiving ID + * @return encrypted result + */ + public static EncryptResult encryptTextMessage( + String text, byte[] senderPrivateKey, byte[] recipientPublicKey) { + return encryptMessage(new TextMessage(text), senderPrivateKey, recipientPublicKey); + } + + /** + * Encrypt an image message. + * + * @param encryptResult result of the image encryption + * @param uploadResult result of the upload + * @param senderPrivateKey the private key of the sending ID + * @param recipientPublicKey the public key of the receiving ID + * @return encrypted result + */ + public static EncryptResult encryptImageMessage( + EncryptResult encryptResult, + UploadResult uploadResult, + byte[] senderPrivateKey, + byte[] recipientPublicKey) { + return encryptMessage( + new ImageMessage( + uploadResult.getBlobId(), encryptResult.getSize(), encryptResult.getNonce()), + senderPrivateKey, + recipientPublicKey); + } + + /** + * Encrypt a file message. + * + * @param encryptResult result of the file data encryption + * @param uploadResult result of the upload + * @param mimeType MIME type of the file + * @param fileName File name + * @param fileSize Size of the file, in bytes + * @param uploadResultThumbnail result of thumbnail upload + * @param senderPrivateKey Private key of sender + * @param recipientPublicKey Public key of recipient + * @return Result of the file message encryption (not the same as the file data encryption!) + */ + public static EncryptResult encryptFileMessage( + EncryptResult encryptResult, + UploadResult uploadResult, + String mimeType, + String fileName, + int fileSize, + UploadResult uploadResultThumbnail, + byte[] senderPrivateKey, + byte[] recipientPublicKey) { + return encryptMessage( + new FileMessage( + uploadResult.getBlobId(), + encryptResult.getSecret(), + mimeType, + fileName, + fileSize, + uploadResultThumbnail != null ? uploadResultThumbnail.getBlobId() : null), + senderPrivateKey, + recipientPublicKey); + } + + private static EncryptResult encryptMessage( + ThreemaMessage threemaMessage, byte[] privateKey, byte[] publicKey) { + /* determine random amount of PKCS7 padding */ + int padbytes = random.nextInt(254) + 1; + + byte[] messageBytes; + try { + messageBytes = threemaMessage.getData(); + } catch (BadMessageException e) { + return null; + } + + /* prepend type byte (0x02) to message data */ + byte[] data = new byte[1 + messageBytes.length + padbytes]; + data[0] = (byte) threemaMessage.getTypeCode(); + + System.arraycopy(messageBytes, 0, data, 1, messageBytes.length); + + /* append padding */ + for (int i = 0; i < padbytes; i++) { + data[i + 1 + messageBytes.length] = (byte) padbytes; + } + + return encrypt(data, privateKey, publicKey); + } + + /** + * Decrypt an NaCl box using the recipient's private key and the sender's public key. + * + * @param box The box to be decrypted + * @param privateKey The private key of the recipient + * @param publicKey The public key of the sender + * @param nonce The nonce that was used for encryption + * @return The decrypted data, or null if decryption failed + */ + public static byte[] decrypt(byte[] box, byte[] privateKey, byte[] publicKey, byte[] nonce) { + return new NaCl(privateKey, publicKey).decrypt(box, nonce); + } + + /** + * Decrypt symmetrically encrypted file data. + * + * @param fileData The encrypted file data + * @param secret The symmetric key that was used for encryption + * @return The decrypted file data, or null if decryption failed + */ + public static byte[] decryptFileData(byte[] fileData, byte[] secret) { + return NaCl.symmetricDecryptData(fileData, secret, FILE_NONCE); + } + + /** + * Decrypt symmetrically encrypted file thumbnail data. + * + * @param fileData The encrypted thumbnail data + * @param secret The symmetric key that was used for encryption + * @return The decrypted thumbnail data, or null if decryption failed + */ + public static byte[] decryptFileThumbnailData(byte[] fileData, byte[] secret) { + return NaCl.symmetricDecryptData(fileData, secret, FILE_THUMBNAIL_NONCE); + } + + /** + * Decrypt a message. + * + * @param box the box to be decrypted + * @param recipientPrivateKey the private key of the receiving ID + * @param senderPublicKey the public key of the sending ID + * @param nonce the nonce that was used for the encryption + * @return decrypted message (text or delivery receipt) + */ + public static ThreemaMessage decryptMessage( + byte[] box, byte[] recipientPrivateKey, byte[] senderPublicKey, byte[] nonce) + throws MessageParseException { + + byte[] data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce); + if (data == null) throw new DecryptionFailedException(); + + /* remove padding */ + int padbytes = data[data.length - 1] & 0xFF; + int realDataLength = data.length - padbytes; + if (realDataLength < 1) throw new BadMessageException(); /* Bad message padding */ + + /* first byte of data is type */ + int type = data[0] & 0xFF; + + switch (type) { + case TextMessage.TYPE_CODE: + /* Text message */ + if (realDataLength < 2) throw new BadMessageException(); + + try { + return new TextMessage(new String(data, 1, realDataLength - 1, "UTF-8")); + } catch (UnsupportedEncodingException e) { + /* should never happen, UTF-8 is always supported */ + throw new RuntimeException(e); + } + + case DeliveryReceipt.TYPE_CODE: + /* Delivery receipt */ + if (realDataLength < MessageId.MESSAGE_ID_LEN + 2 + || ((realDataLength - 2) % MessageId.MESSAGE_ID_LEN) != 0) + throw new BadMessageException(); + + DeliveryReceipt.Type receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF); + if (receiptType == null) throw new BadMessageException(); + + List messageIds = new LinkedList(); + + int numMsgIds = ((realDataLength - 2) / MessageId.MESSAGE_ID_LEN); + for (int i = 0; i < numMsgIds; i++) { + messageIds.add(new MessageId(data, 2 + i * MessageId.MESSAGE_ID_LEN)); + } + + return new DeliveryReceipt(receiptType, messageIds); + + case ImageMessage.TYPE_CODE: + if (realDataLength != (1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)) { + System.out.println(String.valueOf(realDataLength)); + System.out.println(String.valueOf(1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)); + throw new BadMessageException(); + } + byte[] blobId = new byte[ThreemaMessage.BLOB_ID_LEN]; + System.arraycopy(data, 1, blobId, 0, ThreemaMessage.BLOB_ID_LEN); + int size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN); + byte[] fileNonce = new byte[NaCl.NONCEBYTES]; + System.arraycopy(data, 1 + 4 + ThreemaMessage.BLOB_ID_LEN, fileNonce, 0, nonce.length); + + return new ImageMessage(blobId, size, fileNonce); + + case FileMessage.TYPE_CODE: + try { + return FileMessage.fromString(new String(data, 1, realDataLength - 1, "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new BadMessageException(); + } + + default: + throw new UnsupportedMessageTypeException(); + } + } + + /** + * Generate a new key pair. + * + * @param privateKey is used to return the generated private key (length must be + * NaCl.PRIVATEKEYBYTES) + * @param publicKey is used to return the generated public key (length must be + * NaCl.PUBLICKEYBYTES) + */ + public static void generateKeyPair(byte[] privateKey, byte[] publicKey) { + if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) { + throw new IllegalArgumentException("Wrong key length"); + } + + NaCl.genkeypair(publicKey, privateKey); + } + + /** + * Encrypt data using NaCl asymmetric ("box") encryption. + * + * @param data the data to be encrypted + * @param privateKey is used to return the generated private key (length must be + * NaCl.PRIVATEKEYBYTES) + * @param publicKey is used to return the generated public key (length must be + * NaCl.PUBLICKEYBYTES) + */ + public static EncryptResult encrypt(byte[] data, byte[] privateKey, byte[] publicKey) { + if (publicKey.length != NaCl.PUBLICKEYBYTES || privateKey.length != NaCl.SECRETKEYBYTES) { + throw new IllegalArgumentException("Wrong key length"); + } + + byte[] nonce = randomNonce(); + NaCl naCl = new NaCl(privateKey, publicKey); + return new EncryptResult(naCl.encrypt(data, nonce), null, nonce); + } + + /** + * Encrypt file data using NaCl symmetric encryption with a random key. + * + * @param data the file contents to be encrypted + * @return the encryption result including the random key + */ + public static EncryptResult encryptFileData(byte[] data) { + // create random key + SecureRandom rnd = new SecureRandom(); + byte[] encryptionKey = new byte[NaCl.SYMMKEYBYTES]; + rnd.nextBytes(encryptionKey); + + // encrypt file data in-place + NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_NONCE); + + return new EncryptResult(data, encryptionKey, FILE_NONCE); + } + + /** + * Encrypt file thumbnail data using NaCl symmetric encryption with a random key. + * + * @param data the file contents to be encrypted + * @return the encryption result including the random key + */ + public static EncryptResult encryptFileThumbnailData(byte[] data, byte[] encryptionKey) { + // encrypt file data in-place + NaCl.symmetricEncryptDataInplace(data, encryptionKey, FILE_THUMBNAIL_NONCE); + + return new EncryptResult(data, encryptionKey, FILE_THUMBNAIL_NONCE); + } + + /** + * Hashes an email address for identity lookup. + * + * @param email the email address + * @return the raw hash + */ + public static byte[] hashEmail(String email) { + try { + Mac emailMac = Mac.getInstance("HmacSHA256"); + emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, "HmacSHA256")); + String normalizedEmail = email.toLowerCase().trim(); + return emailMac.doFinal(normalizedEmail.getBytes("US-ASCII")); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * Hashes a phone number for identity lookup. + * + * @param phoneNo the phone number + * @return the raw hash + */ + public static byte[] hashPhoneNo(String phoneNo) { + try { + Mac phoneMac = Mac.getInstance("HmacSHA256"); + phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, "HmacSHA256")); + String normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", ""); + return phoneMac.doFinal(normalizedPhoneNo.getBytes("US-ASCII")); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * Generate a random nonce. + * + * @return random nonce + */ + public static byte[] randomNonce() { + byte[] nonce = new byte[NaCl.NONCEBYTES]; + random.nextBytes(nonce); + return nonce; + } + + /** + * Return the public key that corresponds with a given private key. + * + * @param privateKey The private key whose public key should be derived + * @return The corresponding public key. + */ + public static byte[] derivePublicKey(byte[] privateKey) { + return NaCl.derivePublicKey(privateKey); + } } diff --git a/source/src/main/java/ch/threema/apitool/DataUtils.java b/source/src/main/java/ch/threema/apitool/DataUtils.java index f9ffa68..351f473 100644 --- a/source/src/main/java/ch/threema/apitool/DataUtils.java +++ b/source/src/main/java/ch/threema/apitool/DataUtils.java @@ -30,108 +30,110 @@ public class DataUtils { - /** - * Convert a string in hexadecimal representation to a byte array. - * - * @param s hex string - * @return decoded byte array - */ - public static byte[] hexStringToByteArray(String s) { - String sc = s.replaceAll("[^0-9a-fA-F]", ""); - int len = sc.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(sc.charAt(i), 16) << 4) - + Character.digit(sc.charAt(i+1), 16)); - } - return data; - } + /** + * Convert a string in hexadecimal representation to a byte array. + * + * @param s hex string + * @return decoded byte array + */ + public static byte[] hexStringToByteArray(String s) { + String sc = s.replaceAll("[^0-9a-fA-F]", ""); + int len = sc.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = + (byte) ((Character.digit(sc.charAt(i), 16) << 4) + Character.digit(sc.charAt(i + 1), 16)); + } + return data; + } - /** - * Convert a byte array into a hexadecimal string (lowercase). - * - * @param bytes the bytes to encode - * @return hex encoded string - */ - public static String byteArrayToHexString(byte[] bytes) { - final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - char[] hexChars = new char[bytes.length * 2]; - int v; - for (int j = 0; j < bytes.length; j++) { - v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } + /** + * Convert a byte array into a hexadecimal string (lowercase). + * + * @param bytes the bytes to encode + * @return hex encoded string + */ + public static String byteArrayToHexString(byte[] bytes) { + final char[] hexArray = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + char[] hexChars = new char[bytes.length * 2]; + int v; + for (int j = 0; j < bytes.length; j++) { + v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } - /** - * Read hexadecimal data from a file and return it as a byte array. - * - * @param inFile input file - * @return the decoded data - * @throws java.io.IOException - */ - public static byte[] readHexFile(File inFile) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - byte[] data = hexStringToByteArray(br.readLine().trim()); - br.close(); - return data; - } + /** + * Read hexadecimal data from a file and return it as a byte array. + * + * @param inFile input file + * @return the decoded data + * @throws java.io.IOException + */ + public static byte[] readHexFile(File inFile) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(inFile)); + byte[] data = hexStringToByteArray(br.readLine().trim()); + br.close(); + return data; + } - /** - * Write a byte array into a file in hexadecimal format. - * - * @param outFile output file - * @param data the data to be written - */ - public static void writeHexFile(File outFile, byte[] data) throws IOException { - FileWriter fw = new FileWriter(outFile); - fw.write(byteArrayToHexString(data)); - fw.write('\n'); - fw.close(); - } + /** + * Write a byte array into a file in hexadecimal format. + * + * @param outFile output file + * @param data the data to be written + */ + public static void writeHexFile(File outFile, byte[] data) throws IOException { + FileWriter fw = new FileWriter(outFile); + fw.write(byteArrayToHexString(data)); + fw.write('\n'); + fw.close(); + } - /** - * Read an encoded key from a file and return it as a key instance. - * - * @param inFile input file - * @return the decoded key - * @throws java.io.IOException - */ - public static Key readKeyFile(File inFile) throws IOException, InvalidKeyException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - String encodedKey = br.readLine().trim(); - br.close(); - return Key.decodeKey(encodedKey); - } + /** + * Read an encoded key from a file and return it as a key instance. + * + * @param inFile input file + * @return the decoded key + * @throws java.io.IOException + */ + public static Key readKeyFile(File inFile) throws IOException, InvalidKeyException { + BufferedReader br = new BufferedReader(new FileReader(inFile)); + String encodedKey = br.readLine().trim(); + br.close(); + return Key.decodeKey(encodedKey); + } - /** - * Read an encoded key from a file and return it as a key instance. - * - * @param inFile input file - * @param expectedKeyType validates the key type (private or public) - * @return the decoded key - * @throws java.io.IOException - */ - public static Key readKeyFile(File inFile, String expectedKeyType) throws IOException, InvalidKeyException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - String encodedKey = br.readLine().trim(); - br.close(); - return Key.decodeKey(encodedKey, expectedKeyType); - } + /** + * Read an encoded key from a file and return it as a key instance. + * + * @param inFile input file + * @param expectedKeyType validates the key type (private or public) + * @return the decoded key + * @throws java.io.IOException + */ + public static Key readKeyFile(File inFile, String expectedKeyType) + throws IOException, InvalidKeyException { + BufferedReader br = new BufferedReader(new FileReader(inFile)); + String encodedKey = br.readLine().trim(); + br.close(); + return Key.decodeKey(encodedKey, expectedKeyType); + } - /** - * Write an encoded key to a file - * Encoded key format: type:hex_key. - * - * @param outFile output file - * @param key a key that will be encoded and written to a file - */ - public static void writeKeyFile(File outFile, Key key) throws IOException { - FileWriter fw = new FileWriter(outFile); - fw.write(key.encode()); - fw.write('\n'); - fw.close(); - } + /** + * Write an encoded key to a file Encoded key format: type:hex_key. + * + * @param outFile output file + * @param key a key that will be encoded and written to a file + */ + public static void writeKeyFile(File outFile, Key key) throws IOException { + FileWriter fw = new FileWriter(outFile); + fw.write(key.encode()); + fw.write('\n'); + fw.close(); + } } diff --git a/source/src/main/java/ch/threema/apitool/Key.java b/source/src/main/java/ch/threema/apitool/Key.java index d88cd79..1012263 100644 --- a/source/src/main/java/ch/threema/apitool/Key.java +++ b/source/src/main/java/ch/threema/apitool/Key.java @@ -26,77 +26,74 @@ import ch.threema.apitool.exceptions.InvalidKeyException; -/** - * Encapsulates an asymmetric key, either public or private. - */ +/** Encapsulates an asymmetric key, either public or private. */ public class Key { - public static final String separator = ":"; - - public static class KeyType { - public static final String PRIVATE = "private"; - public static final String PUBLIC = "public"; - } + public static final String separator = ":"; - /* Attributes */ - public byte[] key; - public String type; + public static class KeyType { + public static final String PRIVATE = "private"; + public static final String PUBLIC = "public"; + } - public Key(String type, byte[] key) { - this.key = key; - this.type = type; - } + /* Attributes */ + public byte[] key; + public String type; - /** - * Decodes and validates an encoded key. - * Encoded key format: type:hex_key - * - * @param encodedKey an encoded key - * @throws ch.threema.apitool.exceptions.InvalidKeyException - */ - public static Key decodeKey(String encodedKey) throws InvalidKeyException { - // Split key and check length - String[] keyArray = encodedKey.split(Key.separator); - if (keyArray.length != 2) { - throw new InvalidKeyException("Does not contain a valid key format"); - } + public Key(String type, byte[] key) { + this.key = key; + this.type = type; + } - // Unpack key - String keyType = keyArray[0]; - String keyContent = keyArray[1]; + /** + * Decodes and validates an encoded key. Encoded key format: type:hex_key + * + * @param encodedKey an encoded key + * @throws ch.threema.apitool.exceptions.InvalidKeyException + */ + public static Key decodeKey(String encodedKey) throws InvalidKeyException { + // Split key and check length + String[] keyArray = encodedKey.split(Key.separator); + if (keyArray.length != 2) { + throw new InvalidKeyException("Does not contain a valid key format"); + } - // Is this a valid hex key? - if (!keyContent.matches("[0-9a-fA-F]{64}")) { - throw new InvalidKeyException("Does not contain a valid key"); - } + // Unpack key + String keyType = keyArray[0]; + String keyContent = keyArray[1]; - return new Key(keyType, DataUtils.hexStringToByteArray(keyContent)); + // Is this a valid hex key? + if (!keyContent.matches("[0-9a-fA-F]{64}")) { + throw new InvalidKeyException("Does not contain a valid key"); } - /** - * Decodes and validates an encoded key. - * Encoded key format: type:hex_key - * - * @param encodedKey an encoded key - * @param expectedKeyType the expected type of the key - * @throws InvalidKeyException - */ - public static Key decodeKey(String encodedKey, String expectedKeyType) throws InvalidKeyException { - Key key = decodeKey(encodedKey); + return new Key(keyType, DataUtils.hexStringToByteArray(keyContent)); + } - // Check key type - if (!key.type.equals(expectedKeyType)) { - throw new InvalidKeyException("Expected key type: " + expectedKeyType + ", got: " + key.type); - } + /** + * Decodes and validates an encoded key. Encoded key format: type:hex_key + * + * @param encodedKey an encoded key + * @param expectedKeyType the expected type of the key + * @throws InvalidKeyException + */ + public static Key decodeKey(String encodedKey, String expectedKeyType) + throws InvalidKeyException { + Key key = decodeKey(encodedKey); - return key; + // Check key type + if (!key.type.equals(expectedKeyType)) { + throw new InvalidKeyException("Expected key type: " + expectedKeyType + ", got: " + key.type); } - /** - * Encodes a key. - * - * @return an encoded key - */ - public String encode() { - return this.type + Key.separator + DataUtils.byteArrayToHexString(this.key); - } + return key; + } + + /** + * Encodes a key. + * + * @return an encoded key + */ + public String encode() { + return this.type + Key.separator + DataUtils.byteArrayToHexString(this.key); + } } diff --git a/source/src/main/java/ch/threema/apitool/MessageId.java b/source/src/main/java/ch/threema/apitool/MessageId.java index 2c3d5b1..e5f96fa 100644 --- a/source/src/main/java/ch/threema/apitool/MessageId.java +++ b/source/src/main/java/ch/threema/apitool/MessageId.java @@ -24,36 +24,34 @@ package ch.threema.apitool; -/** - * Encapsulates the 8-byte message IDs that Threema uses. - */ +/** Encapsulates the 8-byte message IDs that Threema uses. */ public class MessageId { - public static final int MESSAGE_ID_LEN = 8; + public static final int MESSAGE_ID_LEN = 8; - private final byte[] messageId; + private final byte[] messageId; - public MessageId(byte[] messageId) { - if (messageId.length != MESSAGE_ID_LEN) - throw new IllegalArgumentException("Bad message ID length"); + public MessageId(byte[] messageId) { + if (messageId.length != MESSAGE_ID_LEN) + throw new IllegalArgumentException("Bad message ID length"); - this.messageId = messageId; - } + this.messageId = messageId; + } - public MessageId(byte[] data, int offset) { - if ((offset + MESSAGE_ID_LEN) > data.length) - throw new IllegalArgumentException("Bad message ID buffer length"); + public MessageId(byte[] data, int offset) { + if ((offset + MESSAGE_ID_LEN) > data.length) + throw new IllegalArgumentException("Bad message ID buffer length"); - this.messageId = new byte[MESSAGE_ID_LEN]; - System.arraycopy(data, offset, this.messageId, 0, MESSAGE_ID_LEN); - } + this.messageId = new byte[MESSAGE_ID_LEN]; + System.arraycopy(data, offset, this.messageId, 0, MESSAGE_ID_LEN); + } - public byte[] getMessageId() { - return messageId; - } + public byte[] getMessageId() { + return messageId; + } - @Override - public String toString() { - return DataUtils.byteArrayToHexString(messageId); - } + @Override + public String toString() { + return DataUtils.byteArrayToHexString(messageId); + } } diff --git a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java b/source/src/main/java/ch/threema/apitool/PublicKeyStore.java index 741a0a0..815b46f 100644 --- a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java +++ b/source/src/main/java/ch/threema/apitool/PublicKeyStore.java @@ -28,62 +28,61 @@ import java.util.Map; /** - * Stores and caches public keys for Threema users. Extend this class to provide your - * own storage implementation, e.g. in a file or database. + * Stores and caches public keys for Threema users. Extend this class to provide your own storage + * implementation, e.g. in a file or database. */ public abstract class PublicKeyStore { - private final Map cache = new HashMap<>(); + private final Map cache = new HashMap<>(); - /** - * Get the public key for a given Threema ID. The cache is checked first; if it - * is not found in the cache, fetchPublicKey() is called. - * - * @param threemaId The Threema ID whose public key should be obtained - * @return The public key, or null if not found. - */ - public final byte[] getPublicKey(String threemaId) { - synchronized (this.cache) { - byte[] pk = this.cache.get(threemaId); + /** + * Get the public key for a given Threema ID. The cache is checked first; if it is not found in + * the cache, fetchPublicKey() is called. + * + * @param threemaId The Threema ID whose public key should be obtained + * @return The public key, or null if not found. + */ + public final byte[] getPublicKey(String threemaId) { + synchronized (this.cache) { + byte[] pk = this.cache.get(threemaId); - if (pk == null) { - pk = this.fetchPublicKey(threemaId); - this.cache.put(threemaId, pk); - } - return pk; - } + if (pk == null) { + pk = this.fetchPublicKey(threemaId); + this.cache.put(threemaId, pk); + } + return pk; + } + } - } + /** + * Store the public key for a given Threema ID in the cache, and the underlying store. + * + * @param threemaId The Threema ID whose public key should be stored + * @param publicKey The corresponding public key. + */ + public final void setPublicKey(String threemaId, byte[] publicKey) { + if (publicKey != null) { + synchronized (this.cache) { + this.cache.put(threemaId, publicKey); + this.save(threemaId, publicKey); + } + } + } - /** - * Store the public key for a given Threema ID in the cache, and the underlying store. - * - * @param threemaId The Threema ID whose public key should be stored - * @param publicKey The corresponding public key. - */ - public final void setPublicKey(String threemaId, byte[] publicKey) { - if(publicKey != null) { - synchronized (this.cache) { - this.cache.put(threemaId, publicKey); - this.save(threemaId, publicKey); - } - } - } + /** + * Fetch the public key for the given Threema ID from the store. Override to provide your own + * implementation to read from the store. + * + * @param threemaId The Threema ID whose public key should be obtained + * @return The public key, or null if not found. + */ + protected abstract byte[] fetchPublicKey(String threemaId); - /** - * Fetch the public key for the given Threema ID from the store. Override to provide - * your own implementation to read from the store. - * - * @param threemaId The Threema ID whose public key should be obtained - * @return The public key, or null if not found. - */ - abstract protected byte[] fetchPublicKey(String threemaId); - - /** - * Save the public key for a given Threema ID in the store. Override to provide - * your own implementation to write to the store. - * - * @param threemaId The Threema ID whose public key should be stored - * @param publicKey The corresponding public key. - */ - abstract protected void save(String threemaId, byte[] publicKey); + /** + * Save the public key for a given Threema ID in the store. Override to provide your own + * implementation to write to the store. + * + * @param threemaId The Threema ID whose public key should be stored + * @param publicKey The corresponding public key. + */ + protected abstract void save(String threemaId, byte[] publicKey); } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java index 8037f11..500d968 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java @@ -29,28 +29,27 @@ import ch.threema.apitool.results.CapabilityResult; public class CapabilityCommand extends Command { - private final ThreemaIDField threemaIdField; - private final ThreemaIDField fromField; - private final TextField secretField; - - public CapabilityCommand() { - super("Fetch Capability", - "Fetch the capability of a Threema ID"); - - this.threemaIdField = this.createThreemaId("id"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } - - @Override - protected void execute() throws Exception { - String threemaId = this.threemaIdField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - - CapabilityResult capabilities = this.createConnector(from, secret) - .lookupKeyCapability(threemaId); - - System.out.println(capabilities); - } + private final ThreemaIDField threemaIdField; + private final ThreemaIDField fromField; + private final TextField secretField; + + public CapabilityCommand() { + super("Fetch Capability", "Fetch the capability of a Threema ID"); + + this.threemaIdField = this.createThreemaId("id"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } + + @Override + protected void execute() throws Exception { + String threemaId = this.threemaIdField.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + + CapabilityResult capabilities = + this.createConnector(from, secret).lookupKeyCapability(threemaId); + + System.out.println(capabilities); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/Command.java b/source/src/main/java/ch/threema/apitool/console/commands/Command.java index f8e136f..3d40faf 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/Command.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/Command.java @@ -32,168 +32,172 @@ import java.util.LinkedList; import java.util.List; -abstract public class Command { - private final List fields = new LinkedList<>(); - private final String subject; - private final String description; - - public Command(String subject, String description) { - this.subject = subject; - this.description = description; - } - - private void addField(Field f) { - if(f.isRequired()) { - int pos = this.fields.size(); - //add after last required - for(int n = 0; n < this.fields.size(); n++) { - if(!this.fields.get(n).isRequired()) { - pos = n; - break; - } - } - this.fields.add(pos, f); - } - else { - this.fields.add(f); - } - } - protected TextField createTextField(String key) { - return this.createTextField(key, true); - } - protected TextField createTextField(String key, boolean required) { - TextField field = new TextField(key, required); - this.addField(field); - return field; - } - - protected ThreemaIDField createThreemaId(String key) { - return this.createThreemaId(key, true); - } - - protected ThreemaIDField createThreemaId(String key, boolean required) { - ThreemaIDField field = new ThreemaIDField(key, required); - this.addField(field); - return field; - } - - protected PublicKeyField createPublicKeyField(String key) { - return this.createPublicKeyField(key, true); - } - - protected PublicKeyField createPublicKeyField(String key, boolean required) { - PublicKeyField field = new PublicKeyField(key, required); - this.addField(field); - return field; - } - - protected PrivateKeyField createPrivateKeyField(String key) { - return this.createPrivateKeyField(key, true); - } - - protected PrivateKeyField createPrivateKeyField(String key, boolean required) { - PrivateKeyField field = new PrivateKeyField(key, required); - this.addField(field); - return field; - } - - protected FileField createFileField(String key) { - return this.createFileField(key, true); - } - - protected FileField createFileField(String key, boolean required) { - FileField field = new FileField(key, required); - this.addField(field); - return field; - } - - protected FolderField createFolderField(String key) { - return this.createFolderField(key, true); - } - - protected FolderField createFolderField(String key, boolean required) { - FolderField field = new FolderField(key, required); - this.addField(field); - return field; - } - - protected ByteArrayField createByteArrayField(String key) { - return this.createByteArrayField(key, true); - } - - protected ByteArrayField createByteArrayField(String key, boolean required) { - ByteArrayField field = new ByteArrayField(key, required); - this.addField(field); - return field; - } - - - protected APIConnector createConnector(String gatewayId, String secret) { - return new APIConnector(gatewayId, secret, new PublicKeyStore() { - @Override - protected byte[] fetchPublicKey(String threemaId) { - return null; - } - - @Override - protected void save(String threemaId, byte[] publicKey) { - //do nothing - } - }); - } - - protected String readStream(InputStream stream, String charset) throws IOException { - try { - Reader reader = new BufferedReader(new InputStreamReader(stream, charset)); - StringBuilder builder = new StringBuilder(); - char[] buffer = new char[8192]; - int read; - while ((read = reader.read(buffer, 0, buffer.length)) > 0) { - builder.append(buffer, 0, read); - } - return builder.toString(); - } finally { - stream.close(); - } - } - - public final void run(String[] arguments) throws Exception { - int pos = 0; - for(Field f: this.fields) { - if(arguments.length > pos) { - f.setValue(arguments[pos]); - } - pos++; - } - - //validate - for(Field f: this.fields) { - if(!f.isValid()) { - return; - } - } - - this.execute(); - } - - public final String getSubject() { - return this.subject; - } - - public final String getUsageArguments() { - StringBuilder usage = new StringBuilder(); - for(Field f: this.fields) { - usage.append(" ") - .append(f.isRequired() ? "<" : "[") - .append(f.getKey()) - .append(f.isRequired() ? ">" : "]"); - } - return usage.toString().trim(); - } - - public final String getUsageDescription() { - return this.description; - } - - protected abstract void execute() throws Exception; +public abstract class Command { + private final List fields = new LinkedList<>(); + private final String subject; + private final String description; + + public Command(String subject, String description) { + this.subject = subject; + this.description = description; + } + + private void addField(Field f) { + if (f.isRequired()) { + int pos = this.fields.size(); + // add after last required + for (int n = 0; n < this.fields.size(); n++) { + if (!this.fields.get(n).isRequired()) { + pos = n; + break; + } + } + this.fields.add(pos, f); + } else { + this.fields.add(f); + } + } + + protected TextField createTextField(String key) { + return this.createTextField(key, true); + } + + protected TextField createTextField(String key, boolean required) { + TextField field = new TextField(key, required); + this.addField(field); + return field; + } + + protected ThreemaIDField createThreemaId(String key) { + return this.createThreemaId(key, true); + } + + protected ThreemaIDField createThreemaId(String key, boolean required) { + ThreemaIDField field = new ThreemaIDField(key, required); + this.addField(field); + return field; + } + + protected PublicKeyField createPublicKeyField(String key) { + return this.createPublicKeyField(key, true); + } + + protected PublicKeyField createPublicKeyField(String key, boolean required) { + PublicKeyField field = new PublicKeyField(key, required); + this.addField(field); + return field; + } + + protected PrivateKeyField createPrivateKeyField(String key) { + return this.createPrivateKeyField(key, true); + } + + protected PrivateKeyField createPrivateKeyField(String key, boolean required) { + PrivateKeyField field = new PrivateKeyField(key, required); + this.addField(field); + return field; + } + + protected FileField createFileField(String key) { + return this.createFileField(key, true); + } + + protected FileField createFileField(String key, boolean required) { + FileField field = new FileField(key, required); + this.addField(field); + return field; + } + + protected FolderField createFolderField(String key) { + return this.createFolderField(key, true); + } + + protected FolderField createFolderField(String key, boolean required) { + FolderField field = new FolderField(key, required); + this.addField(field); + return field; + } + + protected ByteArrayField createByteArrayField(String key) { + return this.createByteArrayField(key, true); + } + + protected ByteArrayField createByteArrayField(String key, boolean required) { + ByteArrayField field = new ByteArrayField(key, required); + this.addField(field); + return field; + } + + protected APIConnector createConnector(String gatewayId, String secret) { + return new APIConnector( + gatewayId, + secret, + new PublicKeyStore() { + @Override + protected byte[] fetchPublicKey(String threemaId) { + return null; + } + + @Override + protected void save(String threemaId, byte[] publicKey) { + // do nothing + } + }); + } + + protected String readStream(InputStream stream, String charset) throws IOException { + try { + Reader reader = new BufferedReader(new InputStreamReader(stream, charset)); + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[8192]; + int read; + while ((read = reader.read(buffer, 0, buffer.length)) > 0) { + builder.append(buffer, 0, read); + } + return builder.toString(); + } finally { + stream.close(); + } + } + + public final void run(String[] arguments) throws Exception { + int pos = 0; + for (Field f : this.fields) { + if (arguments.length > pos) { + f.setValue(arguments[pos]); + } + pos++; + } + + // validate + for (Field f : this.fields) { + if (!f.isValid()) { + return; + } + } + + this.execute(); + } + + public final String getSubject() { + return this.subject; + } + + public final String getUsageArguments() { + StringBuilder usage = new StringBuilder(); + for (Field f : this.fields) { + usage + .append(" ") + .append(f.isRequired() ? "<" : "[") + .append(f.getKey()) + .append(f.isRequired() ? ">" : "]"); + } + return usage.toString().trim(); + } + + public final String getUsageDescription() { + return this.description; + } + + protected abstract void execute() throws Exception; } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java index 1cb09e8..df75dd5 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java @@ -28,28 +28,28 @@ import ch.threema.apitool.console.commands.fields.ThreemaIDField; public class CreditsCommand extends Command { - private final ThreemaIDField fromField; - private final TextField secretField; + private final ThreemaIDField fromField; + private final TextField secretField; - public CreditsCommand() { - super("Credits", "Fetch the remaining credits"); + public CreditsCommand() { + super("Credits", "Fetch the remaining credits"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } - @Override - protected void execute() throws Exception { - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); + @Override + protected void execute() throws Exception { + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); - Integer credits = this.createConnector(from, secret).lookupCredits(); + Integer credits = this.createConnector(from, secret).lookupCredits(); - if (credits != null) { - System.out.println("Remaining credits: " + credits); - } else { - System.out.println("Error fetching credits"); - ; - } + if (credits != null) { + System.out.println("Remaining credits: " + credits); + } else { + System.out.println("Error fetching credits"); + ; } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java index 2dd42f4..3701afb 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java @@ -31,43 +31,44 @@ import java.nio.file.Path; public class DecryptAndDownloadCommand extends Command { - private final ThreemaIDField threemaId; - private final ThreemaIDField fromField; - private final TextField secretField; - private final PrivateKeyField privateKeyField; - private final ByteArrayField nonceField; - private final FolderField outputFolderField; - private final TextField messageIdField; + private final ThreemaIDField threemaId; + private final ThreemaIDField fromField; + private final TextField secretField; + private final PrivateKeyField privateKeyField; + private final ByteArrayField nonceField; + private final FolderField outputFolderField; + private final TextField messageIdField; - public DecryptAndDownloadCommand() { - super("Decrypt and download", - "Decrypt a box (box from the stdin) message and download (if the message is a image or file message) the file(s) to the defined directory" - ); - this.threemaId = this.createThreemaId("id"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - this.messageIdField = this.createTextField("messageId"); - this.nonceField = this.createByteArrayField("nonce"); - this.outputFolderField = this.createFolderField("outputFolder", false); - } + public DecryptAndDownloadCommand() { + super( + "Decrypt and download", + "Decrypt a box (box from the stdin) message and download (if the message is a image or file message) the file(s) to the defined directory"); + this.threemaId = this.createThreemaId("id"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + this.privateKeyField = this.createPrivateKeyField("privateKey"); + this.messageIdField = this.createTextField("messageId"); + this.nonceField = this.createByteArrayField("nonce"); + this.outputFolderField = this.createFolderField("outputFolder", false); + } - @Override - protected void execute() throws Exception { - String id = this.threemaId.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - byte[] privateKey = this.privateKeyField.getValue(); - byte[] nonce = this.nonceField.getValue(); - String messageId = this.messageIdField.getValue(); - Path outputFolder = this.outputFolderField.getValue(); + @Override + protected void execute() throws Exception { + String id = this.threemaId.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + byte[] privateKey = this.privateKeyField.getValue(); + byte[] nonce = this.nonceField.getValue(); + String messageId = this.messageIdField.getValue(); + Path outputFolder = this.outputFolderField.getValue(); - E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); + E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); - byte[] box = DataUtils.hexStringToByteArray(this.readStream(System.in, "UTF-8").trim()); + byte[] box = DataUtils.hexStringToByteArray(this.readStream(System.in, "UTF-8").trim()); - E2EHelper.ReceiveMessageResult res = e2EHelper.receiveMessage(id, messageId, box, nonce, outputFolder); - System.out.println(res.toString()); - System.out.println(res.getFiles().toString()); - } + E2EHelper.ReceiveMessageResult res = + e2EHelper.receiveMessage(id, messageId, box, nonce, outputFolder); + System.out.println(res.toString()); + System.out.println(res.getFiles().toString()); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java index fd74ad1..df77a00 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java @@ -32,30 +32,31 @@ import ch.threema.apitool.messages.ThreemaMessage; public class DecryptCommand extends Command { - private final PrivateKeyField privateKeyField; - private final PublicKeyField publicKeyField; - private final ByteArrayField nonceField; + private final PrivateKeyField privateKeyField; + private final PublicKeyField publicKeyField; + private final ByteArrayField nonceField; - public DecryptCommand() { - super("Decrypt", - "Decrypt standard input using the given recipient private key and sender public key. The nonce must be given on the command line, and the box (hex) on standard input. Prints the decrypted message to standard output."); + public DecryptCommand() { + super( + "Decrypt", + "Decrypt standard input using the given recipient private key and sender public key. The nonce must be given on the command line, and the box (hex) on standard input. Prints the decrypted message to standard output."); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - this.publicKeyField = this.createPublicKeyField("publicKey"); - this.nonceField = this.createByteArrayField("nonce"); - } + this.privateKeyField = this.createPrivateKeyField("privateKey"); + this.publicKeyField = this.createPublicKeyField("publicKey"); + this.nonceField = this.createByteArrayField("nonce"); + } - @Override - protected void execute() throws Exception { - byte[] privateKey = this.privateKeyField.getValue(); - byte[] publicKey = this.publicKeyField.getValue(); - byte[] nonce = this.nonceField.getValue(); + @Override + protected void execute() throws Exception { + byte[] privateKey = this.privateKeyField.getValue(); + byte[] publicKey = this.publicKeyField.getValue(); + byte[] nonce = this.nonceField.getValue(); - /* read box from stdin */ - byte[] box = DataUtils.hexStringToByteArray(readStream(System.in, "UTF-8")); + /* read box from stdin */ + byte[] box = DataUtils.hexStringToByteArray(readStream(System.in, "UTF-8")); - ThreemaMessage message = CryptTool.decryptMessage(box, privateKey, publicKey, nonce); + ThreemaMessage message = CryptTool.decryptMessage(box, privateKey, publicKey, nonce); - System.out.println(message); - } + System.out.println(message); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java index 32c9e1e..37a770e 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java @@ -29,21 +29,21 @@ import ch.threema.apitool.Key; public class DerivePublicKeyCommand extends Command { - private final PrivateKeyField privateKeyField; + private final PrivateKeyField privateKeyField; - public DerivePublicKeyCommand() { - super("Derive Public Key", - "Derive the public key that corresponds with the given private key."); + public DerivePublicKeyCommand() { + super( + "Derive Public Key", "Derive the public key that corresponds with the given private key."); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - } + this.privateKeyField = this.createPrivateKeyField("privateKey"); + } - @Override - protected void execute() throws Exception { - byte[] privateKey = this.privateKeyField.getValue(); - byte[] publicKey = CryptTool.derivePublicKey(privateKey); + @Override + protected void execute() throws Exception { + byte[] privateKey = this.privateKeyField.getValue(); + byte[] publicKey = CryptTool.derivePublicKey(privateKey); - System.out.println("res"); - System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); - } + System.out.println("res"); + System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java index 8abaf04..91d3a9d 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java @@ -31,28 +31,29 @@ import ch.threema.apitool.results.EncryptResult; public class EncryptCommand extends Command { - private final PrivateKeyField privateKeyField; - private final PublicKeyField publicKeyField; + private final PrivateKeyField privateKeyField; + private final PublicKeyField publicKeyField; - public EncryptCommand() { - super("Encrypt", - "Encrypt standard input using the given sender private key and recipient public key. Prints two lines to standard output: first the nonce (hex), and then the box (hex)."); + public EncryptCommand() { + super( + "Encrypt", + "Encrypt standard input using the given sender private key and recipient public key. Prints two lines to standard output: first the nonce (hex), and then the box (hex)."); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - this.publicKeyField = this.createPublicKeyField("publicKey"); - } + this.privateKeyField = this.createPrivateKeyField("privateKey"); + this.publicKeyField = this.createPublicKeyField("publicKey"); + } - @Override - protected void execute() throws Exception { - byte[] privateKey = this.privateKeyField.getValue(); - byte[] publicKey = this.publicKeyField.getValue(); + @Override + protected void execute() throws Exception { + byte[] privateKey = this.privateKeyField.getValue(); + byte[] publicKey = this.publicKeyField.getValue(); - /* read text from stdin */ - String text = readStream(System.in, "UTF-8").trim(); + /* read text from stdin */ + String text = readStream(System.in, "UTF-8").trim(); - EncryptResult res = CryptTool.encryptTextMessage(text, privateKey, publicKey); + EncryptResult res = CryptTool.encryptTextMessage(text, privateKey, publicKey); - System.out.println(DataUtils.byteArrayToHexString(res.getNonce())); - System.out.println(DataUtils.byteArrayToHexString(res.getResult())); - } + System.out.println(DataUtils.byteArrayToHexString(res.getNonce())); + System.out.println(DataUtils.byteArrayToHexString(res.getResult())); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java b/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java index d586fd9..c6c774c 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java @@ -30,29 +30,28 @@ import ch.threema.apitool.Key; public class FetchPublicKey extends Command { - private final ThreemaIDField threemaIdField; - private final ThreemaIDField fromField; - private final TextField secretField; + private final ThreemaIDField threemaIdField; + private final ThreemaIDField fromField; + private final TextField secretField; - public FetchPublicKey() { - super("Fetch Public Key", - "Lookup the public key for the given ID."); + public FetchPublicKey() { + super("Fetch Public Key", "Lookup the public key for the given ID."); - this.threemaIdField = this.createThreemaId("id"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } + this.threemaIdField = this.createThreemaId("id"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } - @Override - protected void execute() throws Exception { - String threemaId = this.threemaIdField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); + @Override + protected void execute() throws Exception { + String threemaId = this.threemaIdField.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); - APIConnector apiConnector = this.createConnector(from, secret); - byte[] publicKey = apiConnector.lookupKey(threemaId); - if (publicKey != null) { - System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); - } - } + APIConnector apiConnector = this.createConnector(from, secret); + byte[] publicKey = apiConnector.lookupKey(threemaId); + if (publicKey != null) { + System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); + } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java index d3f176b..2583ea5 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java @@ -33,25 +33,28 @@ import java.io.File; public class GenerateKeyPairCommand extends Command { - private final TextField privateKeyPath; - private final TextField publicKeyPath; - - public GenerateKeyPairCommand() { - super("Generate Key Pair", - "Generate a new key pair and write the private and public keys to the respective files (in hex)."); - this.privateKeyPath = this.createTextField("privateKeyFile"); - this.publicKeyPath = this.createTextField("publicKeyPath"); - } - - @Override - protected void execute() throws Exception { - byte[] privateKey = new byte[NaCl.SECRETKEYBYTES]; - byte[] publicKey = new byte[NaCl.PUBLICKEYBYTES]; - - CryptTool.generateKeyPair(privateKey, publicKey); - - // Write both keys to file - DataUtils.writeKeyFile(new File(this.privateKeyPath.getValue()), new Key(Key.KeyType.PRIVATE, privateKey)); - DataUtils.writeKeyFile(new File(this.publicKeyPath.getValue()), new Key(Key.KeyType.PUBLIC, publicKey)); - } + private final TextField privateKeyPath; + private final TextField publicKeyPath; + + public GenerateKeyPairCommand() { + super( + "Generate Key Pair", + "Generate a new key pair and write the private and public keys to the respective files (in hex)."); + this.privateKeyPath = this.createTextField("privateKeyFile"); + this.publicKeyPath = this.createTextField("publicKeyPath"); + } + + @Override + protected void execute() throws Exception { + byte[] privateKey = new byte[NaCl.SECRETKEYBYTES]; + byte[] publicKey = new byte[NaCl.PUBLICKEYBYTES]; + + CryptTool.generateKeyPair(privateKey, publicKey); + + // Write both keys to file + DataUtils.writeKeyFile( + new File(this.privateKeyPath.getValue()), new Key(Key.KeyType.PRIVATE, privateKey)); + DataUtils.writeKeyFile( + new File(this.publicKeyPath.getValue()), new Key(Key.KeyType.PUBLIC, publicKey)); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java index 4aa7311..aa5d915 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java @@ -29,18 +29,18 @@ import ch.threema.apitool.DataUtils; public class HashEmailCommand extends Command { - private final TextField emailField; + private final TextField emailField; - public HashEmailCommand() { - super("Hash Email Address", - "Hash an email address for identity lookup. Prints the hash in hex."); + public HashEmailCommand() { + super( + "Hash Email Address", "Hash an email address for identity lookup. Prints the hash in hex."); - this.emailField = this.createTextField("email", true); - } + this.emailField = this.createTextField("email", true); + } - @Override - protected void execute() throws Exception { - byte[] emailHash = CryptTool.hashEmail(this.emailField.getValue()); - System.out.println(DataUtils.byteArrayToHexString(emailHash)); - } + @Override + protected void execute() throws Exception { + byte[] emailHash = CryptTool.hashEmail(this.emailField.getValue()); + System.out.println(DataUtils.byteArrayToHexString(emailHash)); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java index 1e349f7..dba4996 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java @@ -29,20 +29,19 @@ import ch.threema.apitool.DataUtils; public class HashPhoneCommand extends Command { - private final TextField phoneNo; + private final TextField phoneNo; - public HashPhoneCommand() { - super("Hash Phone Number", - "Hash a phone number for identity lookup. Prints the hash in hex."); + public HashPhoneCommand() { + super("Hash Phone Number", "Hash a phone number for identity lookup. Prints the hash in hex."); - this.phoneNo = this.createTextField("phoneNo", true); - } + this.phoneNo = this.createTextField("phoneNo", true); + } - @Override - protected void execute() throws Exception { - String phoneNo = this.phoneNo.getValue(); + @Override + protected void execute() throws Exception { + String phoneNo = this.phoneNo.getValue(); - byte[] phoneHash = CryptTool.hashPhoneNo(phoneNo); - System.out.println(DataUtils.byteArrayToHexString(phoneHash)); - } + byte[] phoneHash = CryptTool.hashPhoneNo(phoneNo); + System.out.println(DataUtils.byteArrayToHexString(phoneHash)); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java b/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java index 39a5ee1..8f483e6 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java @@ -29,29 +29,30 @@ import ch.threema.apitool.console.commands.fields.ThreemaIDField; public class IDLookupByEmail extends Command { - private final TextField emailField; - private final ThreemaIDField fromField; - private final TextField secretField; + private final TextField emailField; + private final ThreemaIDField fromField; + private final TextField secretField; - public IDLookupByEmail() { - super("ID Lookup By Email Address", - "Lookup the ID linked to the given email address (will be hashed locally)."); + public IDLookupByEmail() { + super( + "ID Lookup By Email Address", + "Lookup the ID linked to the given email address (will be hashed locally)."); - this.emailField = this.createTextField("email"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } + this.emailField = this.createTextField("email"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } - @Override - protected void execute() throws Exception { - String email = this.emailField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); + @Override + protected void execute() throws Exception { + String email = this.emailField.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); - APIConnector apiConnector = this.createConnector(from, secret); - String id = apiConnector.lookupEmail(email); - if (id != null) { - System.out.println(id); - } - } + APIConnector apiConnector = this.createConnector(from, secret); + String id = apiConnector.lookupEmail(email); + if (id != null) { + System.out.println(id); + } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java b/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java index e02fb7e..cbd99b2 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java @@ -29,29 +29,30 @@ import ch.threema.apitool.console.commands.fields.ThreemaIDField; public class IDLookupByPhoneNo extends Command { - private final TextField phoneNoField; - private final ThreemaIDField fromField; - private final TextField secretField; + private final TextField phoneNoField; + private final ThreemaIDField fromField; + private final TextField secretField; - public IDLookupByPhoneNo() { - super("ID Lookup By Phone Number", - "Lookup the ID linked to the given phone number (will be hashed locally)."); + public IDLookupByPhoneNo() { + super( + "ID Lookup By Phone Number", + "Lookup the ID linked to the given phone number (will be hashed locally)."); - this.phoneNoField = this.createTextField("phoneNo"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } + this.phoneNoField = this.createTextField("phoneNo"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } - @Override - protected void execute() throws Exception { - String phoneNo = this.phoneNoField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); + @Override + protected void execute() throws Exception { + String phoneNo = this.phoneNoField.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); - APIConnector apiConnector = this.createConnector(from, secret); - String id = apiConnector.lookupPhone(phoneNo); - if (id != null) { - System.out.println(id); - } - } + APIConnector apiConnector = this.createConnector(from, secret); + String id = apiConnector.lookupPhone(phoneNo); + if (id != null) { + System.out.println(id); + } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java index 3029618..0b0a927 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java @@ -33,37 +33,36 @@ import java.io.File; public class SendE2EFileMessageCommand extends Command { - private final ThreemaIDField threemaId; - private final ThreemaIDField fromField; - private final TextField secretField; - private final PrivateKeyField privateKeyField; - private final FileField fileField; - private final FileField thumbnailField; + private final ThreemaIDField threemaId; + private final ThreemaIDField fromField; + private final TextField secretField; + private final PrivateKeyField privateKeyField; + private final FileField fileField; + private final FileField thumbnailField; - public SendE2EFileMessageCommand() { - super("Send End-to-End Encrypted File Message", - "Encrypt the file (and thumbnail) and send a file message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success." - ); - this.threemaId = this.createThreemaId("to"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - this.fileField = this.createFileField("file"); - this.thumbnailField = this.createFileField("thumbnail", false); + public SendE2EFileMessageCommand() { + super( + "Send End-to-End Encrypted File Message", + "Encrypt the file (and thumbnail) and send a file message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."); + this.threemaId = this.createThreemaId("to"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + this.privateKeyField = this.createPrivateKeyField("privateKey"); + this.fileField = this.createFileField("file"); + this.thumbnailField = this.createFileField("thumbnail", false); + } - } + @Override + protected void execute() throws Exception { + String to = this.threemaId.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + byte[] privateKey = this.privateKeyField.getValue(); + File file = this.fileField.getValue(); + File thumbnail = this.thumbnailField.getValue(); - @Override - protected void execute() throws Exception { - String to = this.threemaId.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - byte[] privateKey = this.privateKeyField.getValue(); - File file = this.fileField.getValue(); - File thumbnail = this.thumbnailField.getValue(); - - E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); - String messageId = e2EHelper.sendFileMessage(to, file, thumbnail); - System.out.println("MessageId: " + messageId); - } + E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); + String messageId = e2EHelper.sendFileMessage(to, file, thumbnail); + System.out.println("MessageId: " + messageId); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java index 14a248f..f36a9d4 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java @@ -33,33 +33,34 @@ import java.nio.file.Path; public class SendE2EImageMessageCommand extends Command { - private final ThreemaIDField toField; - private final ThreemaIDField fromField; - private final TextField secretField; - private final PrivateKeyField privateKeyField; - private final FolderField imageFilePath; + private final ThreemaIDField toField; + private final ThreemaIDField fromField; + private final TextField secretField; + private final PrivateKeyField privateKeyField; + private final FolderField imageFilePath; - public SendE2EImageMessageCommand() { - super("Send End-to-End Encrypted Image Message", - "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."); + public SendE2EImageMessageCommand() { + super( + "Send End-to-End Encrypted Image Message", + "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."); - this.toField = this.createThreemaId("to", true); - this.fromField = this.createThreemaId("from", true); - this.secretField = this.createTextField("secret", true); - this.privateKeyField = this.createPrivateKeyField("privateKey", true); - this.imageFilePath = this.createFolderField("imageFilePath", true); - } + this.toField = this.createThreemaId("to", true); + this.fromField = this.createThreemaId("from", true); + this.secretField = this.createTextField("secret", true); + this.privateKeyField = this.createPrivateKeyField("privateKey", true); + this.imageFilePath = this.createFolderField("imageFilePath", true); + } - @Override - protected void execute() throws Exception { - String to = this.toField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - byte[] privateKey = this.privateKeyField.getValue(); - Path imageFilePath = this.imageFilePath.getValue(); + @Override + protected void execute() throws Exception { + String to = this.toField.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + byte[] privateKey = this.privateKeyField.getValue(); + Path imageFilePath = this.imageFilePath.getValue(); - E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); - String messageId = e2EHelper.sendImageMessage(to, imageFilePath.toString()); - System.out.println("MessageId: " + messageId); - } + E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); + String messageId = e2EHelper.sendImageMessage(to, imageFilePath.toString()); + System.out.println("MessageId: " + messageId); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java index 1aaf576..9c74496 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java @@ -30,31 +30,32 @@ import ch.threema.apitool.helpers.E2EHelper; public class SendE2ETextMessageCommand extends Command { - private final ThreemaIDField threemaId; - private final ThreemaIDField fromField; - private final TextField secretField; - private final PrivateKeyField privateKeyField; + private final ThreemaIDField threemaId; + private final ThreemaIDField fromField; + private final TextField secretField; + private final PrivateKeyField privateKeyField; - public SendE2ETextMessageCommand() { - super("Send End-to-End Encrypted Text Message", - "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."); - this.threemaId = this.createThreemaId("to"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - this.privateKeyField = this.createPrivateKeyField("privateKey"); - } + public SendE2ETextMessageCommand() { + super( + "Send End-to-End Encrypted Text Message", + "Encrypt standard input and send the message to the given ID. 'from' is the API identity and 'secret' is the API secret. Prints the message ID on success."); + this.threemaId = this.createThreemaId("to"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + this.privateKeyField = this.createPrivateKeyField("privateKey"); + } - @Override - protected void execute() throws Exception { - String to = this.threemaId.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - byte[] privateKey = this.privateKeyField.getValue(); + @Override + protected void execute() throws Exception { + String to = this.threemaId.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + byte[] privateKey = this.privateKeyField.getValue(); - String text = this.readStream(System.in, "UTF-8").trim(); + String text = this.readStream(System.in, "UTF-8").trim(); - E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); - String messageId = e2EHelper.sendTextMessage(to, text); - System.out.println("MessageId: " + messageId); - } + E2EHelper e2EHelper = new E2EHelper(this.createConnector(from, secret), privateKey); + String messageId = e2EHelper.sendTextMessage(to, text); + System.out.println("MessageId: " + messageId); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java index 1fbef43..1c2d8c7 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java @@ -29,29 +29,30 @@ import ch.threema.apitool.console.commands.fields.ThreemaIDField; public class SendSimpleMessageCommand extends Command { - private final ThreemaIDField threemaId; - private final ThreemaIDField fromField; - private final TextField secretField; - - public SendSimpleMessageCommand() { - super("Send Simple Message", - "Send a message from standard input with server-side encryption to the given ID. 'from' is the API identity and 'secret' is the API secret. Returns the message ID on success."); - - this.threemaId = this.createThreemaId("to"); - this.fromField = this.createThreemaId("from"); - this.secretField = this.createTextField("secret"); - } - - @Override - protected void execute() throws Exception { - String to = this.threemaId.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); - - String text = readStream(System.in, "UTF-8").trim(); - - APIConnector apiConnector = this.createConnector(from, secret); - String messageId = apiConnector.sendTextMessageSimple(to, text); - System.out.println(messageId); - } + private final ThreemaIDField threemaId; + private final ThreemaIDField fromField; + private final TextField secretField; + + public SendSimpleMessageCommand() { + super( + "Send Simple Message", + "Send a message from standard input with server-side encryption to the given ID. 'from' is the API identity and 'secret' is the API secret. Returns the message ID on success."); + + this.threemaId = this.createThreemaId("to"); + this.fromField = this.createThreemaId("from"); + this.secretField = this.createTextField("secret"); + } + + @Override + protected void execute() throws Exception { + String to = this.threemaId.getValue(); + String from = this.fromField.getValue(); + String secret = this.secretField.getValue(); + + String text = readStream(System.in, "UTF-8").trim(); + + APIConnector apiConnector = this.createConnector(from, secret); + String messageId = apiConnector.sendTextMessageSimple(to, text); + System.out.println(messageId); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java index f200c13..1372815 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java @@ -27,11 +27,11 @@ import ch.threema.apitool.DataUtils; public class ByteArrayField extends Field { - public ByteArrayField(String key, boolean required) { - super(key, required); - } + public ByteArrayField(String key, boolean required) { + super(key, required); + } - public byte[] getValue() { - return DataUtils.hexStringToByteArray(this.value); - } + public byte[] getValue() { + return DataUtils.hexStringToByteArray(this.value); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java index 7655c5c..81ade93 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java @@ -29,41 +29,41 @@ public abstract class Field { - private final String key; - private final boolean required; - protected String value; + private final String key; + private final boolean required; + protected String value; - protected Field(String key, boolean required) { - this.key = key; - this.required = required; - } + protected Field(String key, boolean required) { + this.key = key; + this.required = required; + } - public void setValue(String value) { - this.value = value; - } + public void setValue(String value) { + this.value = value; + } - public boolean isRequired() { - return this.required; - } + public boolean isRequired() { + return this.required; + } - public String getKey() { - return this.key; - } + public String getKey() { + return this.key; + } - public boolean isValid() throws RequiredCommandFieldMissingException, InvalidCommandFieldValueException { - if(this.isRequired() && this.value == null) { - throw new RequiredCommandFieldMissingException("required field " + this.key + " not set"); - } + public boolean isValid() + throws RequiredCommandFieldMissingException, InvalidCommandFieldValueException { + if (this.isRequired() && this.value == null) { + throw new RequiredCommandFieldMissingException("required field " + this.key + " not set"); + } - if(!this.validate()) { - throw new InvalidCommandFieldValueException("field " + this.key + " value invalid"); - } + if (!this.validate()) { + throw new InvalidCommandFieldValueException("field " + this.key + " value invalid"); + } - return true; - } - - protected boolean validate() { - return true; - } + return true; + } + protected boolean validate() { + return true; + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java index 8b9b649..d650b3d 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java @@ -28,22 +28,20 @@ public class FileField extends Field { - public FileField(String key, boolean required) { - super(key, required); - } + public FileField(String key, boolean required) { + super(key, required); + } - public File getValue() { - if(this.value != null) { - return new File(this.value); - } + public File getValue() { + if (this.value != null) { + return new File(this.value); + } - return null; - } + return null; + } - @Override - protected boolean validate() { - return !this.isRequired() - || (this.value != null - && new File(this.value).isFile()); - } + @Override + protected boolean validate() { + return !this.isRequired() || (this.value != null && new File(this.value).isFile()); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java index 194801d..efc8cf6 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java @@ -28,11 +28,11 @@ import java.nio.file.Paths; public class FolderField extends Field { - public FolderField(String key, boolean required) { - super(key, required); - } + public FolderField(String key, boolean required) { + super(key, required); + } - public Path getValue() { - return Paths.get(this.value); - } + public Path getValue() { + return Paths.get(this.value); + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java index b816dcd..bc814f7 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java @@ -32,22 +32,21 @@ import java.io.IOException; public abstract class KeyField extends Field { - public KeyField(String key, boolean required) { - super(key, required); - } - - - byte[] readKey(String argument, String expectedKeyType) throws IOException, InvalidKeyException { - Key key; - - // Try to open a file with that name - File keyFile = new File(argument); - if (keyFile.isFile()) { - key = DataUtils.readKeyFile(keyFile, expectedKeyType); - } else { - key = Key.decodeKey(argument, expectedKeyType); - } - - return key.key; - } + public KeyField(String key, boolean required) { + super(key, required); + } + + byte[] readKey(String argument, String expectedKeyType) throws IOException, InvalidKeyException { + Key key; + + // Try to open a file with that name + File keyFile = new File(argument); + if (keyFile.isFile()) { + key = DataUtils.readKeyFile(keyFile, expectedKeyType); + } else { + key = Key.decodeKey(argument, expectedKeyType); + } + + return key.key; + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java index 9e87e6b..8ce45ea 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java @@ -28,15 +28,15 @@ import ch.threema.apitool.exceptions.InvalidKeyException; public class PrivateKeyField extends KeyField { - public PrivateKeyField(String key, boolean required) { - super(key, required); - } + public PrivateKeyField(String key, boolean required) { + super(key, required); + } - public byte[] getValue() throws InvalidKeyException { - try { - return this.readKey(this.value, Key.KeyType.PRIVATE); - } catch (Exception e) { - throw new InvalidKeyException("invalid private key"); - } - } + public byte[] getValue() throws InvalidKeyException { + try { + return this.readKey(this.value, Key.KeyType.PRIVATE); + } catch (Exception e) { + throw new InvalidKeyException("invalid private key"); + } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java index 42b8c5a..ead0db3 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java @@ -28,14 +28,15 @@ import ch.threema.apitool.exceptions.InvalidKeyException; public class PublicKeyField extends KeyField { - public PublicKeyField(String key, boolean required) { - super(key, required); - } - public byte[] getValue() throws InvalidKeyException { - try { - return this.readKey(this.value, Key.KeyType.PUBLIC); - } catch (Exception e) { - throw new InvalidKeyException("invalid public key"); - } - } + public PublicKeyField(String key, boolean required) { + super(key, required); + } + + public byte[] getValue() throws InvalidKeyException { + try { + return this.readKey(this.value, Key.KeyType.PUBLIC); + } catch (Exception e) { + throw new InvalidKeyException("invalid public key"); + } + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java index 271e0fc..9d73bae 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java @@ -25,11 +25,11 @@ package ch.threema.apitool.console.commands.fields; public class TextField extends Field { - public TextField(String key, boolean required) { - super(key, required); - } + public TextField(String key, boolean required) { + super(key, required); + } - public String getValue() { - return this.value; - } + public String getValue() { + return this.value; + } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java index a874cc0..31f3e6a 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java @@ -25,11 +25,11 @@ package ch.threema.apitool.console.commands.fields; public class ThreemaIDField extends Field { - public ThreemaIDField(String key, boolean required) { - super(key, required); - } + public ThreemaIDField(String key, boolean required) { + super(key, required); + } - public String getValue() { - return this.value; - } + public String getValue() { + return this.value; + } } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java b/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java index 1b4099d..97a6d1f 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java @@ -24,10 +24,8 @@ package ch.threema.apitool.exceptions; -/** - * Exception that gets thrown if a message has a bad/illegal format after it has been decrypted. - */ +/** Exception that gets thrown if a message has a bad/illegal format after it has been decrypted. */ public class BadMessageException extends MessageParseException { - private static final long serialVersionUID = 4812096297596964107L; + private static final long serialVersionUID = 4812096297596964107L; } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java b/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java index 1992d37..d45d351 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java @@ -25,9 +25,10 @@ package ch.threema.apitool.exceptions; /** - * Exception that gets thrown when decryption fails (because the keys are incorrect, or the data is corrupted). + * Exception that gets thrown when decryption fails (because the keys are incorrect, or the data is + * corrupted). */ public class DecryptionFailedException extends MessageParseException { - private static final long serialVersionUID = 2523453399446307538L; + private static final long serialVersionUID = 2523453399446307538L; } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java b/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java index 3aa47be..d1cb48f 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java @@ -24,13 +24,11 @@ package ch.threema.apitool.exceptions; -/** - * Exception that gets thrown on a illegal call. - */ +/** Exception that gets thrown on a illegal call. */ public class InvalidCommandFieldValueException extends Exception { - private static final long serialVersionUID = -6293436769165519745L; + private static final long serialVersionUID = -6293436769165519745L; - public InvalidCommandFieldValueException(String message) { - super(message); - } + public InvalidCommandFieldValueException(String message) { + super(message); + } } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java b/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java index f213b55..2e7e909 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java @@ -24,13 +24,11 @@ package ch.threema.apitool.exceptions; -/** - * Exception that gets thrown when an invalid key has been specified (e.g. wrong length). - */ +/** Exception that gets thrown when an invalid key has been specified (e.g. wrong length). */ public class InvalidKeyException extends Exception { - private static final long serialVersionUID = 7585373757748175309L; + private static final long serialVersionUID = 7585373757748175309L; - public InvalidKeyException(String s) { - super(s); - } + public InvalidKeyException(String s) { + super(s); + } } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java b/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java index aaed853..5709e71 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java @@ -29,5 +29,5 @@ */ public class MessageParseException extends Exception { - private static final long serialVersionUID = 6829629439344637547L; + private static final long serialVersionUID = 6829629439344637547L; } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java b/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java index 9927a0f..bbfe457 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java @@ -24,10 +24,8 @@ package ch.threema.apitool.exceptions; -/** - * Exception that gets thrown on a illegal call. - */ +/** Exception that gets thrown on a illegal call. */ public class NotAllowedException extends Exception { - private static final long serialVersionUID = 3032360799153840206L; + private static final long serialVersionUID = 3032360799153840206L; } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java b/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java index 559eabe..cd54309 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java @@ -24,13 +24,11 @@ package ch.threema.apitool.exceptions; -/** - * Exception that gets thrown on a illegal call. - */ +/** Exception that gets thrown on a illegal call. */ public class RequiredCommandFieldMissingException extends Exception { - private static final long serialVersionUID = 2273462399743084938L; + private static final long serialVersionUID = 2273462399743084938L; - public RequiredCommandFieldMissingException(String message) { - super(message); - } + public RequiredCommandFieldMissingException(String message) { + super(message); + } } diff --git a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java b/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java index 76c9260..fc6f6fe 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java +++ b/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java @@ -25,10 +25,10 @@ package ch.threema.apitool.exceptions; /** - * Exception that gets thrown when an attempt has been made to decrypt a message - * of a type that is not supported by this library. + * Exception that gets thrown when an attempt has been made to decrypt a message of a type that is + * not supported by this library. */ public class UnsupportedMessageTypeException extends MessageParseException { - private static final long serialVersionUID = -686063411249892256L; + private static final long serialVersionUID = -686063411249892256L; } diff --git a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java b/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java index c8a2d7f..e6a442e 100644 --- a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java +++ b/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java @@ -50,268 +50,282 @@ import ch.threema.apitool.results.EncryptResult; import ch.threema.apitool.results.UploadResult; -/** - * Helper to handle Threema end-to-end encryption. - */ +/** Helper to handle Threema end-to-end encryption. */ public class E2EHelper { - private final APIConnector apiConnector; - private final byte[] privateKey; - - public class ReceiveMessageResult { - private final String messageId; - private final ThreemaMessage message; - protected List files = new ArrayList<>(); - protected List errors = new ArrayList<>(); - - public ReceiveMessageResult(String messageId, ThreemaMessage message) { - this.messageId = messageId; - this.message = message; - } - - public List getFiles() { - return this.files; - } - - public List getErrors() { - return this.errors; - } - - public String getMessageId() { - return messageId; - } - - public ThreemaMessage getMessage() { - return message; - } + private final APIConnector apiConnector; + private final byte[] privateKey; + + public class ReceiveMessageResult { + private final String messageId; + private final ThreemaMessage message; + protected List files = new ArrayList<>(); + protected List errors = new ArrayList<>(); + + public ReceiveMessageResult(String messageId, ThreemaMessage message) { + this.messageId = messageId; + this.message = message; + } + + public List getFiles() { + return this.files; + } + + public List getErrors() { + return this.errors; + } + + public String getMessageId() { + return messageId; + } + + public ThreemaMessage getMessage() { + return message; + } + } + + public E2EHelper(APIConnector apiConnector, byte[] privateKey) { + this.apiConnector = apiConnector; + this.privateKey = privateKey; + } + + /** + * Encrypt a text message and send it to the given recipient. + * + * @param threemaId target Threema ID + * @param text the text to send + * @return generated message ID + */ + public String sendTextMessage(String threemaId, String text) throws Exception { + // fetch public key + byte[] publicKey = this.apiConnector.lookupKey(threemaId); + + if (publicKey == null) { + throw new Exception("invalid threema id"); + } + EncryptResult res = CryptTool.encryptTextMessage(text, this.privateKey, publicKey); + + return this.apiConnector.sendE2EMessage(threemaId, res.getNonce(), res.getResult()); + } + + /** + * Encrypt an image message and send it to the given recipient. + * + * @param threemaId target Threema ID + * @param imageFilePath path to read image data from + * @return generated message ID + * @throws NotAllowedException + * @throws IOException + * @throws InvalidKeyException + */ + public String sendImageMessage(String threemaId, String imageFilePath) + throws NotAllowedException, IOException, InvalidKeyException { + // fetch public key + byte[] publicKey = this.apiConnector.lookupKey(threemaId); + + if (publicKey == null) { + throw new InvalidKeyException("invalid threema id"); + } + + // check capability of a key + CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); + if (capabilityResult == null || !capabilityResult.canImage()) { + throw new NotAllowedException(); + } + + byte[] fileData = Files.readAllBytes(Paths.get(imageFilePath)); + if (fileData == null) { + throw new IOException("invalid file"); + } + + // encrypt the image + EncryptResult encryptResult = CryptTool.encrypt(fileData, this.privateKey, publicKey); + + // upload the image + UploadResult uploadResult = apiConnector.uploadFile(encryptResult); + + if (!uploadResult.isSuccess()) { + throw new IOException( + "could not upload file (upload response " + uploadResult.getResponseCode() + ")"); + } + + // send it + EncryptResult imageMessage = + CryptTool.encryptImageMessage(encryptResult, uploadResult, privateKey, publicKey); + + return apiConnector.sendE2EMessage( + threemaId, imageMessage.getNonce(), imageMessage.getResult()); + } + + /** + * Encrypt a file message and send it to the given recipient. The thumbnailMessagePath can be + * null. + * + * @param threemaId target Threema ID + * @param fileMessageFile the file to be sent + * @param thumbnailMessagePath file for thumbnail; if not set, no thumbnail will be sent + * @return generated message ID + * @throws InvalidKeyException + * @throws IOException + * @throws NotAllowedException + */ + public String sendFileMessage(String threemaId, File fileMessageFile, File thumbnailMessagePath) + throws InvalidKeyException, IOException, NotAllowedException { + // fetch public key + byte[] publicKey = this.apiConnector.lookupKey(threemaId); + + if (publicKey == null) { + throw new InvalidKeyException("invalid threema id"); + } + + // check capability of a key + CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); + if (capabilityResult == null || !capabilityResult.canImage()) { + throw new NotAllowedException(); + } + + if (!fileMessageFile.isFile()) { + throw new IOException("invalid file"); } - public E2EHelper(APIConnector apiConnector, byte[] privateKey) { - this.apiConnector = apiConnector; - this.privateKey = privateKey; + byte[] fileData = this.readFile(fileMessageFile); + + if (fileData == null) { + throw new IOException("invalid file"); } - /** - * Encrypt a text message and send it to the given recipient. - * - * @param threemaId target Threema ID - * @param text the text to send - * @return generated message ID - */ - public String sendTextMessage(String threemaId, String text) throws Exception { - // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); - - if (publicKey == null) { - throw new Exception("invalid threema id"); - } - EncryptResult res = CryptTool.encryptTextMessage(text, this.privateKey, publicKey); - - return this.apiConnector.sendE2EMessage(threemaId, res.getNonce(), res.getResult()); + // encrypt the image + EncryptResult encryptResult = CryptTool.encryptFileData(fileData); + + // upload the image + UploadResult uploadResult = apiConnector.uploadFile(encryptResult); + + if (!uploadResult.isSuccess()) { + throw new IOException( + "could not upload file (upload response " + uploadResult.getResponseCode() + ")"); } - /** - * Encrypt an image message and send it to the given recipient. - * - * @param threemaId target Threema ID - * @param imageFilePath path to read image data from - * @return generated message ID - * @throws NotAllowedException - * @throws IOException - * @throws InvalidKeyException - */ - public String sendImageMessage(String threemaId, String imageFilePath) - throws NotAllowedException, IOException, InvalidKeyException { - // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); - - if (publicKey == null) { - throw new InvalidKeyException("invalid threema id"); - } - - // check capability of a key - CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); - if (capabilityResult == null || !capabilityResult.canImage()) { - throw new NotAllowedException(); - } - - byte[] fileData = Files.readAllBytes(Paths.get(imageFilePath)); - if (fileData == null) { - throw new IOException("invalid file"); - } - - // encrypt the image - EncryptResult encryptResult = CryptTool.encrypt(fileData, this.privateKey, publicKey); - - // upload the image - UploadResult uploadResult = apiConnector.uploadFile(encryptResult); - - if (!uploadResult.isSuccess()) { - throw new IOException("could not upload file (upload response " + uploadResult.getResponseCode() + ")"); - } - - // send it - EncryptResult imageMessage = CryptTool.encryptImageMessage(encryptResult, uploadResult, privateKey, publicKey); - - return apiConnector.sendE2EMessage(threemaId, imageMessage.getNonce(), imageMessage.getResult()); + UploadResult uploadResultThumbnail = null; + + if (thumbnailMessagePath != null && thumbnailMessagePath.isFile()) { + byte[] thumbnailData = this.readFile(thumbnailMessagePath); + if (thumbnailData == null) { + throw new IOException("invalid thumbnail file"); + } + + // encrypt the thumbnail + EncryptResult encryptResultThumbnail = + CryptTool.encryptFileThumbnailData(fileData, encryptResult.getSecret()); + + // upload the thumbnail + uploadResultThumbnail = this.apiConnector.uploadFile(encryptResultThumbnail); } - /** - * Encrypt a file message and send it to the given recipient. - * The thumbnailMessagePath can be null. - * - * @param threemaId target Threema ID - * @param fileMessageFile the file to be sent - * @param thumbnailMessagePath file for thumbnail; if not set, no thumbnail will be sent - * @return generated message ID - * @throws InvalidKeyException - * @throws IOException - * @throws NotAllowedException - */ - public String sendFileMessage(String threemaId, File fileMessageFile, File thumbnailMessagePath) - throws InvalidKeyException, IOException, NotAllowedException { - // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); - - if (publicKey == null) { - throw new InvalidKeyException("invalid threema id"); - } - - // check capability of a key - CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); - if (capabilityResult == null || !capabilityResult.canImage()) { - throw new NotAllowedException(); - } - - if (!fileMessageFile.isFile()) { - throw new IOException("invalid file"); - } - - byte[] fileData = this.readFile(fileMessageFile); - - if (fileData == null) { - throw new IOException("invalid file"); - } - - // encrypt the image - EncryptResult encryptResult = CryptTool.encryptFileData(fileData); - - // upload the image - UploadResult uploadResult = apiConnector.uploadFile(encryptResult); - - if (!uploadResult.isSuccess()) { - throw new IOException("could not upload file (upload response " + uploadResult.getResponseCode() + ")"); - } - - UploadResult uploadResultThumbnail = null; - - if (thumbnailMessagePath != null && thumbnailMessagePath.isFile()) { - byte[] thumbnailData = this.readFile(thumbnailMessagePath); - if (thumbnailData == null) { - throw new IOException("invalid thumbnail file"); - } - - // encrypt the thumbnail - EncryptResult encryptResultThumbnail = CryptTool.encryptFileThumbnailData(fileData, - encryptResult.getSecret()); - - // upload the thumbnail - uploadResultThumbnail = this.apiConnector.uploadFile(encryptResultThumbnail); - } - - // send it - EncryptResult fileMessage = CryptTool.encryptFileMessage(encryptResult, uploadResult, - Files.probeContentType(fileMessageFile.toPath()), fileMessageFile.getName(), - (int) fileMessageFile.length(), uploadResultThumbnail, privateKey, publicKey); - - return this.apiConnector.sendE2EMessage(threemaId, fileMessage.getNonce(), fileMessage.getResult()); + // send it + EncryptResult fileMessage = + CryptTool.encryptFileMessage( + encryptResult, + uploadResult, + Files.probeContentType(fileMessageFile.toPath()), + fileMessageFile.getName(), + (int) fileMessageFile.length(), + uploadResultThumbnail, + privateKey, + publicKey); + + return this.apiConnector.sendE2EMessage( + threemaId, fileMessage.getNonce(), fileMessage.getResult()); + } + + /** + * Decrypt a Message and download the blobs of the Message (e.g. image or file) + * + * @param threemaId Threema ID of the sender + * @param messageId Message ID + * @param box Encrypted box data of the file/image message + * @param nonce Nonce that was used for message encryption + * @param outputFolder Output folder for storing decrypted images/files + * @return result of message reception + * @throws IOException + * @throws InvalidKeyException + * @throws MessageParseException + */ + public ReceiveMessageResult receiveMessage( + String threemaId, String messageId, byte[] box, byte[] nonce, Path outputFolder) + throws IOException, InvalidKeyException, MessageParseException { + // fetch public key + byte[] publicKey = this.apiConnector.lookupKey(threemaId); + + if (publicKey == null) { + throw new InvalidKeyException("invalid threema id"); } - /** - * Decrypt a Message and download the blobs of the Message (e.g. image or file) - * - * @param threemaId Threema ID of the sender - * @param messageId Message ID - * @param box Encrypted box data of the file/image message - * @param nonce Nonce that was used for message encryption - * @param outputFolder Output folder for storing decrypted images/files - * @return result of message reception - * @throws IOException - * @throws InvalidKeyException - * @throws MessageParseException - */ - public ReceiveMessageResult receiveMessage(String threemaId, String messageId, byte[] box, byte[] nonce, - Path outputFolder) throws IOException, InvalidKeyException, MessageParseException { - // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); - - if (publicKey == null) { - throw new InvalidKeyException("invalid threema id"); - } - - ThreemaMessage message = CryptTool.decryptMessage(box, this.privateKey, publicKey, nonce); - if (message == null) { - return null; - } - - ReceiveMessageResult result = new ReceiveMessageResult(messageId, message); - - if (message instanceof ImageMessage) { - // download image - ImageMessage imageMessage = (ImageMessage) message; - byte[] fileData = this.apiConnector.downloadFile(imageMessage.getBlobId()); - - if (fileData == null) { - throw new MessageParseException(); - } - - byte[] decryptedFileContent = CryptTool.decrypt(fileData, privateKey, publicKey, imageMessage.getNonce()); - File imageFile = new File(outputFolder.toString() + "/" + messageId + ".jpg"); - FileOutputStream fos = new FileOutputStream(imageFile); - fos.write(decryptedFileContent); - fos.close(); - - result.files.add(imageFile); - } else if (message instanceof FileMessage) { - // download file - FileMessage fileMessage = (FileMessage) message; - byte[] fileData = this.apiConnector.downloadFile(fileMessage.getBlobId()); - - byte[] decryptedFileData = CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey()); - File file = new File(outputFolder.toString() + "/" + messageId + "-" + fileMessage.getFileName()); - FileOutputStream fos = new FileOutputStream(file); - fos.write(decryptedFileData); - fos.close(); - - result.files.add(file); - - if (fileMessage.getThumbnailBlobId() != null) { - byte[] thumbnailData = this.apiConnector.downloadFile(fileMessage.getThumbnailBlobId()); - - byte[] decryptedThumbnailData = CryptTool.decryptFileThumbnailData(thumbnailData, - fileMessage.getEncryptionKey()); - File thumbnailFile = new File(outputFolder.toString() + "/" + messageId + "-thumbnail.jpg"); - fos = new FileOutputStream(thumbnailFile); - fos.write(decryptedThumbnailData); - fos.close(); - - result.files.add(thumbnailFile); - } - } - - return result; + ThreemaMessage message = CryptTool.decryptMessage(box, this.privateKey, publicKey, nonce); + if (message == null) { + return null; } - /** - * Read file data from file - store at offset in byte array for in-place encryption - * - * @param file input file - * @return file data with padding/offset for NaCl - * @throws IOException - */ - private byte[] readFile(File file) throws IOException { - int fileLength = (int) file.length(); - byte[] fileData = new byte[fileLength + NaCl.BOXOVERHEAD]; - IOUtils.readFully(new FileInputStream(file), fileData, NaCl.BOXOVERHEAD, fileLength); - return fileData; + ReceiveMessageResult result = new ReceiveMessageResult(messageId, message); + + if (message instanceof ImageMessage) { + // download image + ImageMessage imageMessage = (ImageMessage) message; + byte[] fileData = this.apiConnector.downloadFile(imageMessage.getBlobId()); + + if (fileData == null) { + throw new MessageParseException(); + } + + byte[] decryptedFileContent = + CryptTool.decrypt(fileData, privateKey, publicKey, imageMessage.getNonce()); + File imageFile = new File(outputFolder.toString() + "/" + messageId + ".jpg"); + FileOutputStream fos = new FileOutputStream(imageFile); + fos.write(decryptedFileContent); + fos.close(); + + result.files.add(imageFile); + } else if (message instanceof FileMessage) { + // download file + FileMessage fileMessage = (FileMessage) message; + byte[] fileData = this.apiConnector.downloadFile(fileMessage.getBlobId()); + + byte[] decryptedFileData = + CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey()); + File file = + new File(outputFolder.toString() + "/" + messageId + "-" + fileMessage.getFileName()); + FileOutputStream fos = new FileOutputStream(file); + fos.write(decryptedFileData); + fos.close(); + + result.files.add(file); + + if (fileMessage.getThumbnailBlobId() != null) { + byte[] thumbnailData = this.apiConnector.downloadFile(fileMessage.getThumbnailBlobId()); + + byte[] decryptedThumbnailData = + CryptTool.decryptFileThumbnailData(thumbnailData, fileMessage.getEncryptionKey()); + File thumbnailFile = new File(outputFolder.toString() + "/" + messageId + "-thumbnail.jpg"); + fos = new FileOutputStream(thumbnailFile); + fos.write(decryptedThumbnailData); + fos.close(); + + result.files.add(thumbnailFile); + } } + + return result; + } + + /** + * Read file data from file - store at offset in byte array for in-place encryption + * + * @param file input file + * @return file data with padding/offset for NaCl + * @throws IOException + */ + private byte[] readFile(File file) throws IOException { + int fileLength = (int) file.length(); + byte[] fileData = new byte[fileLength + NaCl.BOXOVERHEAD]; + IOUtils.readFully(new FileInputStream(file), fileData, NaCl.BOXOVERHEAD, fileLength); + return fileData; + } } diff --git a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java b/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java index 6f79f76..e0b16c7 100644 --- a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java +++ b/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java @@ -29,85 +29,85 @@ import java.util.List; /** - * A delivery receipt message that can be sent/received with end-to-end encryption via Threema. - * Each delivery receipt message confirms the receipt of one or multiple regular messages. + * A delivery receipt message that can be sent/received with end-to-end encryption via Threema. Each + * delivery receipt message confirms the receipt of one or multiple regular messages. */ public class DeliveryReceipt extends ThreemaMessage { - public static final int TYPE_CODE = 0x80; - - private final Type receiptType; - private final List ackedMessageIds; - - public DeliveryReceipt(Type receiptType, List ackedMessageIds) { - this.receiptType = receiptType; - this.ackedMessageIds = ackedMessageIds; - } - - public Type getReceiptType() { - return receiptType; - } - - public List getAckedMessageIds() { - return ackedMessageIds; - } - - @Override - public int getTypeCode() { - return TYPE_CODE; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Delivery receipt ("); - sb.append(receiptType); - sb.append("): "); - int i = 0; - for (MessageId messageId : ackedMessageIds) { - if (i != 0) - sb.append(", "); - sb.append(messageId); - i++; - } - return sb.toString(); - } - - /** - * A delivery receipt type. The following types are defined: - * - *
    - *
  • RECEIVED: the message has been received and decrypted on the recipient's device
  • - *
  • READ: the message has been shown to the user in the chat view - * (note that this status can be disabled)
  • - *
  • USER_ACK: the user has explicitly acknowledged the message (usually by - * long-pressing it and choosing the "acknowledge" option)
  • - *
- */ - public enum Type { - RECEIVED(1), READ(2), USER_ACK(3); - - private final int code; - - Type(int code) { - this.code = code; - } - - public int getCode() { - return code; - } - - public static Type get(int code) { - for (Type t : values()) { - if (t.code == code) - return t; - } - return null; - } - } - - @Override - public byte[] getData() { - //Not implemented yet - return new byte[0]; - } + public static final int TYPE_CODE = 0x80; + + private final Type receiptType; + private final List ackedMessageIds; + + public DeliveryReceipt(Type receiptType, List ackedMessageIds) { + this.receiptType = receiptType; + this.ackedMessageIds = ackedMessageIds; + } + + public Type getReceiptType() { + return receiptType; + } + + public List getAckedMessageIds() { + return ackedMessageIds; + } + + @Override + public int getTypeCode() { + return TYPE_CODE; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Delivery receipt ("); + sb.append(receiptType); + sb.append("): "); + int i = 0; + for (MessageId messageId : ackedMessageIds) { + if (i != 0) sb.append(", "); + sb.append(messageId); + i++; + } + return sb.toString(); + } + + /** + * A delivery receipt type. The following types are defined: + * + *
    + *
  • RECEIVED: the message has been received and decrypted on the recipient's device + *
  • READ: the message has been shown to the user in the chat view (note that this status can + * be disabled) + *
  • USER_ACK: the user has explicitly acknowledged the message (usually by long-pressing it + * and choosing the "acknowledge" option) + *
+ */ + public enum Type { + RECEIVED(1), + READ(2), + USER_ACK(3); + + private final int code; + + Type(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static Type get(int code) { + for (Type t : values()) { + if (t.code == code) return t; + } + return null; + } + } + + @Override + public byte[] getData() { + // Not implemented yet + return new byte[0]; + } } diff --git a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java b/source/src/main/java/ch/threema/apitool/messages/FileMessage.java index f630c08..7d7ab92 100644 --- a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java +++ b/source/src/main/java/ch/threema/apitool/messages/FileMessage.java @@ -33,119 +33,127 @@ import ch.threema.apitool.DataUtils; import ch.threema.apitool.exceptions.BadMessageException; -/** - * A file message that can be sent/received with end-to-end encryption via Threema. - */ +/** A file message that can be sent/received with end-to-end encryption via Threema. */ public class FileMessage extends ThreemaMessage { - private final static String KEY_BLOB_ID = "b"; - private final static String KEY_THUMBNAIL_BLOB_ID = "t"; - private final static String KEY_ENCRYPTION_KEY = "k"; - private final static String KEY_MIME_TYPE = "m"; - private final static String KEY_FILE_NAME = "n"; - private final static String KEY_FILE_SIZE = "s"; - private final static String KEY_TYPE = "i"; - - public static final int TYPE_CODE = 0x17; - - private final byte[] blobId; - private final byte[] encryptionKey; - private final String mimeType; - private final String fileName; - private final int fileSize; - private final byte[] thumbnailBlobId; - - public FileMessage(byte[] blobId, byte[] encryptionKey, String mimeType, String fileName, int fileSize, - byte[] thumbnailBlobId) { - this.blobId = blobId; - this.encryptionKey = encryptionKey; - this.mimeType = mimeType; - this.fileName = fileName; - this.fileSize = fileSize; - this.thumbnailBlobId = thumbnailBlobId; - } - - public byte[] getBlobId() { - return this.blobId; - } - - public byte[] getEncryptionKey() { - return this.encryptionKey; - } - - public String getMimeType() { - return this.mimeType; - } - - public String getFileName() { - return this.fileName; + private static final String KEY_BLOB_ID = "b"; + private static final String KEY_THUMBNAIL_BLOB_ID = "t"; + private static final String KEY_ENCRYPTION_KEY = "k"; + private static final String KEY_MIME_TYPE = "m"; + private static final String KEY_FILE_NAME = "n"; + private static final String KEY_FILE_SIZE = "s"; + private static final String KEY_TYPE = "i"; + + public static final int TYPE_CODE = 0x17; + + private final byte[] blobId; + private final byte[] encryptionKey; + private final String mimeType; + private final String fileName; + private final int fileSize; + private final byte[] thumbnailBlobId; + + public FileMessage( + byte[] blobId, + byte[] encryptionKey, + String mimeType, + String fileName, + int fileSize, + byte[] thumbnailBlobId) { + this.blobId = blobId; + this.encryptionKey = encryptionKey; + this.mimeType = mimeType; + this.fileName = fileName; + this.fileSize = fileSize; + this.thumbnailBlobId = thumbnailBlobId; + } + + public byte[] getBlobId() { + return this.blobId; + } + + public byte[] getEncryptionKey() { + return this.encryptionKey; + } + + public String getMimeType() { + return this.mimeType; + } + + public String getFileName() { + return this.fileName; + } + + public int getFileSize() { + return this.fileSize; + } + + public byte[] getThumbnailBlobId() { + return this.thumbnailBlobId; + } + + @Override + public int getTypeCode() { + return TYPE_CODE; + } + + @Override + public String toString() { + return "file message " + this.fileName; + } + + @Override + public byte[] getData() throws BadMessageException { + JsonObject o = new JsonObject(); + try { + o.addProperty(KEY_BLOB_ID, DataUtils.byteArrayToHexString(this.blobId)); + o.addProperty( + KEY_THUMBNAIL_BLOB_ID, + this.thumbnailBlobId != null + ? DataUtils.byteArrayToHexString(this.thumbnailBlobId) + : null); + o.addProperty(KEY_ENCRYPTION_KEY, DataUtils.byteArrayToHexString(this.encryptionKey)); + o.addProperty(KEY_MIME_TYPE, this.mimeType); + o.addProperty(KEY_FILE_NAME, this.fileName); + o.addProperty(KEY_FILE_SIZE, this.fileSize); + o.addProperty(KEY_TYPE, 0); + } catch (Exception e) { + throw new BadMessageException(); } - public int getFileSize() { - return this.fileSize; + try { + return o.toString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + return null; } - - public byte[] getThumbnailBlobId() { - return this.thumbnailBlobId; - } - - @Override - public int getTypeCode() { - return TYPE_CODE; - } - - @Override - public String toString() { - return "file message " + this.fileName; - } - - @Override - public byte[] getData() throws BadMessageException { - JsonObject o = new JsonObject(); - try { - o.addProperty(KEY_BLOB_ID, DataUtils.byteArrayToHexString(this.blobId)); - o.addProperty(KEY_THUMBNAIL_BLOB_ID, - this.thumbnailBlobId != null ? DataUtils.byteArrayToHexString(this.thumbnailBlobId) : null); - o.addProperty(KEY_ENCRYPTION_KEY, DataUtils.byteArrayToHexString(this.encryptionKey)); - o.addProperty(KEY_MIME_TYPE, this.mimeType); - o.addProperty(KEY_FILE_NAME, this.fileName); - o.addProperty(KEY_FILE_SIZE, this.fileSize); - o.addProperty(KEY_TYPE, 0); - } catch (Exception e) { - throw new BadMessageException(); - } - - try { - return o.toString().getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } - } - - public static FileMessage fromString(String json) throws BadMessageException { - try { - JsonObject o = new Gson().fromJson(json, JsonObject.class); - byte[] encryptionKey = DataUtils.hexStringToByteArray(o.get(KEY_ENCRYPTION_KEY).getAsString()); - String mimeType = o.get(KEY_MIME_TYPE).getAsString(); - int fileSize = o.get(KEY_FILE_SIZE).getAsInt(); - byte[] blobId = DataUtils.hexStringToByteArray(o.get(KEY_BLOB_ID).getAsString()); - - String fileName; - byte[] thumbnailBlobId = null; - - // optional field - if (o.has(KEY_THUMBNAIL_BLOB_ID)) { - thumbnailBlobId = DataUtils.hexStringToByteArray(o.get(KEY_THUMBNAIL_BLOB_ID).getAsString()); - } - - if (o.has(KEY_FILE_NAME)) { - fileName = o.get(KEY_FILE_NAME).getAsString(); - } else { - fileName = "unnamed"; - } - - return new FileMessage(blobId, encryptionKey, mimeType, fileName, fileSize, thumbnailBlobId); - } catch (JsonSyntaxException e) { - throw new BadMessageException(); - } + } + + public static FileMessage fromString(String json) throws BadMessageException { + try { + JsonObject o = new Gson().fromJson(json, JsonObject.class); + byte[] encryptionKey = + DataUtils.hexStringToByteArray(o.get(KEY_ENCRYPTION_KEY).getAsString()); + String mimeType = o.get(KEY_MIME_TYPE).getAsString(); + int fileSize = o.get(KEY_FILE_SIZE).getAsInt(); + byte[] blobId = DataUtils.hexStringToByteArray(o.get(KEY_BLOB_ID).getAsString()); + + String fileName; + byte[] thumbnailBlobId = null; + + // optional field + if (o.has(KEY_THUMBNAIL_BLOB_ID)) { + thumbnailBlobId = + DataUtils.hexStringToByteArray(o.get(KEY_THUMBNAIL_BLOB_ID).getAsString()); + } + + if (o.has(KEY_FILE_NAME)) { + fileName = o.get(KEY_FILE_NAME).getAsString(); + } else { + fileName = "unnamed"; + } + + return new FileMessage(blobId, encryptionKey, mimeType, fileName, fileSize, thumbnailBlobId); + } catch (JsonSyntaxException e) { + throw new BadMessageException(); } + } } diff --git a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java b/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java index 40e6369..e58b667 100644 --- a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java +++ b/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java @@ -28,60 +28,54 @@ import com.neilalexander.jnacl.NaCl; import org.apache.commons.io.EndianUtils; -/** - * An image message that can be sent/received with end-to-end encryption via Threema. - */ +/** An image message that can be sent/received with end-to-end encryption via Threema. */ public class ImageMessage extends ThreemaMessage { - public static final int TYPE_CODE = 0x02; - private final byte[] blobId; - private final int size; - private final byte[] nonce; - - - public ImageMessage(byte[] blobId, int size, byte[] nonce) { - - this.blobId = blobId; - this.size = size; - this.nonce = nonce; - } - - public byte[] getBlobId() { - return this.blobId; - } - - - public int getSize() { - return this.size; - } - - - public byte[] getNonce() { - return this.nonce; - } - - @Override - public int getTypeCode() { - return TYPE_CODE; - } - - @Override - public String toString() { - return "blob " + DataUtils.byteArrayToHexString(this.blobId); - } - - @Override - public byte[] getData() { - byte[] data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES]; - int pos = 0; - System.arraycopy(this.blobId, 0, data, pos, BLOB_ID_LEN); - pos += BLOB_ID_LEN; - - EndianUtils.writeSwappedInteger(data, pos, this.size); - pos += 4; - - System.arraycopy(this.nonce, 0, data, pos, NaCl.NONCEBYTES); - return data; - - } + public static final int TYPE_CODE = 0x02; + private final byte[] blobId; + private final int size; + private final byte[] nonce; + + public ImageMessage(byte[] blobId, int size, byte[] nonce) { + + this.blobId = blobId; + this.size = size; + this.nonce = nonce; + } + + public byte[] getBlobId() { + return this.blobId; + } + + public int getSize() { + return this.size; + } + + public byte[] getNonce() { + return this.nonce; + } + + @Override + public int getTypeCode() { + return TYPE_CODE; + } + + @Override + public String toString() { + return "blob " + DataUtils.byteArrayToHexString(this.blobId); + } + + @Override + public byte[] getData() { + byte[] data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES]; + int pos = 0; + System.arraycopy(this.blobId, 0, data, pos, BLOB_ID_LEN); + pos += BLOB_ID_LEN; + + EndianUtils.writeSwappedInteger(data, pos, this.size); + pos += 4; + + System.arraycopy(this.nonce, 0, data, pos, NaCl.NONCEBYTES); + return data; + } } diff --git a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java b/source/src/main/java/ch/threema/apitool/messages/TextMessage.java index 3a22e06..d1bf72f 100644 --- a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java +++ b/source/src/main/java/ch/threema/apitool/messages/TextMessage.java @@ -26,39 +26,37 @@ import java.io.UnsupportedEncodingException; -/** - * A text message that can be sent/received with end-to-end encryption via Threema. - */ +/** A text message that can be sent/received with end-to-end encryption via Threema. */ public class TextMessage extends ThreemaMessage { - public static final int TYPE_CODE = 0x01; + public static final int TYPE_CODE = 0x01; - private final String text; + private final String text; - public TextMessage(String text) { - this.text = text; - } + public TextMessage(String text) { + this.text = text; + } - public String getText() { - return text; - } + public String getText() { + return text; + } - @Override - public int getTypeCode() { - return TYPE_CODE; - } + @Override + public int getTypeCode() { + return TYPE_CODE; + } - @Override - public String toString() { - return text; - } + @Override + public String toString() { + return text; + } - @Override - public byte[] getData() { - try { - return text.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } - } + @Override + public byte[] getData() { + try { + return text.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + return null; + } + } } diff --git a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java b/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java index f96265b..85bd53c 100644 --- a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java +++ b/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java @@ -26,20 +26,14 @@ import ch.threema.apitool.exceptions.BadMessageException; -/** - * Abstract base class of messages that can be sent with end-to-end encryption via Threema. - */ +/** Abstract base class of messages that can be sent with end-to-end encryption via Threema. */ public abstract class ThreemaMessage { - public static final int BLOB_ID_LEN = 16; + public static final int BLOB_ID_LEN = 16; - /** - * @return The message's raw content - */ - public abstract byte[] getData() throws BadMessageException; + /** @return The message's raw content */ + public abstract byte[] getData() throws BadMessageException; - /** - * @return the message's type code - */ - public abstract int getTypeCode(); + /** @return the message's type code */ + public abstract int getTypeCode(); } diff --git a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java b/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java index 65284c9..04bcdc3 100644 --- a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java +++ b/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java @@ -24,83 +24,69 @@ package ch.threema.apitool.results; -/** - * Result of a capability lookup - */ +/** Result of a capability lookup */ public class CapabilityResult { - private final String key; - private final String[] capabilities; + private final String key; + private final String[] capabilities; - public CapabilityResult(String key, String[] capabilities) { - this.key = key; - this.capabilities = capabilities; - } + public CapabilityResult(String key, String[] capabilities) { + this.key = key; + this.capabilities = capabilities; + } - /** - * Get all capabilities as a string array. - */ - public String[] getCapabilities() { - return capabilities; - } + /** Get all capabilities as a string array. */ + public String[] getCapabilities() { + return capabilities; + } - /** - * Check whether the Threema ID can receive text - */ - public boolean canText() { - return this.can("text"); - } + /** Check whether the Threema ID can receive text */ + public boolean canText() { + return this.can("text"); + } - /** - * Check whether the Threema ID can receive images - */ - public boolean canImage() { - return this.can("image"); - } + /** Check whether the Threema ID can receive images */ + public boolean canImage() { + return this.can("image"); + } - /** - * Check whether the Threema ID can receive videos - */ - public boolean canVideo() { - return this.can("video"); - } + /** Check whether the Threema ID can receive videos */ + public boolean canVideo() { + return this.can("video"); + } - /** - * Check whether the Threema ID can receive audio - */ - public boolean canAudio() { - return this.can("audio"); - } + /** Check whether the Threema ID can receive audio */ + public boolean canAudio() { + return this.can("audio"); + } - /** - * Check whether the Threema ID can receive files - */ - public boolean canFile() { - return this.can("file"); - } + /** Check whether the Threema ID can receive files */ + public boolean canFile() { + return this.can("file"); + } - private boolean can(String key) { - for(String k: this.capabilities) { - if(k.equals(key)) { - return true; - } - } - return false; - } + private boolean can(String key) { + for (String k : this.capabilities) { + if (k.equals(key)) { + return true; + } + } + return false; + } - @Override - public String toString() { - StringBuilder b = new StringBuilder(); - b.append(this.key).append(": "); - for(int n = 0; n < this.capabilities.length; n++) { - if(n > 0) { - b.append(","); - } - b.append(this.capabilities[n]); - } - return b.toString(); - } + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(this.key).append(": "); + for (int n = 0; n < this.capabilities.length; n++) { + if (n > 0) { + b.append(","); + } + b.append(this.capabilities[n]); + } + return b.toString(); + } - public String getKey() { - return key; - } + public String getKey() { + return key; + } } diff --git a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java b/source/src/main/java/ch/threema/apitool/results/EncryptResult.java index 71fb91a..f20685c 100644 --- a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java +++ b/source/src/main/java/ch/threema/apitool/results/EncryptResult.java @@ -24,45 +24,35 @@ package ch.threema.apitool.results; -/** - * Result of a data encryption - */ +/** Result of a data encryption */ public class EncryptResult { - private final byte[] result; - private final byte[] secret; - private final byte[] nonce; + private final byte[] result; + private final byte[] secret; + private final byte[] nonce; - public EncryptResult(byte[] result, byte[] secret, byte[] nonce) { - this.result = result; - this.secret = secret; - this.nonce = nonce; - } + public EncryptResult(byte[] result, byte[] secret, byte[] nonce) { + this.result = result; + this.secret = secret; + this.nonce = nonce; + } - /** - * @return the encrypted data - */ - public byte[] getResult() { - return this.result; - } + /** @return the encrypted data */ + public byte[] getResult() { + return this.result; + } - /** - * @return the size (in bytes) of the encrypted data - */ - public int getSize() { - return this.result.length; - } + /** @return the size (in bytes) of the encrypted data */ + public int getSize() { + return this.result.length; + } - /** - * @return the nonce that was used for encryption - */ - public byte[] getNonce() { - return this.nonce; - } + /** @return the nonce that was used for encryption */ + public byte[] getNonce() { + return this.nonce; + } - /** - * @return the secret that was used for encryption (only for symmetric encryption, e.g. files) - */ - public byte[] getSecret() { - return secret; - } + /** @return the secret that was used for encryption (only for symmetric encryption, e.g. files) */ + public byte[] getSecret() { + return secret; + } } diff --git a/source/src/main/java/ch/threema/apitool/results/UploadResult.java b/source/src/main/java/ch/threema/apitool/results/UploadResult.java index 055b9ad..fae9d2d 100644 --- a/source/src/main/java/ch/threema/apitool/results/UploadResult.java +++ b/source/src/main/java/ch/threema/apitool/results/UploadResult.java @@ -24,36 +24,28 @@ package ch.threema.apitool.results; -/** - * Result of a file upload - */ +/** Result of a file upload */ public class UploadResult { - private final int responseCode; - private final byte[] blobId; + private final int responseCode; + private final byte[] blobId; - public UploadResult(int responseCode, byte[] blobId) { - this.responseCode = responseCode; - this.blobId = blobId; - } + public UploadResult(int responseCode, byte[] blobId) { + this.responseCode = responseCode; + this.blobId = blobId; + } - /** - * @return the blob ID that has been created - */ - public byte[] getBlobId() { - return this.blobId; - } + /** @return the blob ID that has been created */ + public byte[] getBlobId() { + return this.blobId; + } - /** - * @return whether the upload succeeded - */ - public boolean isSuccess() { - return this.responseCode == 200; - } + /** @return whether the upload succeeded */ + public boolean isSuccess() { + return this.responseCode == 200; + } - /** - * @return the response code of the upload - */ - public int getResponseCode() { - return this.responseCode; - } + /** @return the response code of the upload */ + public int getResponseCode() { + return this.responseCode; + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/NaCl.java b/source/src/main/java/com/neilalexander/jnacl/NaCl.java index 5cddd5d..dc7f20d 100644 --- a/source/src/main/java/com/neilalexander/jnacl/NaCl.java +++ b/source/src/main/java/com/neilalexander/jnacl/NaCl.java @@ -1,17 +1,17 @@ // // 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 @@ -36,295 +36,661 @@ import com.neilalexander.jnacl.crypto.xsalsa20poly1305; public class NaCl { - public static final int PUBLICKEYBYTES = 32; - public static final int SECRETKEYBYTES = 32; - public static final int BEFORENMBYTES = 32; - public static final int NONCEBYTES = 24; - public static final int ZEROBYTES = 32; - public static final int BOXZEROBYTES = 16; - public static final int BOXOVERHEAD = ZEROBYTES - BOXZEROBYTES; - public static final int SYMMKEYBYTES = 32; - public static final int STREAMKEYBYTES = 32; - - private final byte[] precomputed = new byte[BEFORENMBYTES]; - - /* Perform self test before anything else */ - static { - selfTest(); - } + public 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 NaCl(byte[] privatekey, byte[] publickey) { - if (privatekey.length != SECRETKEYBYTES) - throw new Error("Invalid private key length"); + private final byte[] precomputed = new byte[BEFORENMBYTES]; - if (publickey.length != PUBLICKEYBYTES) - throw new Error("Invalid public key length"); + /* Perform self test before anything else */ + static { + selfTest(); + } - curve25519xsalsa20poly1305.crypto_box_beforenm(this.precomputed, publickey, privatekey); - } + public NaCl(byte[] privatekey, byte[] publickey) { + if (privatekey.length != SECRETKEYBYTES) throw new Error("Invalid private key length"); - public NaCl(String privatekey, String publickey) { - this(getBinary(privatekey), getBinary(publickey)); - } + if (publickey.length != PUBLICKEYBYTES) throw new Error("Invalid public key length"); - public byte[] encrypt(byte[] input, byte[] nonce) { - return encrypt(input, input.length, nonce); - } + curve25519xsalsa20poly1305.crypto_box_beforenm(this.precomputed, publickey, privatekey); + } - public byte[] encrypt(byte[] input, int inputlength, byte[] nonce) { - if (nonce.length != NONCEBYTES) - throw new Error("Invalid nonce length"); + public NaCl(String privatekey, String publickey) { + this(getBinary(privatekey), getBinary(publickey)); + } - byte[] output = new byte[inputlength + BOXOVERHEAD]; - curve25519xsalsa20poly1305.crypto_box_afternm_nopad(output, 0, input, 0, input.length, nonce, this.precomputed); + public byte[] encrypt(byte[] input, byte[] nonce) { + return encrypt(input, input.length, nonce); + } - return output; - } + public byte[] encrypt(byte[] input, int inputlength, byte[] nonce) { + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - public byte[] decrypt(byte[] input, byte[] nonce) { - return decrypt(input, input.length, nonce); - } + byte[] output = new byte[inputlength + BOXOVERHEAD]; + curve25519xsalsa20poly1305.crypto_box_afternm_nopad( + output, 0, input, 0, input.length, nonce, this.precomputed); - public byte[] decrypt(byte[] input, int inputlength, byte[] nonce) { - if (nonce.length != NONCEBYTES) - throw new Error("Invalid nonce length"); + return output; + } - if (inputlength < BOXOVERHEAD) - return null; + public byte[] decrypt(byte[] input, byte[] nonce) { + return decrypt(input, input.length, nonce); + } - 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; + public byte[] decrypt(byte[] input, int inputlength, byte[] nonce) { + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - return output; - } + if (inputlength < BOXOVERHEAD) return null; - public static void genkeypair(byte[] publickey, byte[] privatekey) { - genkeypair(publickey, privatekey, 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, byte[] seed) { - SecureRandom random = new SecureRandom(); + public static void genkeypair(byte[] publickey, byte[] privatekey) { + genkeypair(publickey, privatekey, null); + } - random.nextBytes(privatekey); + public static void genkeypair(byte[] publickey, byte[] privatekey, byte[] seed) { + SecureRandom random = new SecureRandom(); - if (seed != null) { - if (seed.length != SECRETKEYBYTES) - throw new Error("Invalid seed length"); + random.nextBytes(privatekey); - for (int i = 0; i < SECRETKEYBYTES; i++) - privatekey[i] ^= seed[i]; - } + if (seed != null) { + if (seed.length != SECRETKEYBYTES) throw new Error("Invalid seed length"); - curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); + for (int i = 0; i < SECRETKEYBYTES; i++) privatekey[i] ^= seed[i]; } - public static byte[] derivePublicKey(byte[] privatekey) { - if (privatekey.length != SECRETKEYBYTES) - throw new Error("Invalid private key length"); + curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); + } - byte[] publickey = new byte[PUBLICKEYBYTES]; - curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); - return publickey; - } + public static byte[] derivePublicKey(byte[] privatekey) { + if (privatekey.length != SECRETKEYBYTES) throw new Error("Invalid private key length"); - public static byte[] symmetricEncryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != SYMMKEYBYTES) - throw new Error("Invalid symmetric key length"); + byte[] publickey = new byte[PUBLICKEYBYTES]; + curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); + return publickey; + } - if (nonce.length != NONCEBYTES) - throw new Error("Invalid nonce length"); + public static byte[] symmetricEncryptData(byte[] input, byte[] key, byte[] nonce) { + if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - byte[] output = new byte[input.length + BOXOVERHEAD]; - xsalsa20poly1305.crypto_secretbox_nopad(output, 0, input, 0, input.length, nonce, key); + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - return output; - } + byte[] output = new byte[input.length + BOXOVERHEAD]; + xsalsa20poly1305.crypto_secretbox_nopad(output, 0, input, 0, input.length, nonce, key); - /** - * 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) { + return output; + } - if (key.length != SYMMKEYBYTES) - throw new Error("Invalid symmetric key length"); + /** + * 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 (nonce.length != NONCEBYTES) - throw new Error("Invalid nonce length"); + if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - if (io.length < BOXOVERHEAD) - throw new Error("Invalid I/O length"); + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - xsalsa20poly1305.crypto_secretbox_nopad(io, 0, io, BOXOVERHEAD, io.length - BOXOVERHEAD, nonce, key); - } + if (io.length < BOXOVERHEAD) throw new Error("Invalid I/O length"); - public static byte[] symmetricDecryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != SYMMKEYBYTES) - throw new Error("Invalid symmetric key length"); + xsalsa20poly1305.crypto_secretbox_nopad( + io, 0, io, BOXOVERHEAD, io.length - BOXOVERHEAD, nonce, key); + } - if (nonce.length != NONCEBYTES) - throw new Error("Invalid nonce length"); + public static byte[] symmetricDecryptData(byte[] input, byte[] key, byte[] nonce) { + if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key 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; + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - return output; - } + byte[] output = new byte[input.length - BOXOVERHEAD]; + if (xsalsa20poly1305.crypto_secretbox_open_nopad(output, 0, input, 0, input.length, nonce, key) + != 0) return null; - /** - * 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; - } + return output; + } - public static byte[] streamCryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != STREAMKEYBYTES) - throw new Error("Invalid symmetric key length"); + /** + * 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"); - byte[] output = new byte[input.length]; - xsalsa20.crypto_stream_xor(output, input, input.length, nonce, key); + if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - return output; - } + if (io.length < BOXOVERHEAD) throw new Error("Invalid I/O length"); - public static byte[] getBinary(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; + if (xsalsa20poly1305.crypto_secretbox_open_nopad(io, 0, io, 0, io.length, nonce, key) != 0) + return false; - 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)); + /* zeroize last bytes */ + for (int i = io.length - BOXOVERHEAD; i < io.length; i++) io[i] = 0; - return data; - } + return true; + } - public static String asHex(byte[] buf) { - try (Formatter formatter = new Formatter()) { - for (byte b : buf) - formatter.format("%02x", b); - return formatter.toString(); - } - } + 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(int[] buf) { - try (Formatter formatter = new Formatter()) { - for (int b : buf) - formatter.format("%02x", b); - return formatter.toString(); - } + public static String asHex(byte[] buf) { + try (Formatter formatter = new Formatter()) { + for (byte 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)"); + public static String asHex(int[] buf) { + try (Formatter formatter = new Formatter()) { + for (int b : buf) formatter.format("%02x", b); + return formatter.toString(); } + } + + public static void selfTest() { + /* test vectors from tests/box.* in nacl distribution */ + byte alicepk[] = + new byte[] { + (byte) 0x85, + (byte) 0x20, + (byte) 0xf0, + (byte) 0x09, + (byte) 0x89, + (byte) 0x30, + (byte) 0xa7, + (byte) 0x54, + (byte) 0x74, + (byte) 0x8b, + (byte) 0x7d, + (byte) 0xdc, + (byte) 0xb4, + (byte) 0x3e, + (byte) 0xf7, + (byte) 0x5a, + (byte) 0x0d, + (byte) 0xbf, + (byte) 0x3a, + (byte) 0x0d, + (byte) 0x26, + (byte) 0x38, + (byte) 0x1a, + (byte) 0xf4, + (byte) 0xeb, + (byte) 0xa4, + (byte) 0xa9, + (byte) 0x8e, + (byte) 0xaa, + (byte) 0x9b, + (byte) 0x4e, + (byte) 0x6a + }; + + byte alicesk[] = { + (byte) 0x77, + (byte) 0x07, + (byte) 0x6d, + (byte) 0x0a, + (byte) 0x73, + (byte) 0x18, + (byte) 0xa5, + (byte) 0x7d, + (byte) 0x3c, + (byte) 0x16, + (byte) 0xc1, + (byte) 0x72, + (byte) 0x51, + (byte) 0xb2, + (byte) 0x66, + (byte) 0x45, + (byte) 0xdf, + (byte) 0x4c, + (byte) 0x2f, + (byte) 0x87, + (byte) 0xeb, + (byte) 0xc0, + (byte) 0x99, + (byte) 0x2a, + (byte) 0xb1, + (byte) 0x77, + (byte) 0xfb, + (byte) 0xa5, + (byte) 0x1d, + (byte) 0xb9, + (byte) 0x2c, + (byte) 0x2a + }; + + byte bobpk[] = { + (byte) 0xde, + (byte) 0x9e, + (byte) 0xdb, + (byte) 0x7d, + (byte) 0x7b, + (byte) 0x7d, + (byte) 0xc1, + (byte) 0xb4, + (byte) 0xd3, + (byte) 0x5b, + (byte) 0x61, + (byte) 0xc2, + (byte) 0xec, + (byte) 0xe4, + (byte) 0x35, + (byte) 0x37, + (byte) 0x3f, + (byte) 0x83, + (byte) 0x43, + (byte) 0xc8, + (byte) 0x5b, + (byte) 0x78, + (byte) 0x67, + (byte) 0x4d, + (byte) 0xad, + (byte) 0xfc, + (byte) 0x7e, + (byte) 0x14, + (byte) 0x6f, + (byte) 0x88, + (byte) 0x2b, + (byte) 0x4f + }; + + byte bobsk[] = { + (byte) 0x5d, + (byte) 0xab, + (byte) 0x08, + (byte) 0x7e, + (byte) 0x62, + (byte) 0x4a, + (byte) 0x8a, + (byte) 0x4b, + (byte) 0x79, + (byte) 0xe1, + (byte) 0x7f, + (byte) 0x8b, + (byte) 0x83, + (byte) 0x80, + (byte) 0x0e, + (byte) 0xe6, + (byte) 0x6f, + (byte) 0x3b, + (byte) 0xb1, + (byte) 0x29, + (byte) 0x26, + (byte) 0x18, + (byte) 0xb6, + (byte) 0xfd, + (byte) 0x1c, + (byte) 0x2f, + (byte) 0x8b, + (byte) 0x27, + (byte) 0xff, + (byte) 0x88, + (byte) 0xe0, + (byte) 0xeb + }; + + byte nonce[] = { + (byte) 0x69, + (byte) 0x69, + (byte) 0x6e, + (byte) 0xe9, + (byte) 0x55, + (byte) 0xb6, + (byte) 0x2b, + (byte) 0x73, + (byte) 0xcd, + (byte) 0x62, + (byte) 0xbd, + (byte) 0xa8, + (byte) 0x75, + (byte) 0xfc, + (byte) 0x73, + (byte) 0xd6, + (byte) 0x82, + (byte) 0x19, + (byte) 0xe0, + (byte) 0x03, + (byte) 0x6b, + (byte) 0x7a, + (byte) 0x0b, + (byte) 0x37 + }; + + byte m[] = { + (byte) 0xbe, + (byte) 0x07, + (byte) 0x5f, + (byte) 0xc5, + (byte) 0x3c, + (byte) 0x81, + (byte) 0xf2, + (byte) 0xd5, + (byte) 0xcf, + (byte) 0x14, + (byte) 0x13, + (byte) 0x16, + (byte) 0xeb, + (byte) 0xeb, + (byte) 0x0c, + (byte) 0x7b, + (byte) 0x52, + (byte) 0x28, + (byte) 0xc5, + (byte) 0x2a, + (byte) 0x4c, + (byte) 0x62, + (byte) 0xcb, + (byte) 0xd4, + (byte) 0x4b, + (byte) 0x66, + (byte) 0x84, + (byte) 0x9b, + (byte) 0x64, + (byte) 0x24, + (byte) 0x4f, + (byte) 0xfc, + (byte) 0xe5, + (byte) 0xec, + (byte) 0xba, + (byte) 0xaf, + (byte) 0x33, + (byte) 0xbd, + (byte) 0x75, + (byte) 0x1a, + (byte) 0x1a, + (byte) 0xc7, + (byte) 0x28, + (byte) 0xd4, + (byte) 0x5e, + (byte) 0x6c, + (byte) 0x61, + (byte) 0x29, + (byte) 0x6c, + (byte) 0xdc, + (byte) 0x3c, + (byte) 0x01, + (byte) 0x23, + (byte) 0x35, + (byte) 0x61, + (byte) 0xf4, + (byte) 0x1d, + (byte) 0xb6, + (byte) 0x6c, + (byte) 0xce, + (byte) 0x31, + (byte) 0x4a, + (byte) 0xdb, + (byte) 0x31, + (byte) 0x0e, + (byte) 0x3b, + (byte) 0xe8, + (byte) 0x25, + (byte) 0x0c, + (byte) 0x46, + (byte) 0xf0, + (byte) 0x6d, + (byte) 0xce, + (byte) 0xea, + (byte) 0x3a, + (byte) 0x7f, + (byte) 0xa1, + (byte) 0x34, + (byte) 0x80, + (byte) 0x57, + (byte) 0xe2, + (byte) 0xf6, + (byte) 0x55, + (byte) 0x6a, + (byte) 0xd6, + (byte) 0xb1, + (byte) 0x31, + (byte) 0x8a, + (byte) 0x02, + (byte) 0x4a, + (byte) 0x83, + (byte) 0x8f, + (byte) 0x21, + (byte) 0xaf, + (byte) 0x1f, + (byte) 0xde, + (byte) 0x04, + (byte) 0x89, + (byte) 0x77, + (byte) 0xeb, + (byte) 0x48, + (byte) 0xf5, + (byte) 0x9f, + (byte) 0xfd, + (byte) 0x49, + (byte) 0x24, + (byte) 0xca, + (byte) 0x1c, + (byte) 0x60, + (byte) 0x90, + (byte) 0x2e, + (byte) 0x52, + (byte) 0xf0, + (byte) 0xa0, + (byte) 0x89, + (byte) 0xbc, + (byte) 0x76, + (byte) 0x89, + (byte) 0x70, + (byte) 0x40, + (byte) 0xe0, + (byte) 0x82, + (byte) 0xf9, + (byte) 0x37, + (byte) 0x76, + (byte) 0x38, + (byte) 0x48, + (byte) 0x64, + (byte) 0x5e, + (byte) 0x07, + (byte) 0x05 + }; + + byte c_expected[] = { + (byte) 0xf3, + (byte) 0xff, + (byte) 0xc7, + (byte) 0x70, + (byte) 0x3f, + (byte) 0x94, + (byte) 0x00, + (byte) 0xe5, + (byte) 0x2a, + (byte) 0x7d, + (byte) 0xfb, + (byte) 0x4b, + (byte) 0x3d, + (byte) 0x33, + (byte) 0x05, + (byte) 0xd9, + (byte) 0x8e, + (byte) 0x99, + (byte) 0x3b, + (byte) 0x9f, + (byte) 0x48, + (byte) 0x68, + (byte) 0x12, + (byte) 0x73, + (byte) 0xc2, + (byte) 0x96, + (byte) 0x50, + (byte) 0xba, + (byte) 0x32, + (byte) 0xfc, + (byte) 0x76, + (byte) 0xce, + (byte) 0x48, + (byte) 0x33, + (byte) 0x2e, + (byte) 0xa7, + (byte) 0x16, + (byte) 0x4d, + (byte) 0x96, + (byte) 0xa4, + (byte) 0x47, + (byte) 0x6f, + (byte) 0xb8, + (byte) 0xc5, + (byte) 0x31, + (byte) 0xa1, + (byte) 0x18, + (byte) 0x6a, + (byte) 0xc0, + (byte) 0xdf, + (byte) 0xc1, + (byte) 0x7c, + (byte) 0x98, + (byte) 0xdc, + (byte) 0xe8, + (byte) 0x7b, + (byte) 0x4d, + (byte) 0xa7, + (byte) 0xf0, + (byte) 0x11, + (byte) 0xec, + (byte) 0x48, + (byte) 0xc9, + (byte) 0x72, + (byte) 0x71, + (byte) 0xd2, + (byte) 0xc2, + (byte) 0x0f, + (byte) 0x9b, + (byte) 0x92, + (byte) 0x8f, + (byte) 0xe2, + (byte) 0x27, + (byte) 0x0d, + (byte) 0x6f, + (byte) 0xb8, + (byte) 0x63, + (byte) 0xd5, + (byte) 0x17, + (byte) 0x38, + (byte) 0xb4, + (byte) 0x8e, + (byte) 0xee, + (byte) 0xe3, + (byte) 0x14, + (byte) 0xa7, + (byte) 0xcc, + (byte) 0x8a, + (byte) 0xb9, + (byte) 0x32, + (byte) 0x16, + (byte) 0x45, + (byte) 0x48, + (byte) 0xe5, + (byte) 0x26, + (byte) 0xae, + (byte) 0x90, + (byte) 0x22, + (byte) 0x43, + (byte) 0x68, + (byte) 0x51, + (byte) 0x7a, + (byte) 0xcf, + (byte) 0xea, + (byte) 0xbd, + (byte) 0x6b, + (byte) 0xb3, + (byte) 0x73, + (byte) 0x2b, + (byte) 0xc0, + (byte) 0xe9, + (byte) 0xda, + (byte) 0x99, + (byte) 0x83, + (byte) 0x2b, + (byte) 0x61, + (byte) 0xca, + (byte) 0x01, + (byte) 0xb6, + (byte) 0xde, + (byte) 0x56, + (byte) 0x24, + (byte) 0x4a, + (byte) 0x9e, + (byte) 0x88, + (byte) 0xd5, + (byte) 0xf9, + (byte) 0xb3, + (byte) 0x79, + (byte) 0x73, + (byte) 0xf6, + (byte) 0x22, + (byte) 0xa4, + (byte) 0x3d, + (byte) 0x14, + (byte) 0xa6, + (byte) 0x59, + (byte) 0x9b, + (byte) 0x1f, + (byte) 0x65, + (byte) 0x4c, + (byte) 0xb4, + (byte) 0x5a, + (byte) 0x74, + (byte) 0xe3, + (byte) 0x55, + (byte) 0xa5 + }; + + /* encrypt data and compare with expected result */ + NaCl nacl = new NaCl(alicesk, bobpk); + byte[] c = nacl.encrypt(m, nonce); + if (!Arrays.equals(c, c_expected)) throw new RuntimeException("Crypto self-test failed (1)"); + + /* decrypt data and compare with plaintext */ + nacl = new NaCl(bobsk, alicepk); + byte[] p_d = nacl.decrypt(c, nonce); + if (!Arrays.equals(p_d, m)) throw new RuntimeException("Crypto self-test failed (2)"); + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java index 3079e04..f1c7d7f 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java @@ -1,17 +1,17 @@ // // 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 @@ -27,440 +27,402 @@ package com.neilalexander.jnacl.crypto; -public class curve25519 -{ - final int CRYPTO_BYTES = 32; - final int CRYPTO_SCALARBYTES = 32; - - static byte[] basev = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static int[] minusp = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 }; - - public static int crypto_scalarmult_base(byte[] q, byte[] n) - { - byte[] basevp = basev; - return crypto_scalarmult(q, n, basevp); - } - - static void add(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) - { - int u = 0; - - for (int j = 0; j < 31; ++j) - { - u += a[aoffset + j] + b[boffset + j]; - outv[outvoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31] + b[boffset + 31]; - outv[outvoffset + 31] = u; - } - - static void sub(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) - { - int u = 218; - - for (int j = 0; j < 31; ++j) - { - u += a[aoffset + j] + 65280 - b[boffset + j]; - outv[outvoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31] - b[boffset + 31]; - outv[outvoffset + 31] = u; - } - - static void squeeze(int[] a, int aoffset) - { - int u = 0; - - for (int j = 0; j < 31; ++j) - { - u += a[aoffset + j]; - a[aoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31]; - a[aoffset + 31] = u & 127; - u = 19 * (u >>> 7); - - for (int j = 0; j < 31; ++j) - { - u += a[aoffset + j]; - a[aoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31]; - a[aoffset + 31] = u; - } - - static void freeze(int[] a, int aoffset) - { - int[] aorig = new int[32]; - - for (int j = 0; j < 32; ++j) - aorig[j] = a[aoffset + j]; - - int[] minuspp = minusp; - - add(a, 0, a, 0, minuspp, 0); - - int negative = (int) (-((a[aoffset + 31] >>> 7) & 1)); - - for (int j = 0; j < 32; ++j) - a[aoffset + j] ^= negative & (aorig[j] ^ a[aoffset + j]); - } - - static void mult(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) - { - int j; - - for (int i = 0; i < 32; ++i) - { - int u = 0; - - for (j = 0; j <= i; ++j) - u += a[aoffset + j] * b[boffset + i - j]; - - for (j = i + 1; j < 32; ++j) - u += 38 * a[aoffset + j] * b[boffset + i + 32 - j]; - - outv[outvoffset + i] = u; - } - - squeeze(outv, outvoffset); - } - - static void mult121665(int[] outv, int[] a) - { - int j; - int u = 0; - - for (j = 0; j < 31; ++j) - { - u += 121665 * a[j]; - outv[j] = u & 255; - u >>>= 8; - } - - u += 121665 * a[31]; - outv[31] = u & 127; - u = 19 * (u >>> 7); - - for (j = 0; j < 31; ++j) - { - u += outv[j]; - outv[j] = u & 255; - u >>>= 8; - } - - u += outv[j]; - outv[j] = u; - } - - static void square(int[] outv, int outvoffset, int[] a, int aoffset) - { - int j; - - for (int i = 0; i < 32; ++i) - { - int u = 0; - - for (j = 0; j < i - j; ++j) - u += a[aoffset + j] * a[aoffset + i - j]; - - for (j = i + 1; j < i + 32 - j; ++j) - u += 38 * a[aoffset + j] * a[aoffset + i + 32 - j]; - - u *= 2; - - if ((i & 1) == 0) - { - u += a[aoffset + i / 2] * a[aoffset + i / 2]; - u += 38 * a[aoffset + i / 2 + 16] * a[aoffset + i / 2 + 16]; - } - - outv[outvoffset + i] = u; - } - - squeeze(outv, outvoffset); - } - - static void select(int[] p, int[] q, int[] r, int[] s, int b) - { - int bminus1 = b - 1; - - for (int j = 0; j < 64; ++j) - { - int t = bminus1 & (r[j] ^ s[j]); - p[j] = s[j] ^ t; - q[j] = r[j] ^ t; - } - } - - static void mainloop(int[] work, byte[] e) - { - int[] xzm1 = new int[64]; - int[] xzm = new int[64]; - int[] xzmb = new int[64]; - int[] xzm1b = new int[64]; - int[] xznb = new int[64]; - int[] xzn1b = new int[64]; - int[] a0 = new int[64]; - int[] a1 = new int[64]; - int[] b0 = new int[64]; - int[] b1 = new int[64]; - int[] c1 = new int[64]; - int[] r = new int[32]; - int[] s = new int[32]; - int[] t = new int[32]; - int[] u = new int[32]; - - for (int j = 0; j < 32; ++j) - xzm1[j] = work[j]; - - xzm1[32] = 1; - - for (int j = 33; j < 64; ++j) - xzm1[j] = 0; - - xzm[0] = 1; - - for (int j = 1; j < 64; ++j) - xzm[j] = 0; - - int[] xzmbp = xzmb, a0p = a0, xzm1bp = xzm1b; - int[] a1p = a1, b0p = b0, b1p = b1, c1p = c1; - int[] xznbp = xznb, up = u, xzn1bp = xzn1b; - int[] workp = work, sp = s, rp = r; - - for (int pos = 254; pos >= 0; --pos) - { - int b = ((int) ((e[pos / 8] & 0xFF) >>> (pos & 7))); - b &= 1; - select(xzmb, xzm1b, xzm, xzm1, b); - add(a0, 0, xzmb, 0, xzmbp, 32); - sub(a0p, 32, xzmb, 0, xzmbp, 32); - add(a1, 0, xzm1b, 0, xzm1bp, 32); - sub(a1p, 32, xzm1b, 0, xzm1bp, 32); - square(b0p, 0, a0p, 0); - square(b0p, 32, a0p, 32); - mult(b1p, 0, a1p, 0, a0p, 32); - mult(b1p, 32, a1p, 32, a0p, 0); - add(c1, 0, b1, 0, b1p, 32); - sub(c1p, 32, b1, 0, b1p, 32); - square(rp, 0, c1p, 32); - sub(sp, 0, b0, 0, b0p, 32); - mult121665(t, s); - add(u, 0, t, 0, b0p, 0); - mult(xznbp, 0, b0p, 0, b0p, 32); - mult(xznbp, 32, sp, 0, up, 0); - square(xzn1bp, 0, c1p, 0); - mult(xzn1bp, 32, rp, 0, workp, 0); - select(xzm, xzm1, xznb, xzn1b, b); - } - - for (int j = 0; j < 64; ++j) - work[j] = xzm[j]; - } - - static void recip(int[] outv, int outvoffset, int[] z, int zoffset) - { - int[] z2 = new int[32]; - int[] z9 = new int[32]; - int[] z11 = new int[32]; - int[] z2_5_0 = new int[32]; - int[] z2_10_0 = new int[32]; - int[] z2_20_0 = new int[32]; - int[] z2_50_0 = new int[32]; - int[] z2_100_0 = new int[32]; - int[] t0 = new int[32]; - int[] t1 = new int[32]; - - /* 2 */ - int[] z2p = z2; - square(z2p, 0, z, zoffset); - - /* 4 */ - square(t1, 0, z2, 0); - - /* 8 */ - square(t0, 0, t1, 0); - - /* 9 */ - int[] z9p = z9, t0p = t0; - mult(z9p, 0, t0p, 0, z, zoffset); - - /* 11 */ - mult(z11, 0, z9, 0, z2, 0); - - /* 22 */ - square(t0, 0, z11, 0); - - /* 2^5 - 2^0 = 31 */ - mult(z2_5_0, 0, t0, 0, z9, 0); - - /* 2^6 - 2^1 */ - square(t0, 0, z2_5_0, 0); - - /* 2^7 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^8 - 2^3 */ - square(t0, 0, t1, 0); - - /* 2^9 - 2^4 */ - square(t1, 0, t0, 0); - - /* 2^10 - 2^5 */ - square(t0, 0, t1, 0); - - /* 2^10 - 2^0 */ - mult(z2_10_0, 0, t0, 0, z2_5_0, 0); - - /* 2^11 - 2^1 */ - square(t0, 0, z2_10_0, 0); - - /* 2^12 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^20 - 2^10 */ - for (int i = 2; i < 10; i += 2) - { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^20 - 2^0 */ - mult(z2_20_0, 0, t1, 0, z2_10_0, 0); - - /* 2^21 - 2^1 */ - square(t0, 0, z2_20_0, 0); - - /* 2^22 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^40 - 2^20 */ - for (int i = 2; i < 20; i += 2) - { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^40 - 2^0 */ - mult(t0, 0, t1, 0, z2_20_0, 0); - - /* 2^41 - 2^1 */ - square(t1, 0, t0, 0); - - /* 2^42 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^50 - 2^10 */ - for (int i = 2; i < 10; i += 2) - { - square(t1, 0, t0, 0); - square(t0, 0, t1, 0); - } - - /* 2^50 - 2^0 */ - mult(z2_50_0, 0, t0, 0, z2_10_0, 0); - - /* 2^51 - 2^1 */ - square(t0, 0, z2_50_0, 0); - - /* 2^52 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^100 - 2^50 */ - for (int i = 2; i < 50; i += 2) - { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^100 - 2^0 */ - mult(z2_100_0, 0, t1, 0, z2_50_0, 0); - - /* 2^101 - 2^1 */ - square(t1, 0, z2_100_0, 0); - - /* 2^102 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^200 - 2^100 */ - for (int i = 2; i < 100; i += 2) - { - square(t1, 0, t0, 0); - square(t0, 0, t1, 0); - } - - /* 2^200 - 2^0 */ - mult(t1, 0, t0, 0, z2_100_0, 0); - - /* 2^201 - 2^1 */ - square(t0, 0, t1, 0); - - /* 2^202 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^250 - 2^50 */ - for (int i = 2; i < 50; i += 2) - { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^250 - 2^0 */ - mult(t0, 0, t1, 0, z2_50_0, 0); - - /* 2^251 - 2^1 */ - square(t1, 0, t0, 0); - - /* 2^252 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^253 - 2^3 */ - square(t1, 0, t0, 0); - - /* 2^254 - 2^4 */ - square(t0, 0, t1, 0); - - /* 2^255 - 2^5 */ - square(t1, 0, t0, 0); - - /* 2^255 - 21 */ - int[] t1p = t1, z11p = z11; - mult(outv, outvoffset, t1p, 0, z11p, 0); - } - - public static int crypto_scalarmult(byte[] q, byte[] n, byte[] p) - { - int[] work = new int[96]; - byte[] e = new byte[32]; - - for (int i = 0; i < 32; ++i) - e[i] = n[i]; - - e[0] &= 248; - e[31] &= 127; - e[31] |= 64; - - for (int i = 0; i < 32; ++i) - work[i] = p[i] & 0xFF; - - mainloop(work, e); - - recip(work, 32, work, 32); - mult(work, 64, work, 0, work, 32); - freeze(work, 64); - - for (int i = 0; i < 32; ++i) - q[i] = (byte) work[64 + i]; - - return 0; - } +public class curve25519 { + final int CRYPTO_BYTES = 32; + final int CRYPTO_SCALARBYTES = 32; + + static byte[] basev = { + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static int[] minusp = { + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128 + }; + + public static int crypto_scalarmult_base(byte[] q, byte[] n) { + byte[] basevp = basev; + return crypto_scalarmult(q, n, basevp); + } + + static void add(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { + int u = 0; + + for (int j = 0; j < 31; ++j) { + u += a[aoffset + j] + b[boffset + j]; + outv[outvoffset + j] = u & 255; + u >>>= 8; + } + + u += a[aoffset + 31] + b[boffset + 31]; + outv[outvoffset + 31] = u; + } + + static void sub(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { + int u = 218; + + for (int j = 0; j < 31; ++j) { + u += a[aoffset + j] + 65280 - b[boffset + j]; + outv[outvoffset + j] = u & 255; + u >>>= 8; + } + + u += a[aoffset + 31] - b[boffset + 31]; + outv[outvoffset + 31] = u; + } + + static void squeeze(int[] a, int aoffset) { + int u = 0; + + for (int j = 0; j < 31; ++j) { + u += a[aoffset + j]; + a[aoffset + j] = u & 255; + u >>>= 8; + } + + u += a[aoffset + 31]; + a[aoffset + 31] = u & 127; + u = 19 * (u >>> 7); + + for (int j = 0; j < 31; ++j) { + u += a[aoffset + j]; + a[aoffset + j] = u & 255; + u >>>= 8; + } + + u += a[aoffset + 31]; + a[aoffset + 31] = u; + } + + static void freeze(int[] a, int aoffset) { + int[] aorig = new int[32]; + + for (int j = 0; j < 32; ++j) aorig[j] = a[aoffset + j]; + + int[] minuspp = minusp; + + add(a, 0, a, 0, minuspp, 0); + + int negative = (int) (-((a[aoffset + 31] >>> 7) & 1)); + + for (int j = 0; j < 32; ++j) a[aoffset + j] ^= negative & (aorig[j] ^ a[aoffset + j]); + } + + static void mult(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { + int j; + + for (int i = 0; i < 32; ++i) { + int u = 0; + + for (j = 0; j <= i; ++j) u += a[aoffset + j] * b[boffset + i - j]; + + for (j = i + 1; j < 32; ++j) u += 38 * a[aoffset + j] * b[boffset + i + 32 - j]; + + outv[outvoffset + i] = u; + } + + squeeze(outv, outvoffset); + } + + static void mult121665(int[] outv, int[] a) { + int j; + int u = 0; + + for (j = 0; j < 31; ++j) { + u += 121665 * a[j]; + outv[j] = u & 255; + u >>>= 8; + } + + u += 121665 * a[31]; + outv[31] = u & 127; + u = 19 * (u >>> 7); + + for (j = 0; j < 31; ++j) { + u += outv[j]; + outv[j] = u & 255; + u >>>= 8; + } + + u += outv[j]; + outv[j] = u; + } + + static void square(int[] outv, int outvoffset, int[] a, int aoffset) { + int j; + + for (int i = 0; i < 32; ++i) { + int u = 0; + + for (j = 0; j < i - j; ++j) u += a[aoffset + j] * a[aoffset + i - j]; + + for (j = i + 1; j < i + 32 - j; ++j) u += 38 * a[aoffset + j] * a[aoffset + i + 32 - j]; + + u *= 2; + + if ((i & 1) == 0) { + u += a[aoffset + i / 2] * a[aoffset + i / 2]; + u += 38 * a[aoffset + i / 2 + 16] * a[aoffset + i / 2 + 16]; + } + + outv[outvoffset + i] = u; + } + + squeeze(outv, outvoffset); + } + + static void select(int[] p, int[] q, int[] r, int[] s, int b) { + int bminus1 = b - 1; + + for (int j = 0; j < 64; ++j) { + int t = bminus1 & (r[j] ^ s[j]); + p[j] = s[j] ^ t; + q[j] = r[j] ^ t; + } + } + + static void mainloop(int[] work, byte[] e) { + int[] xzm1 = new int[64]; + int[] xzm = new int[64]; + int[] xzmb = new int[64]; + int[] xzm1b = new int[64]; + int[] xznb = new int[64]; + int[] xzn1b = new int[64]; + int[] a0 = new int[64]; + int[] a1 = new int[64]; + int[] b0 = new int[64]; + int[] b1 = new int[64]; + int[] c1 = new int[64]; + int[] r = new int[32]; + int[] s = new int[32]; + int[] t = new int[32]; + int[] u = new int[32]; + + for (int j = 0; j < 32; ++j) xzm1[j] = work[j]; + + xzm1[32] = 1; + + for (int j = 33; j < 64; ++j) xzm1[j] = 0; + + xzm[0] = 1; + + for (int j = 1; j < 64; ++j) xzm[j] = 0; + + int[] xzmbp = xzmb, a0p = a0, xzm1bp = xzm1b; + int[] a1p = a1, b0p = b0, b1p = b1, c1p = c1; + int[] xznbp = xznb, up = u, xzn1bp = xzn1b; + int[] workp = work, sp = s, rp = r; + + for (int pos = 254; pos >= 0; --pos) { + int b = ((int) ((e[pos / 8] & 0xFF) >>> (pos & 7))); + b &= 1; + select(xzmb, xzm1b, xzm, xzm1, b); + add(a0, 0, xzmb, 0, xzmbp, 32); + sub(a0p, 32, xzmb, 0, xzmbp, 32); + add(a1, 0, xzm1b, 0, xzm1bp, 32); + sub(a1p, 32, xzm1b, 0, xzm1bp, 32); + square(b0p, 0, a0p, 0); + square(b0p, 32, a0p, 32); + mult(b1p, 0, a1p, 0, a0p, 32); + mult(b1p, 32, a1p, 32, a0p, 0); + add(c1, 0, b1, 0, b1p, 32); + sub(c1p, 32, b1, 0, b1p, 32); + square(rp, 0, c1p, 32); + sub(sp, 0, b0, 0, b0p, 32); + mult121665(t, s); + add(u, 0, t, 0, b0p, 0); + mult(xznbp, 0, b0p, 0, b0p, 32); + mult(xznbp, 32, sp, 0, up, 0); + square(xzn1bp, 0, c1p, 0); + mult(xzn1bp, 32, rp, 0, workp, 0); + select(xzm, xzm1, xznb, xzn1b, b); + } + + for (int j = 0; j < 64; ++j) work[j] = xzm[j]; + } + + static void recip(int[] outv, int outvoffset, int[] z, int zoffset) { + int[] z2 = new int[32]; + int[] z9 = new int[32]; + int[] z11 = new int[32]; + int[] z2_5_0 = new int[32]; + int[] z2_10_0 = new int[32]; + int[] z2_20_0 = new int[32]; + int[] z2_50_0 = new int[32]; + int[] z2_100_0 = new int[32]; + int[] t0 = new int[32]; + int[] t1 = new int[32]; + + /* 2 */ + int[] z2p = z2; + square(z2p, 0, z, zoffset); + + /* 4 */ + square(t1, 0, z2, 0); + + /* 8 */ + square(t0, 0, t1, 0); + + /* 9 */ + int[] z9p = z9, t0p = t0; + mult(z9p, 0, t0p, 0, z, zoffset); + + /* 11 */ + mult(z11, 0, z9, 0, z2, 0); + + /* 22 */ + square(t0, 0, z11, 0); + + /* 2^5 - 2^0 = 31 */ + mult(z2_5_0, 0, t0, 0, z9, 0); + + /* 2^6 - 2^1 */ + square(t0, 0, z2_5_0, 0); + + /* 2^7 - 2^2 */ + square(t1, 0, t0, 0); + + /* 2^8 - 2^3 */ + square(t0, 0, t1, 0); + + /* 2^9 - 2^4 */ + square(t1, 0, t0, 0); + + /* 2^10 - 2^5 */ + square(t0, 0, t1, 0); + + /* 2^10 - 2^0 */ + mult(z2_10_0, 0, t0, 0, z2_5_0, 0); + + /* 2^11 - 2^1 */ + square(t0, 0, z2_10_0, 0); + + /* 2^12 - 2^2 */ + square(t1, 0, t0, 0); + + /* 2^20 - 2^10 */ + for (int i = 2; i < 10; i += 2) { + square(t0, 0, t1, 0); + square(t1, 0, t0, 0); + } + + /* 2^20 - 2^0 */ + mult(z2_20_0, 0, t1, 0, z2_10_0, 0); + + /* 2^21 - 2^1 */ + square(t0, 0, z2_20_0, 0); + + /* 2^22 - 2^2 */ + square(t1, 0, t0, 0); + + /* 2^40 - 2^20 */ + for (int i = 2; i < 20; i += 2) { + square(t0, 0, t1, 0); + square(t1, 0, t0, 0); + } + + /* 2^40 - 2^0 */ + mult(t0, 0, t1, 0, z2_20_0, 0); + + /* 2^41 - 2^1 */ + square(t1, 0, t0, 0); + + /* 2^42 - 2^2 */ + square(t0, 0, t1, 0); + + /* 2^50 - 2^10 */ + for (int i = 2; i < 10; i += 2) { + square(t1, 0, t0, 0); + square(t0, 0, t1, 0); + } + + /* 2^50 - 2^0 */ + mult(z2_50_0, 0, t0, 0, z2_10_0, 0); + + /* 2^51 - 2^1 */ + square(t0, 0, z2_50_0, 0); + + /* 2^52 - 2^2 */ + square(t1, 0, t0, 0); + + /* 2^100 - 2^50 */ + for (int i = 2; i < 50; i += 2) { + square(t0, 0, t1, 0); + square(t1, 0, t0, 0); + } + + /* 2^100 - 2^0 */ + mult(z2_100_0, 0, t1, 0, z2_50_0, 0); + + /* 2^101 - 2^1 */ + square(t1, 0, z2_100_0, 0); + + /* 2^102 - 2^2 */ + square(t0, 0, t1, 0); + + /* 2^200 - 2^100 */ + for (int i = 2; i < 100; i += 2) { + square(t1, 0, t0, 0); + square(t0, 0, t1, 0); + } + + /* 2^200 - 2^0 */ + mult(t1, 0, t0, 0, z2_100_0, 0); + + /* 2^201 - 2^1 */ + square(t0, 0, t1, 0); + + /* 2^202 - 2^2 */ + square(t1, 0, t0, 0); + + /* 2^250 - 2^50 */ + for (int i = 2; i < 50; i += 2) { + square(t0, 0, t1, 0); + square(t1, 0, t0, 0); + } + + /* 2^250 - 2^0 */ + mult(t0, 0, t1, 0, z2_50_0, 0); + + /* 2^251 - 2^1 */ + square(t1, 0, t0, 0); + + /* 2^252 - 2^2 */ + square(t0, 0, t1, 0); + + /* 2^253 - 2^3 */ + square(t1, 0, t0, 0); + + /* 2^254 - 2^4 */ + square(t0, 0, t1, 0); + + /* 2^255 - 2^5 */ + square(t1, 0, t0, 0); + + /* 2^255 - 21 */ + int[] t1p = t1, z11p = z11; + mult(outv, outvoffset, t1p, 0, z11p, 0); + } + + public static int crypto_scalarmult(byte[] q, byte[] n, byte[] p) { + int[] work = new int[96]; + byte[] e = new byte[32]; + + for (int i = 0; i < 32; ++i) e[i] = n[i]; + + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + for (int i = 0; i < 32; ++i) work[i] = p[i] & 0xFF; + + mainloop(work, e); + + recip(work, 32, work, 32); + mult(work, 64, work, 0, work, 32); + freeze(work, 64); + + for (int i = 0; i < 32; ++i) q[i] = (byte) work[64 + i]; + + return 0; + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java index b952deb..fab9ce3 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java @@ -1,17 +1,17 @@ // // 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 @@ -27,84 +27,73 @@ package com.neilalexander.jnacl.crypto; -public class curve25519xsalsa20poly1305 -{ - public static final int crypto_box_PUBLICKEYBYTES = 32; - public static final int crypto_box_SECRETKEYBYTES = 32; - public static final int crypto_box_BEFORENMBYTES = 32; - public static final int crypto_box_NONCEBYTES = 24; - public static final int crypto_box_ZEROBYTES = 32; - public static final int crypto_box_BOXZEROBYTES = 16; - - public static int crypto_box_getpublickey(byte[] pk, byte[] sk) - { - return curve25519.crypto_scalarmult_base(pk, sk); - } - - public static int crypto_box_afternm(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) - { - return xsalsa20poly1305.crypto_secretbox(c, m, mlen, n, k); - } - - public static int crypto_box_afternm_nopad(byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) - { - return xsalsa20poly1305.crypto_secretbox_nopad(c, coffset, m, moffset, mlen, n, k); - } - - public static int crypto_box_beforenm(byte[] k, byte[] pk, byte[] sk) - { - byte[] s = new byte[32]; - byte[] sp = s, sigmap = xsalsa20.sigma; - - curve25519.crypto_scalarmult(sp, sk, pk); - return hsalsa20.crypto_core(k, null, sp, sigmap); - } - - public static int crypto_box(byte[] c, byte[] m, long mlen, byte[] n, byte[] pk, byte[] sk) - { - byte[] k = new byte[crypto_box_BEFORENMBYTES]; - byte[] kp = k; - - crypto_box_beforenm(kp, pk, sk); - return crypto_box_afternm(c, m, mlen, n, kp); - } - - public static int crypto_box_open(byte[] m, byte[] c, long clen, byte[] n, byte[] pk, byte[] sk) - { - byte[] k = new byte[crypto_box_BEFORENMBYTES]; - byte[] kp = k; - - crypto_box_beforenm(kp, pk, sk); - return crypto_box_open_afternm(m, c, clen, n, kp); - } - - public static int crypto_box_open_afternm(byte[] m, byte[] c, long clen, byte[] n, byte[] k) - { - return xsalsa20poly1305.crypto_secretbox_open(m, c, clen, n, k); - } - - public static int crypto_box_open_afternm_nopad(byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) - { - return xsalsa20poly1305.crypto_secretbox_open_nopad(m, moffset, c, coffset, clen, n, k); - } - - public static int crypto_box_afternm(byte[] c, byte[] m, byte[] n, byte[] k) - { - return crypto_box_afternm(c, m, (long)m.length, n, k); - } - - public static int crypto_box_open_afternm(byte[] m, byte[] c, byte[] n, byte[] k) - { - return crypto_box_open_afternm(m, c, (long) c.length, n, k); - } - - public static int crypto_box(byte[] c, byte[] m, byte[] n, byte[] pk, byte[] sk) - { - return crypto_box(c, m, (long) m.length, n, pk, sk); - } - - public static int crypto_box_open(byte[] m, byte[] c, byte[] n, byte[] pk, byte[] sk) - { - return crypto_box_open(m, c, (long) c.length, n, pk, sk); - } +public class curve25519xsalsa20poly1305 { + public static final int crypto_box_PUBLICKEYBYTES = 32; + public static final int crypto_box_SECRETKEYBYTES = 32; + public static final int crypto_box_BEFORENMBYTES = 32; + public static final int crypto_box_NONCEBYTES = 24; + public static final int crypto_box_ZEROBYTES = 32; + public static final int crypto_box_BOXZEROBYTES = 16; + + public static int crypto_box_getpublickey(byte[] pk, byte[] sk) { + return curve25519.crypto_scalarmult_base(pk, sk); + } + + public static int crypto_box_afternm(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) { + return xsalsa20poly1305.crypto_secretbox(c, m, mlen, n, k); + } + + public static int crypto_box_afternm_nopad( + byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) { + return xsalsa20poly1305.crypto_secretbox_nopad(c, coffset, m, moffset, mlen, n, k); + } + + public static int crypto_box_beforenm(byte[] k, byte[] pk, byte[] sk) { + byte[] s = new byte[32]; + byte[] sp = s, sigmap = xsalsa20.sigma; + + curve25519.crypto_scalarmult(sp, sk, pk); + return hsalsa20.crypto_core(k, null, sp, sigmap); + } + + public static int crypto_box(byte[] c, byte[] m, long mlen, byte[] n, byte[] pk, byte[] sk) { + byte[] k = new byte[crypto_box_BEFORENMBYTES]; + byte[] kp = k; + + crypto_box_beforenm(kp, pk, sk); + return crypto_box_afternm(c, m, mlen, n, kp); + } + + public static int crypto_box_open(byte[] m, byte[] c, long clen, byte[] n, byte[] pk, byte[] sk) { + byte[] k = new byte[crypto_box_BEFORENMBYTES]; + byte[] kp = k; + + crypto_box_beforenm(kp, pk, sk); + return crypto_box_open_afternm(m, c, clen, n, kp); + } + + public static int crypto_box_open_afternm(byte[] m, byte[] c, long clen, byte[] n, byte[] k) { + return xsalsa20poly1305.crypto_secretbox_open(m, c, clen, n, k); + } + + public static int crypto_box_open_afternm_nopad( + byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) { + return xsalsa20poly1305.crypto_secretbox_open_nopad(m, moffset, c, coffset, clen, n, k); + } + + public static int crypto_box_afternm(byte[] c, byte[] m, byte[] n, byte[] k) { + return crypto_box_afternm(c, m, (long) m.length, n, k); + } + + public static int crypto_box_open_afternm(byte[] m, byte[] c, byte[] n, byte[] k) { + return crypto_box_open_afternm(m, c, (long) c.length, n, k); + } + + public static int crypto_box(byte[] c, byte[] m, byte[] n, byte[] pk, byte[] sk) { + return crypto_box(c, m, (long) m.length, n, pk, sk); + } + + public static int crypto_box_open(byte[] m, byte[] c, byte[] n, byte[] pk, byte[] sk) { + return crypto_box_open(m, c, (long) c.length, n, pk, sk); + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java b/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java index ae5e429..77be278 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java @@ -1,17 +1,17 @@ // // 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 @@ -27,138 +27,131 @@ package com.neilalexander.jnacl.crypto; -public class hsalsa20 -{ - static final int ROUNDS = 20; +public class hsalsa20 { + static final int ROUNDS = 20; - static int rotate(int u, int c) - { - return (u << c) | (u >>> (32 - 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) - { - 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; + public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c) { + int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + int i; - j0 = x0 = load_littleendian(c, 0); - j1 = x1 = load_littleendian(k, 0); - j2 = x2 = load_littleendian(k, 4); - j3 = x3 = load_littleendian(k, 8); - j4 = x4 = load_littleendian(k, 12); - j5 = x5 = load_littleendian(c, 4); + j0 = x0 = load_littleendian(c, 0); + j1 = x1 = load_littleendian(k, 0); + j2 = x2 = load_littleendian(k, 4); + j3 = x3 = load_littleendian(k, 8); + j4 = x4 = load_littleendian(k, 12); + j5 = x5 = load_littleendian(c, 4); - if (inv != null) - { - j6 = x6 = load_littleendian(inv, 0); - j7 = x7 = load_littleendian(inv, 4); - j8 = x8 = load_littleendian(inv, 8); - j9 = x9 = load_littleendian(inv, 12); - } - else - { - j6 = x6 = j7 = x7 = j8 = x8 = j9 = x9 = 0; - } + if (inv != null) { + j6 = x6 = load_littleendian(inv, 0); + j7 = x7 = load_littleendian(inv, 4); + j8 = x8 = load_littleendian(inv, 8); + j9 = x9 = load_littleendian(inv, 12); + } else { + j6 = x6 = j7 = x7 = j8 = x8 = j9 = x9 = 0; + } - j10 = x10 = load_littleendian(c, 8); - j11 = x11 = load_littleendian(k, 16); - j12 = x12 = load_littleendian(k, 20); - j13 = x13 = load_littleendian(k, 24); - j14 = x14 = load_littleendian(k, 28); - j15 = x15 = load_littleendian(c, 12); + j10 = x10 = load_littleendian(c, 8); + j11 = x11 = load_littleendian(k, 16); + j12 = x12 = load_littleendian(k, 20); + j13 = x13 = load_littleendian(k, 24); + j14 = x14 = load_littleendian(k, 28); + j15 = x15 = load_littleendian(c, 12); - for (i = ROUNDS; i > 0; i -= 2) - { - x4 ^= rotate(x0 + x12, 7); - x8 ^= rotate(x4 + x0, 9); - x12 ^= rotate(x8 + x4, 13); - x0 ^= rotate(x12 + x8, 18); - x9 ^= rotate(x5 + x1, 7); - x13 ^= rotate(x9 + x5, 9); - x1 ^= rotate(x13 + x9, 13); - x5 ^= rotate(x1 + x13, 18); - x14 ^= rotate(x10 + x6, 7); - x2 ^= rotate(x14 + x10, 9); - x6 ^= rotate(x2 + x14, 13); - x10 ^= rotate(x6 + x2, 18); - x3 ^= rotate(x15 + x11, 7); - x7 ^= rotate(x3 + x15, 9); - x11 ^= rotate(x7 + x3, 13); - x15 ^= rotate(x11 + x7, 18); - x1 ^= rotate(x0 + x3, 7); - x2 ^= rotate(x1 + x0, 9); - x3 ^= rotate(x2 + x1, 13); - x0 ^= rotate(x3 + x2, 18); - x6 ^= rotate(x5 + x4, 7); - x7 ^= rotate(x6 + x5, 9); - x4 ^= rotate(x7 + x6, 13); - x5 ^= rotate(x4 + x7, 18); - x11 ^= rotate(x10 + x9, 7); - x8 ^= rotate(x11 + x10, 9); - x9 ^= rotate(x8 + x11, 13); - x10 ^= rotate(x9 + x8, 18); - x12 ^= rotate(x15 + x14, 7); - x13 ^= rotate(x12 + x15, 9); - x14 ^= rotate(x13 + x12, 13); - x15 ^= rotate(x14 + x13, 18); - } + for (i = ROUNDS; i > 0; i -= 2) { + x4 ^= rotate(x0 + x12, 7); + x8 ^= rotate(x4 + x0, 9); + x12 ^= rotate(x8 + x4, 13); + x0 ^= rotate(x12 + x8, 18); + x9 ^= rotate(x5 + x1, 7); + x13 ^= rotate(x9 + x5, 9); + x1 ^= rotate(x13 + x9, 13); + x5 ^= rotate(x1 + x13, 18); + x14 ^= rotate(x10 + x6, 7); + x2 ^= rotate(x14 + x10, 9); + x6 ^= rotate(x2 + x14, 13); + x10 ^= rotate(x6 + x2, 18); + x3 ^= rotate(x15 + x11, 7); + x7 ^= rotate(x3 + x15, 9); + x11 ^= rotate(x7 + x3, 13); + x15 ^= rotate(x11 + x7, 18); + x1 ^= rotate(x0 + x3, 7); + x2 ^= rotate(x1 + x0, 9); + x3 ^= rotate(x2 + x1, 13); + x0 ^= rotate(x3 + x2, 18); + x6 ^= rotate(x5 + x4, 7); + x7 ^= rotate(x6 + x5, 9); + x4 ^= rotate(x7 + x6, 13); + x5 ^= rotate(x4 + x7, 18); + x11 ^= rotate(x10 + x9, 7); + x8 ^= rotate(x11 + x10, 9); + x9 ^= rotate(x8 + x11, 13); + x10 ^= rotate(x9 + x8, 18); + x12 ^= rotate(x15 + x14, 7); + x13 ^= rotate(x12 + x15, 9); + x14 ^= rotate(x13 + x12, 13); + x15 ^= rotate(x14 + x13, 18); + } - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; - x4 += j4; - x5 += j5; - x6 += j6; - x7 += j7; - x8 += j8; - x9 += j9; - x10 += j10; - x11 += j11; - x12 += j12; - x13 += j13; - x14 += j14; - x15 += j15; + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; - x0 -= load_littleendian(c, 0); - x5 -= load_littleendian(c, 4); - x10 -= load_littleendian(c, 8); - x15 -= load_littleendian(c, 12); + x0 -= load_littleendian(c, 0); + x5 -= load_littleendian(c, 4); + x10 -= load_littleendian(c, 8); + x15 -= load_littleendian(c, 12); - if (inv != null) - { - x6 -= load_littleendian(inv, 0); - x7 -= load_littleendian(inv, 4); - x8 -= load_littleendian(inv, 8); - x9 -= load_littleendian(inv, 12); - } + if (inv != null) { + x6 -= load_littleendian(inv, 0); + x7 -= load_littleendian(inv, 4); + x8 -= load_littleendian(inv, 8); + x9 -= load_littleendian(inv, 12); + } - store_littleendian(outv, 0, x0); - store_littleendian(outv, 4, x5); - store_littleendian(outv, 8, x10); - store_littleendian(outv, 12, x15); - store_littleendian(outv, 16, x6); - store_littleendian(outv, 20, x7); - store_littleendian(outv, 24, x8); - store_littleendian(outv, 28, x9); + store_littleendian(outv, 0, x0); + store_littleendian(outv, 4, x5); + store_littleendian(outv, 8, x10); + store_littleendian(outv, 12, x15); + store_littleendian(outv, 16, x6); + store_littleendian(outv, 20, x7); + store_littleendian(outv, 24, x8); + store_littleendian(outv, 28, x9); - return 0; - } + return 0; + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java index 7db9951..c52f216 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java @@ -1,17 +1,17 @@ // // 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 @@ -27,152 +27,132 @@ package com.neilalexander.jnacl.crypto; -public class poly1305 -{ - final int CRYPTO_BYTES = 16; - final int CRYPTO_KEYBYTES = 32; - - static final int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252}; - - public static int crypto_onetimeauth_verify(byte[] h, int hoffset, byte[] inv, int invoffset, long inlen, byte[] k) - { - byte[] correct = new byte[16]; - - crypto_onetimeauth(correct, 0, inv, invoffset, inlen, k); - return verify_16.crypto_verify(h, hoffset, correct); - } - - static void add(int[] h, int[] c) - { - int j; - int u = 0; - - for (j = 0; j < 17; ++j) - { - u += h[j] + c[j]; - h[j] = u & 255; - u >>>= 8; - } - } - - static void squeeze(int[] h) - { - int u = 0; - - for (int j = 0; j < 16; ++j) - { - u += h[j]; - h[j] = u & 255; - u >>>= 8; - } - - u += h[16]; - h[16] = u & 3; - u = 5 * (u >>> 2); - - for (int j = 0; j < 16; ++j) - { - u += h[j]; - h[j] = u & 255; - u >>>= 8; - } - - u += h[16]; - h[16] = u; - } - - static void freeze(int[] h) - { - int[] horig = new int[17]; - - for (int j = 0; j < 17; ++j) - horig[j] = h[j]; - - add(h, minusp); - - int negative = (int)(-(h[16] >>> 7)); - - for (int j = 0; j < 17; ++j) - h[j] ^= negative & (horig[j] ^ h[j]); - } - - static void mulmod(int[] h, int[] r) - { - int[] hr = new int[17]; - - for (int i = 0; i < 17; ++i) - { - int u = 0; - - for (int j = 0; j <= i; ++j) - u += h[j] * r[i - j]; - - for (int j = i + 1; j < 17; ++j) - u += 320 * h[j] * r[i + 17 - j]; - - hr[i] = u; - } - - for (int i = 0; i < 17; ++i) - h[i] = hr[i]; - - squeeze(h); - } - - public static int crypto_onetimeauth(byte[] outv, int outvoffset, byte[] inv, int invoffset, long inlen, byte[] k) - { - int j; - int[] r = new int[17]; - int[] h = new int[17]; - int[] c = new int[17]; - - r[0] = k[0] & 0xFF; - r[1] = k[1] & 0xFF; - r[2] = k[2] & 0xFF; - r[3] = k[3] & 15; - r[4] = k[4] & 252; - r[5] = k[5] & 0xFF; - r[6] = k[6] & 0xFF; - r[7] = k[7] & 15; - r[8] = k[8] & 252; - r[9] = k[9] & 0xFF; - r[10] = k[10] & 0xFF; - r[11] = k[11] & 15; - r[12] = k[12] & 252; - r[13] = k[13] & 0xFF; - r[14] = k[14] & 0xFF; - r[15] = k[15] & 15; - r[16] = 0; - - for (j = 0; j < 17; ++j) - h[j] = 0; - - while (inlen > 0) - { - for (j = 0; j < 17; ++j) - c[j] = 0; - - for (j = 0; (j < 16) && (j < inlen); ++j) - c[j] = inv[invoffset + j]&0xff; - - c[j] = 1; - invoffset += j; - inlen -= j; - add(h, c); - mulmod(h, r); - } - - freeze(h); - - for (j = 0; j < 16; ++j) - c[j] = k[j + 16] & 0xFF; - - c[16] = 0; - add(h, c); - - for (j = 0; j < 16; ++j) - outv[j + outvoffset] = (byte)h[j]; - - return 0; - } +public class poly1305 { + final int CRYPTO_BYTES = 16; + final int CRYPTO_KEYBYTES = 32; + + static final int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252}; + + public static int crypto_onetimeauth_verify( + byte[] h, int hoffset, byte[] inv, int invoffset, long inlen, byte[] k) { + byte[] correct = new byte[16]; + + crypto_onetimeauth(correct, 0, inv, invoffset, inlen, k); + return verify_16.crypto_verify(h, hoffset, correct); + } + + static void add(int[] h, int[] c) { + int j; + int u = 0; + + for (j = 0; j < 17; ++j) { + u += h[j] + c[j]; + h[j] = u & 255; + u >>>= 8; + } + } + + static void squeeze(int[] h) { + int u = 0; + + for (int j = 0; j < 16; ++j) { + u += h[j]; + h[j] = u & 255; + u >>>= 8; + } + + u += h[16]; + h[16] = u & 3; + u = 5 * (u >>> 2); + + for (int j = 0; j < 16; ++j) { + u += h[j]; + h[j] = u & 255; + u >>>= 8; + } + + u += h[16]; + h[16] = u; + } + + static void freeze(int[] h) { + int[] horig = new int[17]; + + for (int j = 0; j < 17; ++j) horig[j] = h[j]; + + add(h, minusp); + + int negative = (int) (-(h[16] >>> 7)); + + for (int j = 0; j < 17; ++j) h[j] ^= negative & (horig[j] ^ h[j]); + } + + static void mulmod(int[] h, int[] r) { + int[] hr = new int[17]; + + for (int i = 0; i < 17; ++i) { + int u = 0; + + for (int j = 0; j <= i; ++j) u += h[j] * r[i - j]; + + for (int j = i + 1; j < 17; ++j) u += 320 * h[j] * r[i + 17 - j]; + + hr[i] = u; + } + + for (int i = 0; i < 17; ++i) h[i] = hr[i]; + + squeeze(h); + } + + public static int crypto_onetimeauth( + byte[] outv, int outvoffset, byte[] inv, int invoffset, long inlen, byte[] k) { + int j; + int[] r = new int[17]; + int[] h = new int[17]; + int[] c = new int[17]; + + r[0] = k[0] & 0xFF; + r[1] = k[1] & 0xFF; + r[2] = k[2] & 0xFF; + r[3] = k[3] & 15; + r[4] = k[4] & 252; + r[5] = k[5] & 0xFF; + r[6] = k[6] & 0xFF; + r[7] = k[7] & 15; + r[8] = k[8] & 252; + r[9] = k[9] & 0xFF; + r[10] = k[10] & 0xFF; + r[11] = k[11] & 15; + r[12] = k[12] & 252; + r[13] = k[13] & 0xFF; + r[14] = k[14] & 0xFF; + r[15] = k[15] & 15; + r[16] = 0; + + for (j = 0; j < 17; ++j) h[j] = 0; + + while (inlen > 0) { + for (j = 0; j < 17; ++j) c[j] = 0; + + for (j = 0; (j < 16) && (j < inlen); ++j) c[j] = inv[invoffset + j] & 0xff; + + c[j] = 1; + invoffset += j; + inlen -= j; + add(h, c); + mulmod(h, r); + } + + freeze(h); + + for (j = 0; j < 16; ++j) c[j] = k[j + 16] & 0xFF; + + c[16] = 0; + add(h, c); + + for (j = 0; j < 16; ++j) outv[j + outvoffset] = (byte) h[j]; + + return 0; + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java b/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java index 62c58d3..c2d80f7 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java @@ -1,17 +1,17 @@ // // 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 @@ -27,298 +27,277 @@ package com.neilalexander.jnacl.crypto; -public class salsa20 -{ - final int crypto_core_salsa20_ref_OUTPUTBYTES = 64; - final int crypto_core_salsa20_ref_INPUTBYTES = 16; - final int crypto_core_salsa20_ref_KEYBYTES = 32; - final int crypto_core_salsa20_ref_CONSTBYTES = 16; - final int crypto_stream_salsa20_ref_KEYBYTES = 32; - final int crypto_stream_salsa20_ref_NONCEBYTES = 8; - - final static int ROUNDS = 20; - - static long rotate(int u, int c) - { - return (u << c) | (u >>> (32 - c)); - } - - static int load_littleendian(byte[] x, int offset) - { - return ((int)(x[offset])&0xff) | - ((((int)(x[offset + 1])&0xff)) << 8) | - ((((int)(x[offset + 2])&0xff)) << 16) | - ((((int)(x[offset + 3])&0xff)) << 24); - } - - static void store_littleendian(byte[] x, int offset, int u) - { - x[offset] = (byte) u; u >>>= 8; - x[offset + 1] = (byte) u; u >>>= 8; - x[offset + 2] = (byte) u; u >>>= 8; - x[offset + 3] = (byte) u; - } - - public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c) - { - int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - int i; - - j0 = x0 = load_littleendian(c, 0); - j1 = x1 = load_littleendian(k, 0); - j2 = x2 = load_littleendian(k, 4); - j3 = x3 = load_littleendian(k, 8); - j4 = x4 = load_littleendian(k, 12); - j5 = x5 = load_littleendian(c, 4); - j6 = x6 = load_littleendian(inv, 0); - j7 = x7 = load_littleendian(inv, 4); - j8 = x8 = load_littleendian(inv, 8); - j9 = x9 = load_littleendian(inv, 12); - j10 = x10 = load_littleendian(c, 8); - j11 = x11 = load_littleendian(k, 16); - j12 = x12 = load_littleendian(k, 20); - j13 = x13 = load_littleendian(k, 24); - j14 = x14 = load_littleendian(k, 28); - j15 = x15 = load_littleendian(c, 12); - - for (i = ROUNDS; i > 0; i -= 2) - { - x4 ^= rotate(x0 + x12, 7); - x8 ^= rotate(x4 + x0, 9); - x12 ^= rotate(x8 + x4, 13); - x0 ^= rotate(x12 + x8, 18); - x9 ^= rotate(x5 + x1, 7); - x13 ^= rotate(x9 + x5, 9); - x1 ^= rotate(x13 + x9, 13); - x5 ^= rotate(x1 + x13, 18); - x14 ^= rotate(x10 + x6, 7); - x2 ^= rotate(x14 + x10, 9); - x6 ^= rotate(x2 + x14, 13); - x10 ^= rotate(x6 + x2, 18); - x3 ^= rotate(x15 + x11, 7); - x7 ^= rotate(x3 + x15, 9); - x11 ^= rotate(x7 + x3, 13); - x15 ^= rotate(x11 + x7, 18); - x1 ^= rotate(x0 + x3, 7); - x2 ^= rotate(x1 + x0, 9); - x3 ^= rotate(x2 + x1, 13); - x0 ^= rotate(x3 + x2, 18); - x6 ^= rotate(x5 + x4, 7); - x7 ^= rotate(x6 + x5, 9); - x4 ^= rotate(x7 + x6, 13); - x5 ^= rotate(x4 + x7, 18); - x11 ^= rotate(x10 + x9, 7); - x8 ^= rotate(x11 + x10, 9); - x9 ^= rotate(x8 + x11, 13); - x10 ^= rotate(x9 + x8, 18); - x12 ^= rotate(x15 + x14, 7); - x13 ^= rotate(x12 + x15, 9); - x14 ^= rotate(x13 + x12, 13); - x15 ^= rotate(x14 + x13, 18); - } - - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; - x4 += j4; - x5 += j5; - x6 += j6; - x7 += j7; - x8 += j8; - x9 += j9; - x10 += j10; - x11 += j11; - x12 += j12; - x13 += j13; - x14 += j14; - x15 += j15; - - store_littleendian(outv, 0, x0); - store_littleendian(outv, 4, x1); - store_littleendian(outv, 8, x2); - store_littleendian(outv, 12, x3); - store_littleendian(outv, 16, x4); - store_littleendian(outv, 20, x5); - store_littleendian(outv, 24, x6); - store_littleendian(outv, 28, x7); - store_littleendian(outv, 32, x8); - store_littleendian(outv, 36, x9); - store_littleendian(outv, 40, x10); - store_littleendian(outv, 44, x11); - store_littleendian(outv, 48, x12); - store_littleendian(outv, 52, x13); - store_littleendian(outv, 56, x14); - store_littleendian(outv, 60, x15); - - return 0; - } - - public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[] k) - { - byte[] inv = new byte[16]; - byte[] block = new byte[64]; - - int coffset = 0; - - if (clen == 0) - return 0; - - for (int i = 0; i < 8; ++i) - inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) - inv[i] = 0; - - while (clen >= 64) - { - salsa20.crypto_core(c, inv, k, xsalsa20.sigma); - - int u = 1; - - for (int i = 8; i < 16; ++i) - { - u += inv[i]&0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - clen -= 64; - coffset += 64; - } - - if (clen != 0) - { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < clen; ++i) - c[coffset + i] = block[i]; - } - - return 0; - } - - public static int crypto_stream_xor(byte[] c, byte[] m, int mlen, byte[] n, int noffset, byte[] k) - { - byte[] inv = new byte[16]; - byte[] block = new byte[64]; - - int coffset = 0; - int moffset = 0; - - if (mlen == 0) - return 0; - - for (int i = 0; i < 8; ++i) - inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) - inv[i] = 0; - - while (mlen >= 64) - { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < 64; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ block[i]); - - int u = 1; - - for (int i = 8; i < 16; ++i) - { - u += inv[i]&0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - mlen -= 64; - coffset += 64; - moffset += 64; - } - - if (mlen != 0) - { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < mlen; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ block[i]); - } - - return 0; - } - - public static int crypto_stream_xor_skip32(byte[] c0, byte[] c, int coffset, byte[] m, int moffset, int mlen, byte[] n, int noffset, byte[] k) - { - /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */ - - int u; - byte[] inv = new byte[16]; - byte[] prevblock = new byte[64]; - byte[] curblock = new byte[64]; - - if (mlen == 0) - return 0; - - for (int i = 0; i < 8; ++i) - inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) - inv[i] = 0; - - /* calculate first block */ - salsa20.crypto_core(prevblock, inv, k, xsalsa20.sigma); - - /* extract first 32 bytes of cipherstream into c0 */ - if (c0 != null) - System.arraycopy(prevblock, 0, c0, 0, 32); - - while (mlen >= 64) - { - u = 1; - for (int i = 8; i < 16; ++i) - { - u += inv[i]&0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); - - for (int i = 0; i < 32; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ prevblock[i+32]); - - for (int i = 32; i < 64; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ curblock[i-32]); - - mlen -= 64; - coffset += 64; - moffset += 64; - - byte[] tmpblock = prevblock; - prevblock = curblock; - curblock = tmpblock; - } - - if (mlen != 0) - { - u = 1; - for (int i = 8; i < 16; ++i) - { - u += inv[i]&0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); - - for (int i = 0; i < mlen && i < 32; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ prevblock[i+32]); - - for (int i = 32; i < mlen && i < 64; ++i) - c[coffset + i] = (byte)(m[moffset + i] ^ curblock[i-32]); - } - - return 0; +public class salsa20 { + final int crypto_core_salsa20_ref_OUTPUTBYTES = 64; + final int crypto_core_salsa20_ref_INPUTBYTES = 16; + final int crypto_core_salsa20_ref_KEYBYTES = 32; + final int crypto_core_salsa20_ref_CONSTBYTES = 16; + final int crypto_stream_salsa20_ref_KEYBYTES = 32; + final int crypto_stream_salsa20_ref_NONCEBYTES = 8; + + static final int ROUNDS = 20; + + static long rotate(int u, int c) { + return (u << c) | (u >>> (32 - c)); + } + + static int load_littleendian(byte[] x, int offset) { + return ((int) (x[offset]) & 0xff) + | ((((int) (x[offset + 1]) & 0xff)) << 8) + | ((((int) (x[offset + 2]) & 0xff)) << 16) + | ((((int) (x[offset + 3]) & 0xff)) << 24); + } + + static void store_littleendian(byte[] x, int offset, int u) { + x[offset] = (byte) u; + u >>>= 8; + x[offset + 1] = (byte) u; + u >>>= 8; + x[offset + 2] = (byte) u; + u >>>= 8; + x[offset + 3] = (byte) u; + } + + public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c) { + int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + int i; + + j0 = x0 = load_littleendian(c, 0); + j1 = x1 = load_littleendian(k, 0); + j2 = x2 = load_littleendian(k, 4); + j3 = x3 = load_littleendian(k, 8); + j4 = x4 = load_littleendian(k, 12); + j5 = x5 = load_littleendian(c, 4); + j6 = x6 = load_littleendian(inv, 0); + j7 = x7 = load_littleendian(inv, 4); + j8 = x8 = load_littleendian(inv, 8); + j9 = x9 = load_littleendian(inv, 12); + j10 = x10 = load_littleendian(c, 8); + j11 = x11 = load_littleendian(k, 16); + j12 = x12 = load_littleendian(k, 20); + j13 = x13 = load_littleendian(k, 24); + j14 = x14 = load_littleendian(k, 28); + j15 = x15 = load_littleendian(c, 12); + + for (i = ROUNDS; i > 0; i -= 2) { + x4 ^= rotate(x0 + x12, 7); + x8 ^= rotate(x4 + x0, 9); + x12 ^= rotate(x8 + x4, 13); + x0 ^= rotate(x12 + x8, 18); + x9 ^= rotate(x5 + x1, 7); + x13 ^= rotate(x9 + x5, 9); + x1 ^= rotate(x13 + x9, 13); + x5 ^= rotate(x1 + x13, 18); + x14 ^= rotate(x10 + x6, 7); + x2 ^= rotate(x14 + x10, 9); + x6 ^= rotate(x2 + x14, 13); + x10 ^= rotate(x6 + x2, 18); + x3 ^= rotate(x15 + x11, 7); + x7 ^= rotate(x3 + x15, 9); + x11 ^= rotate(x7 + x3, 13); + x15 ^= rotate(x11 + x7, 18); + x1 ^= rotate(x0 + x3, 7); + x2 ^= rotate(x1 + x0, 9); + x3 ^= rotate(x2 + x1, 13); + x0 ^= rotate(x3 + x2, 18); + x6 ^= rotate(x5 + x4, 7); + x7 ^= rotate(x6 + x5, 9); + x4 ^= rotate(x7 + x6, 13); + x5 ^= rotate(x4 + x7, 18); + x11 ^= rotate(x10 + x9, 7); + x8 ^= rotate(x11 + x10, 9); + x9 ^= rotate(x8 + x11, 13); + x10 ^= rotate(x9 + x8, 18); + x12 ^= rotate(x15 + x14, 7); + x13 ^= rotate(x12 + x15, 9); + x14 ^= rotate(x13 + x12, 13); + x15 ^= rotate(x14 + x13, 18); + } + + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; + + store_littleendian(outv, 0, x0); + store_littleendian(outv, 4, x1); + store_littleendian(outv, 8, x2); + store_littleendian(outv, 12, x3); + store_littleendian(outv, 16, x4); + store_littleendian(outv, 20, x5); + store_littleendian(outv, 24, x6); + store_littleendian(outv, 28, x7); + store_littleendian(outv, 32, x8); + store_littleendian(outv, 36, x9); + store_littleendian(outv, 40, x10); + store_littleendian(outv, 44, x11); + store_littleendian(outv, 48, x12); + store_littleendian(outv, 52, x13); + store_littleendian(outv, 56, x14); + store_littleendian(outv, 60, x15); + + return 0; + } + + public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[] k) { + byte[] inv = new byte[16]; + byte[] block = new byte[64]; + + int coffset = 0; + + if (clen == 0) return 0; + + for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; + + for (int i = 8; i < 16; ++i) inv[i] = 0; + + while (clen >= 64) { + salsa20.crypto_core(c, inv, k, xsalsa20.sigma); + + int u = 1; + + for (int i = 8; i < 16; ++i) { + u += inv[i] & 0xff; + inv[i] = (byte) u; + u >>>= 8; + } + + clen -= 64; + coffset += 64; + } + + if (clen != 0) { + salsa20.crypto_core(block, inv, k, xsalsa20.sigma); + + for (int i = 0; i < clen; ++i) c[coffset + i] = block[i]; + } + + return 0; + } + + public static int crypto_stream_xor( + byte[] c, byte[] m, int mlen, byte[] n, int noffset, byte[] k) { + byte[] inv = new byte[16]; + byte[] block = new byte[64]; + + int coffset = 0; + int moffset = 0; + + if (mlen == 0) return 0; + + for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; + + for (int i = 8; i < 16; ++i) inv[i] = 0; + + while (mlen >= 64) { + salsa20.crypto_core(block, inv, k, xsalsa20.sigma); + + for (int i = 0; i < 64; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ block[i]); + + int u = 1; + + for (int i = 8; i < 16; ++i) { + u += inv[i] & 0xff; + inv[i] = (byte) u; + u >>>= 8; + } + + mlen -= 64; + coffset += 64; + moffset += 64; + } + + if (mlen != 0) { + salsa20.crypto_core(block, inv, k, xsalsa20.sigma); + + for (int i = 0; i < mlen; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ block[i]); + } + + return 0; + } + + public static int crypto_stream_xor_skip32( + byte[] c0, + byte[] c, + int coffset, + byte[] m, + int moffset, + int mlen, + byte[] n, + int noffset, + byte[] k) { + /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */ + + int u; + byte[] inv = new byte[16]; + byte[] prevblock = new byte[64]; + byte[] curblock = new byte[64]; + + if (mlen == 0) return 0; + + for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; + + for (int i = 8; i < 16; ++i) inv[i] = 0; + + /* calculate first block */ + salsa20.crypto_core(prevblock, inv, k, xsalsa20.sigma); + + /* extract first 32 bytes of cipherstream into c0 */ + if (c0 != null) System.arraycopy(prevblock, 0, c0, 0, 32); + + while (mlen >= 64) { + u = 1; + for (int i = 8; i < 16; ++i) { + u += inv[i] & 0xff; + inv[i] = (byte) u; + u >>>= 8; + } + + salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); + + for (int i = 0; i < 32; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ prevblock[i + 32]); + + for (int i = 32; i < 64; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]); + + mlen -= 64; + coffset += 64; + moffset += 64; + + byte[] tmpblock = prevblock; + prevblock = curblock; + curblock = tmpblock; } + + if (mlen != 0) { + u = 1; + for (int i = 8; i < 16; ++i) { + u += inv[i] & 0xff; + inv[i] = (byte) u; + u >>>= 8; + } + + salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); + + for (int i = 0; i < mlen && i < 32; ++i) + c[coffset + i] = (byte) (m[moffset + i] ^ prevblock[i + 32]); + + for (int i = 32; i < mlen && i < 64; ++i) + c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]); + } + + return 0; + } } diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java b/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java index 443c641..19cfdf8 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,17 +1,17 @@ // // 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 @@ -27,17 +27,14 @@ package com.neilalexander.jnacl.crypto; -public class verify_16 -{ - final int crypto_verify_16_ref_BYTES = 16; +public class verify_16 { + final int crypto_verify_16_ref_BYTES = 16; + + public static int crypto_verify(byte[] x, int xoffset, byte[] y) { + int differentbits = 0; + + for (int i = 0; i < 15; i++) differentbits |= ((int) (x[xoffset + i] ^ y[i])) & 0xff; - 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; - } + 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..fd7025f 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java @@ -1,17 +1,17 @@ // // 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 @@ -27,39 +27,50 @@ package com.neilalexander.jnacl.crypto; -public class xsalsa20 -{ - final int crypto_stream_xsalsa20_ref_KEYBYTES = 32; - final int crypto_stream_xsalsa20_ref_NONCEBYTES = 24; - - public final static byte[] sigma = {(byte) 'e', (byte) 'x', (byte) 'p', (byte) 'a', - (byte) 'n', (byte) 'd', (byte) ' ', (byte) '3', - (byte) '2', (byte) '-', (byte) 'b', (byte) 'y', - (byte) 't', (byte) 'e', (byte) ' ', (byte) 'k'}; - - public static int crypto_stream(byte[] c, int clen, byte[] n, byte[] k) - { - byte[] subkey = new byte[32]; - - hsalsa20.crypto_core(subkey, n, k, sigma); - return salsa20.crypto_stream(c, clen, n, 16, subkey); - } - - public static int crypto_stream_xor(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) - { - byte[] subkey = new byte[32]; - - hsalsa20.crypto_core(subkey, n, k, sigma); - return salsa20.crypto_stream_xor(c, m, (int) mlen, n, 16, subkey); - } +public class xsalsa20 { + final int crypto_stream_xsalsa20_ref_KEYBYTES = 32; + final int crypto_stream_xsalsa20_ref_NONCEBYTES = 24; + + public static final byte[] sigma = { + (byte) 'e', + (byte) 'x', + (byte) 'p', + (byte) 'a', + (byte) 'n', + (byte) 'd', + (byte) ' ', + (byte) '3', + (byte) '2', + (byte) '-', + (byte) 'b', + (byte) 'y', + (byte) 't', + (byte) 'e', + (byte) ' ', + (byte) 'k' + }; + + public static int crypto_stream(byte[] c, int clen, byte[] n, byte[] k) { + byte[] subkey = new byte[32]; + + hsalsa20.crypto_core(subkey, n, k, sigma); + return salsa20.crypto_stream(c, clen, n, 16, subkey); + } + + public static int crypto_stream_xor(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) { + byte[] subkey = new byte[32]; + + hsalsa20.crypto_core(subkey, n, k, sigma); + return salsa20.crypto_stream_xor(c, m, (int) mlen, n, 16, subkey); + } - public static int crypto_stream_xor_skip32(byte[] c0, byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) - { - /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */ + 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..bf84a73 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java @@ -1,17 +1,17 @@ // // 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 @@ -27,76 +27,67 @@ package com.neilalexander.jnacl.crypto; -public class xsalsa20poly1305 -{ - final int crypto_secretbox_KEYBYTES = 32; - final int crypto_secretbox_NONCEBYTES = 24; - final int crypto_secretbox_ZEROBYTES = 32; - final int crypto_secretbox_BOXZEROBYTES = 16; - - static public int crypto_secretbox(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) - { - if (mlen < 32) - return -1; - - xsalsa20.crypto_stream_xor(c, m, mlen, n, k); - poly1305.crypto_onetimeauth(c, 16, c, 32, mlen - 32, c); - - for (int i = 0; i < 16; ++i) - c[i] = 0; - - return 0; - } - - static public int crypto_secretbox_nopad(byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) - { - /* variant of crypto_secretbox that doesn't require 32 zero bytes before m and doesn't output - * 16 zero bytes before c */ - byte[] c0 = new byte[32]; - - xsalsa20.crypto_stream_xor_skip32(c0, c, coffset+16, m, moffset, mlen, n, k); - poly1305.crypto_onetimeauth(c, coffset, c, coffset+16, mlen, c0); - - return 0; - } - - static public int crypto_secretbox_open(byte[] m, byte[] c, long clen, byte[] n, byte[] k) - { - if (clen < 32) - return -1; - - byte[] subkeyp = new byte[32]; - - xsalsa20.crypto_stream(subkeyp, 32, n, k); - - if (poly1305.crypto_onetimeauth_verify(c, 16, c, 32, clen - 32, subkeyp) != 0) - return -1; - - xsalsa20.crypto_stream_xor(m, c, clen, n, k); - - for (int i = 0; i < 32; ++i) - m[i] = 0; - - return 0; - } - - static public int crypto_secretbox_open_nopad(byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) - { - /* variant of crypto_secretbox_open that doesn't require 16 zero bytes before c and doesn't output - * 32 zero bytes before m */ - - if (clen < 16) - return -1; - - byte[] subkeyp = new byte[32]; - - xsalsa20.crypto_stream(subkeyp, 32, n, k); - - if (poly1305.crypto_onetimeauth_verify(c, coffset, c, coffset+16, clen - 16, subkeyp) != 0) - return -1; - - xsalsa20.crypto_stream_xor_skip32(null, m, moffset, c, coffset+16, clen - 16, n, k); - - return 0; - } +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; + + public static 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; + } + + public static int crypto_secretbox_nopad( + byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) { + /* variant of crypto_secretbox that doesn't require 32 zero bytes before m and doesn't output + * 16 zero bytes before c */ + byte[] c0 = new byte[32]; + + xsalsa20.crypto_stream_xor_skip32(c0, c, coffset + 16, m, moffset, mlen, n, k); + poly1305.crypto_onetimeauth(c, coffset, c, coffset + 16, mlen, c0); + + return 0; + } + + public static 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; + } + + public static int crypto_secretbox_open_nopad( + byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) { + /* variant of crypto_secretbox_open that doesn't require 16 zero bytes before c and doesn't output + * 32 zero bytes before m */ + + if (clen < 16) return -1; + + byte[] subkeyp = new byte[32]; + + xsalsa20.crypto_stream(subkeyp, 32, n, k); + + if (poly1305.crypto_onetimeauth_verify(c, coffset, c, coffset + 16, clen - 16, subkeyp) != 0) + return -1; + + xsalsa20.crypto_stream_xor_skip32(null, m, moffset, c, coffset + 16, clen - 16, n, k); + + return 0; + } } From ebd4f4b0d961adc551e19b0bb696848850faa70a Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 26 Oct 2022 22:31:16 +0200 Subject: [PATCH 03/11] Code clean-up --- source/pom.xml | 5 +-- .../java/ch/threema/apitool/APIConnector.java | 23 +++++++--- .../java/ch/threema/apitool/ConsoleMain.java | 44 +++++++++---------- .../java/ch/threema/apitool/CryptTool.java | 29 +++++++----- .../java/ch/threema/apitool/DataUtils.java | 8 +++- .../apitool/console/commands/Command.java | 21 ++++++--- .../console/commands/CreditsCommand.java | 2 +- .../commands/DecryptAndDownloadCommand.java | 10 +++-- .../console/commands/DecryptCommand.java | 4 +- .../commands/DerivePublicKeyCommand.java | 2 +- .../console/commands/EncryptCommand.java | 4 +- .../console/commands/FetchPublicKey.java | 2 +- .../commands/GenerateKeyPairCommand.java | 9 ++-- .../console/commands/HashEmailCommand.java | 2 +- .../console/commands/HashPhoneCommand.java | 2 +- .../commands/SendE2EFileMessageCommand.java | 4 +- .../commands/SendE2EImageMessageCommand.java | 4 +- .../console/commands/fields/KeyField.java | 6 +-- .../apitool/messages/DeliveryReceipt.java | 4 +- .../apitool/messages/ImageMessage.java | 6 ++- .../jnacl/crypto/curve25519.java | 4 +- .../crypto/curve25519xsalsa20poly1305.java | 8 ++-- .../neilalexander/jnacl/crypto/hsalsa20.java | 8 ++-- .../neilalexander/jnacl/crypto/poly1305.java | 2 +- .../neilalexander/jnacl/crypto/salsa20.java | 8 ++-- .../neilalexander/jnacl/crypto/verify_16.java | 4 +- 26 files changed, 129 insertions(+), 96 deletions(-) diff --git a/source/pom.xml b/source/pom.xml index 4e93c92..2786124 100644 --- a/source/pom.xml +++ b/source/pom.xml @@ -37,7 +37,6 @@ 3.23.1 test
- commons-io commons-io @@ -45,8 +44,8 @@ org.apache.commons - commons-lang3 - 3.12.0 + commons-text + 1.10.0 com.google.code.gson diff --git a/source/src/main/java/ch/threema/apitool/APIConnector.java b/source/src/main/java/ch/threema/apitool/APIConnector.java index 8e80538..06a8a8a 100644 --- a/source/src/main/java/ch/threema/apitool/APIConnector.java +++ b/source/src/main/java/ch/threema/apitool/APIConnector.java @@ -24,18 +24,27 @@ package ch.threema.apitool; -import ch.threema.apitool.results.CapabilityResult; -import ch.threema.apitool.results.EncryptResult; -import ch.threema.apitool.results.UploadResult; - -import javax.net.ssl.HttpsURLConnection; -import java.io.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; + +import ch.threema.apitool.results.CapabilityResult; +import ch.threema.apitool.results.EncryptResult; +import ch.threema.apitool.results.UploadResult; + /** Facilitates HTTPS communication with the Threema Message API. */ public class APIConnector { private static final int BUFFER_SIZE = 16384; @@ -347,7 +356,7 @@ public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) thr } private Map makeRequestParams() { - Map postParams = new HashMap(); + Map postParams = new HashMap<>(); postParams.put("from", apiIdentity); postParams.put("secret", secret); diff --git a/source/src/main/java/ch/threema/apitool/ConsoleMain.java b/source/src/main/java/ch/threema/apitool/ConsoleMain.java index 846ad8a..17b5d48 100644 --- a/source/src/main/java/ch/threema/apitool/ConsoleMain.java +++ b/source/src/main/java/ch/threema/apitool/ConsoleMain.java @@ -1,6 +1,4 @@ /* - * $Id$ - * * The MIT License (MIT) * Copyright (c) 2015 Threema GmbH * @@ -27,9 +25,8 @@ import java.util.List; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.text.WordUtils; +import org.apache.commons.text.StringEscapeUtils; import ch.threema.apitool.console.commands.CapabilityCommand; import ch.threema.apitool.console.commands.Command; @@ -59,7 +56,7 @@ static class Commands { protected final List commandGroups = new ArrayList<>(); public CommandGroup create(String description) { - CommandGroup g = new CommandGroup(description); + var g = new CommandGroup(description); this.commandGroups.add(g); return g; } @@ -67,7 +64,7 @@ public CommandGroup create(String description) { public ArgumentCommand find(String... arguments) { if (arguments.length > 0) { for (CommandGroup g : this.commandGroups) { - ArgumentCommand c = g.find(arguments); + var c = g.find(arguments); if (c != null) { return c; } @@ -92,17 +89,16 @@ public CommandGroup add(Command command, String... arguments) { public ArgumentCommand find(String... arguments) { ArgumentCommand matchedArgumentCommand = null; - int argMatchedSize = -1; + var argMatchedSize = -1; for (ArgumentCommand c : this.argumentCommands) { - boolean matched = true; - int matchedSize = 0; - for (int n = 0; n < c.arguments.length; n++) { + var matched = true; + var matchedSize = 0; + for (var n = 0; n < c.arguments.length; n++) { if (n > arguments.length || !c.arguments[n].equals(arguments[n])) { matched = false; break; - } else { - matchedSize++; } + matchedSize++; } if (matched && matchedSize > argMatchedSize) { @@ -129,8 +125,7 @@ public void run(String[] givenArguments) throws Exception { } this.command.run( - (String[]) - ArrayUtils.subarray(givenArguments, this.arguments.length, givenArguments.length)); + ArrayUtils.subarray(givenArguments, this.arguments.length, givenArguments.length)); } } @@ -160,9 +155,9 @@ public static void main(String[] args) throws Exception { .add(new DecryptAndDownloadCommand(), "-D") .add(new CreditsCommand(), "-C"); - ArgumentCommand argumentCommand = commands.find(args); + var argumentCommand = commands.find(args); if (argumentCommand == null) { - usage(args.length == 1 && args[0].equals("html")); + usage(args.length == 1 && "html".equals(args[0])); } else { argumentCommand.run(args); } @@ -184,20 +179,20 @@ private static void usage(boolean htmlOutput) { System.out.println("(file contents also in hex with the prefix).\n"); } - String groupDescriptionTemplate = + var groupDescriptionTemplate = htmlOutput ? "

%s

\n" : "\n%s\n" + StringUtils.repeat("-", 80) + "\n\n"; - String commandTemplate = + var commandTemplate = htmlOutput ? "
java -jar threema-msgapi-tool.jar %s
\n" : "%s\n"; for (CommandGroup commandGroup : commands.commandGroups) { System.out.format(groupDescriptionTemplate, commandGroup.description); for (ArgumentCommand argumentCommand : commandGroup.argumentCommands) { - StringBuilder command = new StringBuilder(); - for (int n = 0; n < argumentCommand.arguments.length; n++) { - command.append(argumentCommand.arguments[n]).append(" "); + var command = new StringBuilder(); + for (String argument : argumentCommand.arguments) { + command.append(argument).append(" "); } - String argumentDescription = argumentCommand.command.getUsageArguments(); + var argumentDescription = argumentCommand.command.getUsageArguments(); if (htmlOutput) { System.out.format("

%s

\n", argumentCommand.command.getSubject()); argumentDescription = StringEscapeUtils.escapeHtml3(argumentDescription); @@ -206,11 +201,12 @@ private static void usage(boolean htmlOutput) { System.out.format(commandTemplate, command.toString().trim()); - String description = argumentCommand.command.getUsageDescription(); + var description = argumentCommand.command.getUsageDescription(); if (htmlOutput) { System.out.format("

%s

\n\n", description); } else { - System.out.println(" " + WordUtils.wrap(description, 76, "\n ", false)); + System.out.println( + " " + org.apache.commons.text.WordUtils.wrap(description, 76, "\n ", false)); System.out.println(""); } } diff --git a/source/src/main/java/ch/threema/apitool/CryptTool.java b/source/src/main/java/ch/threema/apitool/CryptTool.java index 099b23d..f800f3a 100644 --- a/source/src/main/java/ch/threema/apitool/CryptTool.java +++ b/source/src/main/java/ch/threema/apitool/CryptTool.java @@ -24,22 +24,29 @@ package ch.threema.apitool; +import java.io.UnsupportedEncodingException; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.io.EndianUtils; + +import com.neilalexander.jnacl.NaCl; + import ch.threema.apitool.exceptions.BadMessageException; import ch.threema.apitool.exceptions.DecryptionFailedException; import ch.threema.apitool.exceptions.MessageParseException; import ch.threema.apitool.exceptions.UnsupportedMessageTypeException; -import ch.threema.apitool.messages.*; +import ch.threema.apitool.messages.DeliveryReceipt; +import ch.threema.apitool.messages.FileMessage; +import ch.threema.apitool.messages.ImageMessage; +import ch.threema.apitool.messages.TextMessage; +import ch.threema.apitool.messages.ThreemaMessage; import ch.threema.apitool.results.EncryptResult; import ch.threema.apitool.results.UploadResult; -import com.neilalexander.jnacl.NaCl; -import org.apache.commons.io.EndianUtils; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; -import java.util.LinkedList; -import java.util.List; /** Contains static methods to do various Threema cryptography related tasks. */ public class CryptTool { @@ -303,7 +310,7 @@ public static ThreemaMessage decryptMessage( DeliveryReceipt.Type receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF); if (receiptType == null) throw new BadMessageException(); - List messageIds = new LinkedList(); + List messageIds = new LinkedList<>(); int numMsgIds = ((realDataLength - 2) / MessageId.MESSAGE_ID_LEN); for (int i = 0; i < numMsgIds; i++) { diff --git a/source/src/main/java/ch/threema/apitool/DataUtils.java b/source/src/main/java/ch/threema/apitool/DataUtils.java index 351f473..994722f 100644 --- a/source/src/main/java/ch/threema/apitool/DataUtils.java +++ b/source/src/main/java/ch/threema/apitool/DataUtils.java @@ -24,9 +24,13 @@ package ch.threema.apitool; -import ch.threema.apitool.exceptions.InvalidKeyException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; -import java.io.*; +import ch.threema.apitool.exceptions.InvalidKeyException; public class DataUtils { diff --git a/source/src/main/java/ch/threema/apitool/console/commands/Command.java b/source/src/main/java/ch/threema/apitool/console/commands/Command.java index 3d40faf..6ef693e 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/Command.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/Command.java @@ -24,14 +24,25 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.APIConnector; -import ch.threema.apitool.PublicKeyStore; -import ch.threema.apitool.console.commands.fields.*; - -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.LinkedList; import java.util.List; +import ch.threema.apitool.APIConnector; +import ch.threema.apitool.PublicKeyStore; +import ch.threema.apitool.console.commands.fields.ByteArrayField; +import ch.threema.apitool.console.commands.fields.Field; +import ch.threema.apitool.console.commands.fields.FileField; +import ch.threema.apitool.console.commands.fields.FolderField; +import ch.threema.apitool.console.commands.fields.PrivateKeyField; +import ch.threema.apitool.console.commands.fields.PublicKeyField; +import ch.threema.apitool.console.commands.fields.TextField; +import ch.threema.apitool.console.commands.fields.ThreemaIDField; + public abstract class Command { private final List fields = new LinkedList<>(); private final String subject; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java index df75dd5..0bc9c89 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java @@ -49,7 +49,7 @@ protected void execute() throws Exception { System.out.println("Remaining credits: " + credits); } else { System.out.println("Error fetching credits"); - ; + } } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java index 3701afb..22b101c 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java @@ -24,12 +24,16 @@ package ch.threema.apitool.console.commands; +import java.nio.file.Path; + import ch.threema.apitool.DataUtils; -import ch.threema.apitool.console.commands.fields.*; +import ch.threema.apitool.console.commands.fields.ByteArrayField; +import ch.threema.apitool.console.commands.fields.FolderField; +import ch.threema.apitool.console.commands.fields.PrivateKeyField; +import ch.threema.apitool.console.commands.fields.TextField; +import ch.threema.apitool.console.commands.fields.ThreemaIDField; import ch.threema.apitool.helpers.E2EHelper; -import java.nio.file.Path; - public class DecryptAndDownloadCommand extends Command { private final ThreemaIDField threemaId; private final ThreemaIDField fromField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java index df77a00..ef795a5 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java @@ -24,11 +24,11 @@ package ch.threema.apitool.console.commands; +import ch.threema.apitool.CryptTool; +import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.ByteArrayField; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.PublicKeyField; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.messages.ThreemaMessage; public class DecryptCommand extends Command { diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java index 37a770e..b3099ef 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.CryptTool; import ch.threema.apitool.Key; +import ch.threema.apitool.console.commands.fields.PrivateKeyField; public class DerivePublicKeyCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java index 91d3a9d..38cb973 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java @@ -24,10 +24,10 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.PublicKeyField; import ch.threema.apitool.CryptTool; import ch.threema.apitool.DataUtils; +import ch.threema.apitool.console.commands.fields.PrivateKeyField; +import ch.threema.apitool.console.commands.fields.PublicKeyField; import ch.threema.apitool.results.EncryptResult; public class EncryptCommand extends Command { diff --git a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java b/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java index c6c774c..b0b9b9c 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java @@ -25,9 +25,9 @@ package ch.threema.apitool.console.commands; import ch.threema.apitool.APIConnector; +import ch.threema.apitool.Key; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.Key; public class FetchPublicKey extends Command { private final ThreemaIDField threemaIdField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java index 2583ea5..0829ee3 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java @@ -24,13 +24,14 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; +import java.io.File; + +import com.neilalexander.jnacl.NaCl; + import ch.threema.apitool.CryptTool; import ch.threema.apitool.DataUtils; import ch.threema.apitool.Key; -import com.neilalexander.jnacl.NaCl; - -import java.io.File; +import ch.threema.apitool.console.commands.fields.TextField; public class GenerateKeyPairCommand extends Command { private final TextField privateKeyPath; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java index aa5d915..7860e9c 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.CryptTool; import ch.threema.apitool.DataUtils; +import ch.threema.apitool.console.commands.fields.TextField; public class HashEmailCommand extends Command { private final TextField emailField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java index dba4996..0d7be86 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.CryptTool; import ch.threema.apitool.DataUtils; +import ch.threema.apitool.console.commands.fields.TextField; public class HashPhoneCommand extends Command { private final TextField phoneNo; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java index 0b0a927..82e103f 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java @@ -24,14 +24,14 @@ package ch.threema.apitool.console.commands; +import java.io.File; + import ch.threema.apitool.console.commands.fields.FileField; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; import ch.threema.apitool.helpers.E2EHelper; -import java.io.File; - public class SendE2EFileMessageCommand extends Command { private final ThreemaIDField threemaId; private final ThreemaIDField fromField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java index f36a9d4..0640a44 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java @@ -24,14 +24,14 @@ package ch.threema.apitool.console.commands; +import java.nio.file.Path; + import ch.threema.apitool.console.commands.fields.FolderField; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; import ch.threema.apitool.helpers.E2EHelper; -import java.nio.file.Path; - public class SendE2EImageMessageCommand extends Command { private final ThreemaIDField toField; private final ThreemaIDField fromField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java b/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java index bc814f7..b9e644e 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java +++ b/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java @@ -24,13 +24,13 @@ package ch.threema.apitool.console.commands.fields; +import java.io.File; +import java.io.IOException; + import ch.threema.apitool.DataUtils; import ch.threema.apitool.Key; import ch.threema.apitool.exceptions.InvalidKeyException; -import java.io.File; -import java.io.IOException; - public abstract class KeyField extends Field { public KeyField(String key, boolean required) { super(key, required); diff --git a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java b/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java index e0b16c7..209bdc6 100644 --- a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java +++ b/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java @@ -24,10 +24,10 @@ package ch.threema.apitool.messages; -import ch.threema.apitool.MessageId; - import java.util.List; +import ch.threema.apitool.MessageId; + /** * A delivery receipt message that can be sent/received with end-to-end encryption via Threema. Each * delivery receipt message confirms the receipt of one or multiple regular messages. diff --git a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java b/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java index e58b667..0612d49 100644 --- a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java +++ b/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java @@ -24,10 +24,12 @@ package ch.threema.apitool.messages; -import ch.threema.apitool.DataUtils; -import com.neilalexander.jnacl.NaCl; import org.apache.commons.io.EndianUtils; +import com.neilalexander.jnacl.NaCl; + +import ch.threema.apitool.DataUtils; + /** An image message that can be sent/received with end-to-end encryption via Threema. */ public class ImageMessage extends ThreemaMessage { 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 f1c7d7f..1276233 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java @@ -102,7 +102,7 @@ static void freeze(int[] a, int aoffset) { add(a, 0, a, 0, minuspp, 0); - int negative = (int) (-((a[aoffset + 31] >>> 7) & 1)); + int negative = (-((a[aoffset + 31] >>> 7) & 1)); for (int j = 0; j < 32; ++j) a[aoffset + j] ^= negative & (aorig[j] ^ a[aoffset + j]); } @@ -213,7 +213,7 @@ static void mainloop(int[] work, byte[] e) { int[] workp = work, sp = s, rp = r; for (int pos = 254; pos >= 0; --pos) { - int b = ((int) ((e[pos / 8] & 0xFF) >>> (pos & 7))); + int b = ((e[pos / 8] & 0xFF) >>> (pos & 7)); b &= 1; select(xzmb, xzm1b, xzm, xzm1, b); add(a0, 0, xzmb, 0, xzmbp, 32); 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 fab9ce3..07f8b81 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java @@ -82,18 +82,18 @@ public static int crypto_box_open_afternm_nopad( } 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); + return crypto_box_afternm(c, m, 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); + return crypto_box_open_afternm(m, c, c.length, n, k); } public static int crypto_box(byte[] c, byte[] m, byte[] n, byte[] pk, byte[] sk) { - return crypto_box(c, m, (long) m.length, n, pk, sk); + return crypto_box(c, m, m.length, n, pk, sk); } public static int crypto_box_open(byte[] m, byte[] c, byte[] n, byte[] pk, byte[] sk) { - return crypto_box_open(m, c, (long) c.length, n, pk, sk); + return crypto_box_open(m, c, 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 77be278..20b894b 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java @@ -35,10 +35,10 @@ static int rotate(int u, int 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); + return ((x[offset]) & 0xff) + | ((((x[offset + 1]) & 0xff)) << 8) + | ((((x[offset + 2]) & 0xff)) << 16) + | ((((x[offset + 3]) & 0xff)) << 24); } static void store_littleendian(byte[] x, int offset, int u) { 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 c52f216..a9cffee 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java @@ -82,7 +82,7 @@ static void freeze(int[] h) { add(h, minusp); - int negative = (int) (-(h[16] >>> 7)); + int negative = (-(h[16] >>> 7)); for (int j = 0; j < 17; ++j) h[j] ^= negative & (horig[j] ^ h[j]); } 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 c2d80f7..118d895 100644 --- a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java +++ b/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java @@ -42,10 +42,10 @@ static long rotate(int u, int 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); + return ((x[offset]) & 0xff) + | ((((x[offset + 1]) & 0xff)) << 8) + | ((((x[offset + 2]) & 0xff)) << 16) + | ((((x[offset + 3]) & 0xff)) << 24); } static void store_littleendian(byte[] x, int offset, int u) { 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 19cfdf8..7599552 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 @@ -33,8 +33,8 @@ public class verify_16 { public static int crypto_verify(byte[] x, int xoffset, byte[] y) { int differentbits = 0; - for (int i = 0; i < 15; i++) differentbits |= ((int) (x[xoffset + i] ^ y[i])) & 0xff; + for (int i = 0; i < 15; i++) differentbits |= (x[xoffset + i] ^ y[i]) & 0xff; - return (1 & (((int) differentbits - 1) >>> 8)) - 1; + return (1 & ((differentbits - 1) >>> 8)) - 1; } } From 827d2638fbfd9f6169687a6594e524c3f4bf030f Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 26 Oct 2022 22:33:54 +0200 Subject: [PATCH 04/11] fixes workflows --- .github/workflows/maven-publish.yml | 4 ++-- .github/workflows/maven.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index b41ce36..9aae6fa 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' server-id: github # Value of the distributionManagement/repository/id field of the pom.xml settings-path: ${{ github.workspace }} # location for the settings.xml file diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d8d81a8..f32797f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,10 +21,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: maven - name: Build with Maven From 4ee2f71ff8e9a52b68e32d1d853aca3ce6587b49 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 26 Oct 2022 23:50:07 +0200 Subject: [PATCH 05/11] Moves API into its own maven module --- api/pom.xml | 80 ++++++ .../java/com/neilalexander/jnacl/NaCl.java | 0 .../jnacl/crypto/curve25519.java | 0 .../crypto/curve25519xsalsa20poly1305.java | 0 .../neilalexander/jnacl/crypto/hsalsa20.java | 0 .../neilalexander/jnacl/crypto/poly1305.java | 0 .../neilalexander/jnacl/crypto/salsa20.java | 0 .../neilalexander/jnacl/crypto/verify_16.java | 0 .../neilalexander/jnacl/crypto/xsalsa20.java | 0 .../jnacl/crypto/xsalsa20poly1305.java | 0 .../threema/api}/APIConnector.java | 126 +++++---- .../klesatschke/threema/api}/CryptTool.java | 262 +++++++++--------- .../klesatschke/threema/api}/DataUtils.java | 57 ++-- .../net/klesatschke/threema/api}/Key.java | 39 ++- .../klesatschke/threema/api}/MessageId.java | 8 +- .../threema/api}/PublicKeyStore.java | 33 +-- .../api}/exceptions/BadMessageException.java | 2 +- .../exceptions/DecryptionFailedException.java | 2 +- .../InvalidCommandFieldValueException.java | 2 +- .../api}/exceptions/InvalidKeyException.java | 2 +- .../exceptions/MessageParseException.java | 2 +- .../api}/exceptions/NotAllowedException.java | 2 +- .../RequiredCommandFieldMissingException.java | 2 +- .../UnsupportedMessageTypeException.java | 2 +- .../api}/messages/DeliveryReceipt.java | 16 +- .../threema/api}/messages/FileMessage.java | 14 +- .../threema/api}/messages/ImageMessage.java | 8 +- .../threema/api}/messages/TextMessage.java | 2 +- .../threema/api}/messages/ThreemaMessage.java | 4 +- .../api}/results/CapabilityResult.java | 20 +- .../threema/api}/results/EncryptResult.java | 29 +- .../threema/api}/results/UploadResult.java | 20 +- .../net/klesatschke/threema/api}/Common.java | 2 +- .../threema/api}/CryptToolTest.java | 14 +- .../net/klesatschke/threema/api}/KeyTest.java | 16 +- cli/pom.xml | 94 +++++++ .../java/ch/threema/apitool/ConsoleMain.java | 2 + .../console/commands/CapabilityCommand.java | 2 +- .../apitool/console/commands/Command.java | 4 +- .../console/commands/CreditsCommand.java | 0 .../commands/DecryptAndDownloadCommand.java | 2 +- .../console/commands/DecryptCommand.java | 6 +- .../commands/DerivePublicKeyCommand.java | 11 +- .../console/commands/EncryptCommand.java | 6 +- .../console/commands/FetchPublicKey.java | 16 +- .../commands/GenerateKeyPairCommand.java | 15 +- .../console/commands/HashEmailCommand.java | 4 +- .../console/commands/HashPhoneCommand.java | 4 +- .../console/commands/IDLookupByEmail.java | 2 +- .../console/commands/IDLookupByPhoneNo.java | 2 +- .../commands/SendE2EFileMessageCommand.java | 0 .../commands/SendE2EImageMessageCommand.java | 0 .../commands/SendE2ETextMessageCommand.java | 0 .../commands/SendSimpleMessageCommand.java | 2 +- .../commands/fields/ByteArrayField.java | 2 +- .../console/commands/fields/Field.java | 4 +- .../console/commands/fields/FileField.java | 0 .../console/commands/fields/FolderField.java | 0 .../console/commands/fields/KeyField.java | 15 +- .../commands/fields/PrivateKeyField.java | 6 +- .../commands/fields/PublicKeyField.java | 6 +- .../console/commands/fields/TextField.java | 0 .../commands/fields/ThreemaIDField.java | 0 .../ch/threema/apitool/helpers/E2EHelper.java | 94 +++---- pom.xml | 46 +++ 65 files changed, 652 insertions(+), 459 deletions(-) create mode 100644 api/pom.xml rename {source => api}/src/main/java/com/neilalexander/jnacl/NaCl.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java (100%) rename {source => api}/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java (100%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/APIConnector.java (75%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/CryptTool.java (70%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/DataUtils.java (75%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/Key.java (77%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/MessageId.java (92%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/PublicKeyStore.java (82%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/BadMessageException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/DecryptionFailedException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/InvalidCommandFieldValueException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/InvalidKeyException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/MessageParseException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/NotAllowedException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/RequiredCommandFieldMissingException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/exceptions/UnsupportedMessageTypeException.java (96%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/messages/DeliveryReceipt.java (91%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/messages/FileMessage.java (92%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/messages/ImageMessage.java (93%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/messages/TextMessage.java (97%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/messages/ThreemaMessage.java (93%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/results/CapabilityResult.java (88%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/results/EncryptResult.java (72%) rename {source/src/main/java/ch/threema/apitool => api/src/main/java/net/klesatschke/threema/api}/results/UploadResult.java (78%) rename {source/src/test/java/ch/threema/apitool => api/src/test/java/net/klesatschke/threema/api}/Common.java (98%) rename {source/src/test/java/ch/threema/apitool => api/src/test/java/net/klesatschke/threema/api}/CryptToolTest.java (91%) rename {source/src/test/java/ch/threema/apitool => api/src/test/java/net/klesatschke/threema/api}/KeyTest.java (86%) create mode 100644 cli/pom.xml rename {source => cli}/src/main/java/ch/threema/apitool/ConsoleMain.java (98%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java (97%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/Command.java (98%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java (98%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java (94%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java (84%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java (93%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java (81%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java (81%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java (95%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java (95%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java (97%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java (97%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java (97%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java (96%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/Field.java (92%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java (78%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java (89%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java (89%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java (100%) rename {source => cli}/src/main/java/ch/threema/apitool/helpers/E2EHelper.java (74%) create mode 100644 pom.xml diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..1b0b29a --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + + net.klesatschke.threema + msgapi-sdk-java + 1.1.5-SNAPSHOT + + api + Threema Messsge Gateway API + + + MIT-License + http://opensource.org/licenses/mit-license.php + repo + + + https://github.com/lordyavin/threema-msgapi-sdk-java + + UTF-8 + ${source.encoding} + ${source.encoding} + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${source.encoding} + 17 + 17 + + + org.projectlombok + lombok + + + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.assertj + assertj-core + test + + + commons-io + commons-io + + + org.apache.commons + commons-text + + + com.google.code.gson + gson + + + org.projectlombok + lombok + 1.18.22 + provided + + + + + github + GitHub lordyavin Apache Maven Packages + https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java + + + \ No newline at end of file diff --git a/source/src/main/java/com/neilalexander/jnacl/NaCl.java b/api/src/main/java/com/neilalexander/jnacl/NaCl.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/NaCl.java rename to api/src/main/java/com/neilalexander/jnacl/NaCl.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java b/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java b/api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java diff --git a/source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java similarity index 100% rename from source/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java rename to api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java diff --git a/source/src/main/java/ch/threema/apitool/APIConnector.java b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java similarity index 75% rename from source/src/main/java/ch/threema/apitool/APIConnector.java rename to api/src/main/java/net/klesatschke/threema/api/APIConnector.java index 06a8a8a..df9b83c 100644 --- a/source/src/main/java/ch/threema/apitool/APIConnector.java +++ b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; @@ -31,19 +31,18 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.SecureRandom; -import java.util.HashMap; import java.util.Map; import javax.net.ssl.HttpsURLConnection; -import ch.threema.apitool.results.CapabilityResult; -import ch.threema.apitool.results.EncryptResult; -import ch.threema.apitool.results.UploadResult; +import net.klesatschke.threema.api.results.CapabilityResult; +import net.klesatschke.threema.api.results.EncryptResult; +import net.klesatschke.threema.api.results.UploadResult; /** Facilitates HTTPS communication with the Threema Message API. */ public class APIConnector { @@ -96,7 +95,7 @@ public APIConnector( */ public String sendTextMessageSimple(String to, String text) throws IOException { - Map postParams = makeRequestParams(); + var postParams = makeRequestParams(); postParams.put("to", to); postParams.put("text", text); @@ -114,7 +113,7 @@ public String sendTextMessageSimple(String to, String text) throws IOException { */ public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOException { - Map postParams = makeRequestParams(); + var postParams = makeRequestParams(); postParams.put("to", to); postParams.put("nonce", DataUtils.byteArrayToHexString(nonce)); postParams.put("box", DataUtils.byteArrayToHexString(box)); @@ -132,9 +131,9 @@ public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOExcep public String lookupPhone(String phoneNumber) throws IOException { try { - Map getParams = makeRequestParams(); + var getParams = makeRequestParams(); - byte[] phoneHash = CryptTool.hashPhoneNo(phoneNumber); + var phoneHash = CryptTool.hashPhoneNo(phoneNumber); return doGet( new URL(this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)), @@ -155,9 +154,9 @@ public String lookupPhone(String phoneNumber) throws IOException { public String lookupEmail(String email) throws IOException { try { - Map getParams = makeRequestParams(); + var getParams = makeRequestParams(); - byte[] emailHash = CryptTool.hashEmail(email); + var emailHash = CryptTool.hashEmail(email); return doGet( new URL(this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)), @@ -175,18 +174,18 @@ public String lookupEmail(String email) throws IOException { * @throws IOException if a communication or server error occurs */ public byte[] lookupKey(String id) throws IOException { - byte[] key = this.publicKeyStore.getPublicKey(id); + var key = this.publicKeyStore.getPublicKey(id); if (key == null) { try { - Map getParams = makeRequestParams(); - String pubkeyHex = doGet(new URL(this.apiUrl + "pubkeys/" + id), getParams); + var getParams = makeRequestParams(); + var pubkeyHex = doGet(new URL(this.apiUrl + "pubkeys/" + id), getParams); key = DataUtils.hexStringToByteArray(pubkeyHex); if (key != null) { this.publicKeyStore.save(id, key); } } catch (FileNotFoundException e) { - return null; + return new byte[0]; } } return key; @@ -200,7 +199,7 @@ public byte[] lookupKey(String id) throws IOException { * @throws IOException */ public CapabilityResult lookupKeyCapability(String threemaId) throws IOException { - String res = doGet(new URL(this.apiUrl + "capabilities/" + threemaId), makeRequestParams()); + var res = doGet(new URL(this.apiUrl + "capabilities/" + threemaId), makeRequestParams()); if (res != null) { return new CapabilityResult(threemaId, res.split(",")); } @@ -208,7 +207,7 @@ public CapabilityResult lookupKeyCapability(String threemaId) throws IOException } public Integer lookupCredits() throws IOException { - String res = doGet(new URL(this.apiUrl + "credits"), makeRequestParams()); + var res = doGet(new URL(this.apiUrl + "credits"), makeRequestParams()); if (res != null) { return Integer.valueOf(res); } @@ -223,23 +222,23 @@ public Integer lookupCredits() throws IOException { */ public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOException { - String attachmentName = "blob"; - String attachmentFileName = "blob.file"; - String crlf = "\r\n"; - String twoHyphens = "--"; - - char[] chars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); - String boundary = ""; - SecureRandom rand = new SecureRandom(); - int count = rand.nextInt(11) + 30; - for (int i = 0; i < count; i++) { - boundary += chars[rand.nextInt(chars.length)]; + var attachmentName = "blob"; + var attachmentFileName = "blob.file"; + var crlf = "\r\n"; + var twoHyphens = "--"; + + var chars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + var rand = new SecureRandom(); + var count = rand.nextInt(11) + 30; + var boundary = new StringBuilder(count); + for (var i = 0; i < count; i++) { + boundary.append(chars[rand.nextInt(chars.length)]); } - String queryString = makeUrlEncoded(makeRequestParams()); - URL url = new URL(this.apiUrl + "upload_blob?" + queryString); + var queryString = makeUrlEncoded(makeRequestParams()); + var url = new URL(this.apiUrl + "upload_blob?" + queryString); - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + var connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); @@ -249,7 +248,7 @@ public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOExce connection.setRequestProperty("Cache-Control", "no-cache"); connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); - DataOutputStream request = new DataOutputStream(connection.getOutputStream()); + var request = new DataOutputStream(connection.getOutputStream()); request.writeBytes(twoHyphens + boundary + crlf); request.writeBytes( @@ -265,11 +264,11 @@ public UploadResult uploadFile(EncryptResult fileEncryptionResult) throws IOExce request.writeBytes(twoHyphens + boundary + twoHyphens + crlf); String response = null; - int responseCode = connection.getResponseCode(); + var responseCode = connection.getResponseCode(); if (responseCode == 200) { - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); + var is = connection.getInputStream(); + var br = new BufferedReader(new InputStreamReader(is)); response = br.readLine(); br.close(); } @@ -300,26 +299,26 @@ public byte[] downloadFile(byte[] blobId) throws IOException { * @throws IOException */ public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) throws IOException { - String queryString = makeUrlEncoded(makeRequestParams()); - URL blobUrl = + var queryString = makeUrlEncoded(makeRequestParams()); + var blobUrl = new URL( String.format( - this.apiUrl + "blobs/%s?%s", DataUtils.byteArrayToHexString(blobId), queryString)); + "%sblobs/%s?%s", this.apiUrl, DataUtils.byteArrayToHexString(blobId), queryString)); - HttpsURLConnection connection = (HttpsURLConnection) blobUrl.openConnection(); + var connection = (HttpsURLConnection) blobUrl.openConnection(); connection.setConnectTimeout(20 * 1000); connection.setReadTimeout(20 * 1000); connection.setDoOutput(false); - InputStream inputStream = connection.getInputStream(); - int contentLength = connection.getContentLength(); - InputStreamLength isl = new InputStreamLength(inputStream, contentLength); + var inputStream = connection.getInputStream(); + var contentLength = connection.getContentLength(); + var isl = new InputStreamLength(inputStream, contentLength); /* Content length known? */ byte[] blob; if (isl.length != -1) { blob = new byte[isl.length]; - int offset = 0; + var offset = 0; int readed; while (offset < isl.length @@ -338,8 +337,8 @@ public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) thr } else { /* Content length is unknown - need to read until EOF */ - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buffer = new byte[BUFFER_SIZE]; + var bos = new ByteArrayOutputStream(); + var buffer = new byte[BUFFER_SIZE]; int read; while ((read = isl.inputStream.read(buffer)) != -1) { @@ -356,31 +355,27 @@ public byte[] downloadFile(byte[] blobId, ProgressListener progressListener) thr } private Map makeRequestParams() { - Map postParams = new HashMap<>(); - - postParams.put("from", apiIdentity); - postParams.put("secret", secret); - return postParams; + return Map.of("from", apiIdentity, "secret", secret); } private String doGet(URL url, Map getParams) throws IOException { if (getParams != null) { - String queryString = makeUrlEncoded(getParams); + var queryString = makeUrlEncoded(getParams); url = new URL(url.toString() + "?" + queryString); } - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + var connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(false); connection.setDoInput(true); connection.setInstanceFollowRedirects(false); connection.setRequestMethod("GET"); connection.setUseCaches(false); - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String response = br.readLine(); + var is = connection.getInputStream(); + var br = new BufferedReader(new InputStreamReader(is)); + var response = br.readLine(); br.close(); connection.disconnect(); @@ -390,9 +385,9 @@ private String doGet(URL url, Map getParams) throws IOException private String doPost(URL url, Map postParams) throws IOException { - byte[] postData = makeUrlEncoded(postParams).getBytes("UTF-8"); + var postData = makeUrlEncoded(postParams).getBytes(StandardCharsets.UTF_8); - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + var connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setInstanceFollowRedirects(false); @@ -402,14 +397,14 @@ private String doPost(URL url, Map postParams) throws IOExceptio connection.setRequestProperty("Content-Length", Integer.toString(postData.length)); connection.setUseCaches(false); - OutputStream os = connection.getOutputStream(); + var os = connection.getOutputStream(); os.write(postData); os.flush(); os.close(); - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String response = br.readLine(); + var is = connection.getInputStream(); + var br = new BufferedReader(new InputStreamReader(is)); + var response = br.readLine(); br.close(); connection.disconnect(); @@ -418,16 +413,19 @@ private String doPost(URL url, Map postParams) throws IOExceptio } private String makeUrlEncoded(Map params) { - StringBuilder s = new StringBuilder(); + var s = new StringBuilder(); for (Map.Entry param : params.entrySet()) { - if (s.length() > 0) s.append('&'); + if (s.length() > 0) { + s.append('&'); + } s.append(param.getKey()); s.append('='); try { s.append(URLEncoder.encode(param.getValue(), "UTF-8")); } catch (UnsupportedEncodingException ignored) { + // ignored } } diff --git a/source/src/main/java/ch/threema/apitool/CryptTool.java b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java similarity index 70% rename from source/src/main/java/ch/threema/apitool/CryptTool.java rename to api/src/main/java/net/klesatschke/threema/api/CryptTool.java index f800f3a..72b2da3 100644 --- a/source/src/main/java/ch/threema/apitool/CryptTool.java +++ b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java @@ -22,9 +22,10 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.LinkedList; import java.util.List; @@ -36,103 +37,102 @@ import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.exceptions.BadMessageException; -import ch.threema.apitool.exceptions.DecryptionFailedException; -import ch.threema.apitool.exceptions.MessageParseException; -import ch.threema.apitool.exceptions.UnsupportedMessageTypeException; -import ch.threema.apitool.messages.DeliveryReceipt; -import ch.threema.apitool.messages.FileMessage; -import ch.threema.apitool.messages.ImageMessage; -import ch.threema.apitool.messages.TextMessage; -import ch.threema.apitool.messages.ThreemaMessage; -import ch.threema.apitool.results.EncryptResult; -import ch.threema.apitool.results.UploadResult; +import net.klesatschke.threema.api.exceptions.BadMessageException; +import net.klesatschke.threema.api.exceptions.DecryptionFailedException; +import net.klesatschke.threema.api.exceptions.MessageParseException; +import net.klesatschke.threema.api.exceptions.UnsupportedMessageTypeException; +import net.klesatschke.threema.api.messages.DeliveryReceipt; +import net.klesatschke.threema.api.messages.FileMessage; +import net.klesatschke.threema.api.messages.ImageMessage; +import net.klesatschke.threema.api.messages.TextMessage; +import net.klesatschke.threema.api.messages.ThreemaMessage; +import net.klesatschke.threema.api.results.EncryptResult; +import net.klesatschke.threema.api.results.UploadResult; /** Contains static methods to do various Threema cryptography related tasks. */ public class CryptTool { + private static final String HMAC_SHA256 = "HmacSHA256"; + +private CryptTool() {} /* HMAC-SHA256 keys for email/mobile phone hashing */ - private static final byte[] EMAIL_HMAC_KEY = - new byte[] { - (byte) 0x30, - (byte) 0xa5, - (byte) 0x50, - (byte) 0x0f, - (byte) 0xed, - (byte) 0x97, - (byte) 0x01, - (byte) 0xfa, - (byte) 0x6d, - (byte) 0xef, - (byte) 0xdb, - (byte) 0x61, - (byte) 0x08, - (byte) 0x41, - (byte) 0x90, - (byte) 0x0f, - (byte) 0xeb, - (byte) 0xb8, - (byte) 0xe4, - (byte) 0x30, - (byte) 0x88, - (byte) 0x1f, - (byte) 0x7a, - (byte) 0xd8, - (byte) 0x16, - (byte) 0x82, - (byte) 0x62, - (byte) 0x64, - (byte) 0xec, - (byte) 0x09, - (byte) 0xba, - (byte) 0xd7 - }; - private static final byte[] PHONENO_HMAC_KEY = - new byte[] { - (byte) 0x85, - (byte) 0xad, - (byte) 0xf8, - (byte) 0x22, - (byte) 0x69, - (byte) 0x53, - (byte) 0xf3, - (byte) 0xd9, - (byte) 0x6c, - (byte) 0xfd, - (byte) 0x5d, - (byte) 0x09, - (byte) 0xbf, - (byte) 0x29, - (byte) 0x55, - (byte) 0x5e, - (byte) 0xb9, - (byte) 0x55, - (byte) 0xfc, - (byte) 0xd8, - (byte) 0xaa, - (byte) 0x5e, - (byte) 0xc4, - (byte) 0xf9, - (byte) 0xfc, - (byte) 0xd8, - (byte) 0x69, - (byte) 0xe2, - (byte) 0x58, - (byte) 0x37, - (byte) 0x07, - (byte) 0x23 - }; - - private static final byte[] FILE_NONCE = - new byte[] { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - private static final byte[] FILE_THUMBNAIL_NONCE = - new byte[] { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 - }; + private static final byte[] EMAIL_HMAC_KEY = { + (byte) 0x30, + (byte) 0xa5, + (byte) 0x50, + (byte) 0x0f, + (byte) 0xed, + (byte) 0x97, + (byte) 0x01, + (byte) 0xfa, + (byte) 0x6d, + (byte) 0xef, + (byte) 0xdb, + (byte) 0x61, + (byte) 0x08, + (byte) 0x41, + (byte) 0x90, + (byte) 0x0f, + (byte) 0xeb, + (byte) 0xb8, + (byte) 0xe4, + (byte) 0x30, + (byte) 0x88, + (byte) 0x1f, + (byte) 0x7a, + (byte) 0xd8, + (byte) 0x16, + (byte) 0x82, + (byte) 0x62, + (byte) 0x64, + (byte) 0xec, + (byte) 0x09, + (byte) 0xba, + (byte) 0xd7 + }; + private static final byte[] PHONENO_HMAC_KEY = { + (byte) 0x85, + (byte) 0xad, + (byte) 0xf8, + (byte) 0x22, + (byte) 0x69, + (byte) 0x53, + (byte) 0xf3, + (byte) 0xd9, + (byte) 0x6c, + (byte) 0xfd, + (byte) 0x5d, + (byte) 0x09, + (byte) 0xbf, + (byte) 0x29, + (byte) 0x55, + (byte) 0x5e, + (byte) 0xb9, + (byte) 0x55, + (byte) 0xfc, + (byte) 0xd8, + (byte) 0xaa, + (byte) 0x5e, + (byte) 0xc4, + (byte) 0xf9, + (byte) 0xfc, + (byte) 0xd8, + (byte) 0x69, + (byte) 0xe2, + (byte) 0x58, + (byte) 0x37, + (byte) 0x07, + (byte) 0x23 + }; + + private static final byte[] FILE_NONCE = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + private static final byte[] FILE_THUMBNAIL_NONCE = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; private static final SecureRandom random = new SecureRandom(); @@ -207,7 +207,7 @@ public static EncryptResult encryptFileMessage( private static EncryptResult encryptMessage( ThreemaMessage threemaMessage, byte[] privateKey, byte[] publicKey) { /* determine random amount of PKCS7 padding */ - int padbytes = random.nextInt(254) + 1; + var padbytes = random.nextInt(254) + 1; byte[] messageBytes; try { @@ -217,13 +217,13 @@ private static EncryptResult encryptMessage( } /* prepend type byte (0x02) to message data */ - byte[] data = new byte[1 + messageBytes.length + padbytes]; + var data = new byte[1 + messageBytes.length + padbytes]; data[0] = (byte) threemaMessage.getTypeCode(); System.arraycopy(messageBytes, 0, data, 1, messageBytes.length); /* append padding */ - for (int i = 0; i < padbytes; i++) { + for (var i = 0; i < padbytes; i++) { data[i + 1 + messageBytes.length] = (byte) padbytes; } @@ -278,57 +278,61 @@ public static ThreemaMessage decryptMessage( byte[] box, byte[] recipientPrivateKey, byte[] senderPublicKey, byte[] nonce) throws MessageParseException { - byte[] data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce); - if (data == null) throw new DecryptionFailedException(); + var data = decrypt(box, recipientPrivateKey, senderPublicKey, nonce); + if (data == null) { + throw new DecryptionFailedException(); + } /* remove padding */ - int padbytes = data[data.length - 1] & 0xFF; - int realDataLength = data.length - padbytes; - if (realDataLength < 1) throw new BadMessageException(); /* Bad message padding */ + var padbytes = data[data.length - 1] & 0xFF; + var realDataLength = data.length - padbytes; + if (realDataLength < 1) { + throw new BadMessageException(); /* Bad message padding */ + } /* first byte of data is type */ - int type = data[0] & 0xFF; + var type = data[0] & 0xFF; switch (type) { case TextMessage.TYPE_CODE: /* Text message */ - if (realDataLength < 2) throw new BadMessageException(); - - try { - return new TextMessage(new String(data, 1, realDataLength - 1, "UTF-8")); - } catch (UnsupportedEncodingException e) { - /* should never happen, UTF-8 is always supported */ - throw new RuntimeException(e); + if (realDataLength < 2) { + throw new BadMessageException(); } + return new TextMessage(new String(data, 1, realDataLength - 1, StandardCharsets.UTF_8)); + case DeliveryReceipt.TYPE_CODE: /* Delivery receipt */ if (realDataLength < MessageId.MESSAGE_ID_LEN + 2 - || ((realDataLength - 2) % MessageId.MESSAGE_ID_LEN) != 0) + || (realDataLength - 2) % MessageId.MESSAGE_ID_LEN != 0) { throw new BadMessageException(); + } - DeliveryReceipt.Type receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF); - if (receiptType == null) throw new BadMessageException(); + var receiptType = DeliveryReceipt.Type.get(data[1] & 0xFF); + if (receiptType == null) { + throw new BadMessageException(); + } List messageIds = new LinkedList<>(); - int numMsgIds = ((realDataLength - 2) / MessageId.MESSAGE_ID_LEN); - for (int i = 0; i < numMsgIds; i++) { + var numMsgIds = (realDataLength - 2) / MessageId.MESSAGE_ID_LEN; + for (var i = 0; i < numMsgIds; i++) { messageIds.add(new MessageId(data, 2 + i * MessageId.MESSAGE_ID_LEN)); } return new DeliveryReceipt(receiptType, messageIds); case ImageMessage.TYPE_CODE: - if (realDataLength != (1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)) { + if (realDataLength != 1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES) { System.out.println(String.valueOf(realDataLength)); System.out.println(String.valueOf(1 + ThreemaMessage.BLOB_ID_LEN + 4 + NaCl.NONCEBYTES)); throw new BadMessageException(); } - byte[] blobId = new byte[ThreemaMessage.BLOB_ID_LEN]; + var blobId = new byte[ThreemaMessage.BLOB_ID_LEN]; System.arraycopy(data, 1, blobId, 0, ThreemaMessage.BLOB_ID_LEN); - int size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN); - byte[] fileNonce = new byte[NaCl.NONCEBYTES]; + var size = EndianUtils.readSwappedInteger(data, 1 + ThreemaMessage.BLOB_ID_LEN); + var fileNonce = new byte[NaCl.NONCEBYTES]; System.arraycopy(data, 1 + 4 + ThreemaMessage.BLOB_ID_LEN, fileNonce, 0, nonce.length); return new ImageMessage(blobId, size, fileNonce); @@ -375,8 +379,8 @@ public static EncryptResult encrypt(byte[] data, byte[] privateKey, byte[] publi throw new IllegalArgumentException("Wrong key length"); } - byte[] nonce = randomNonce(); - NaCl naCl = new NaCl(privateKey, publicKey); + var nonce = randomNonce(); + var naCl = new NaCl(privateKey, publicKey); return new EncryptResult(naCl.encrypt(data, nonce), null, nonce); } @@ -388,8 +392,8 @@ public static EncryptResult encrypt(byte[] data, byte[] privateKey, byte[] publi */ public static EncryptResult encryptFileData(byte[] data) { // create random key - SecureRandom rnd = new SecureRandom(); - byte[] encryptionKey = new byte[NaCl.SYMMKEYBYTES]; + var rnd = new SecureRandom(); + var encryptionKey = new byte[NaCl.SYMMKEYBYTES]; rnd.nextBytes(encryptionKey); // encrypt file data in-place @@ -419,9 +423,9 @@ public static EncryptResult encryptFileThumbnailData(byte[] data, byte[] encrypt */ public static byte[] hashEmail(String email) { try { - Mac emailMac = Mac.getInstance("HmacSHA256"); - emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, "HmacSHA256")); - String normalizedEmail = email.toLowerCase().trim(); + var emailMac = Mac.getInstance(HMAC_SHA256); + emailMac.init(new SecretKeySpec(EMAIL_HMAC_KEY, HMAC_SHA256)); + var normalizedEmail = email.toLowerCase().trim(); return emailMac.doFinal(normalizedEmail.getBytes("US-ASCII")); } catch (Exception e) { e.printStackTrace(); @@ -437,9 +441,9 @@ public static byte[] hashEmail(String email) { */ public static byte[] hashPhoneNo(String phoneNo) { try { - Mac phoneMac = Mac.getInstance("HmacSHA256"); - phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, "HmacSHA256")); - String normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", ""); + var phoneMac = Mac.getInstance(HMAC_SHA256); + phoneMac.init(new SecretKeySpec(PHONENO_HMAC_KEY, HMAC_SHA256)); + var normalizedPhoneNo = phoneNo.replaceAll("[^0-9]", ""); return phoneMac.doFinal(normalizedPhoneNo.getBytes("US-ASCII")); } catch (Exception e) { e.printStackTrace(); @@ -453,7 +457,7 @@ public static byte[] hashPhoneNo(String phoneNo) { * @return random nonce */ public static byte[] randomNonce() { - byte[] nonce = new byte[NaCl.NONCEBYTES]; + var nonce = new byte[NaCl.NONCEBYTES]; random.nextBytes(nonce); return nonce; } diff --git a/source/src/main/java/ch/threema/apitool/DataUtils.java b/api/src/main/java/net/klesatschke/threema/api/DataUtils.java similarity index 75% rename from source/src/main/java/ch/threema/apitool/DataUtils.java rename to api/src/main/java/net/klesatschke/threema/api/DataUtils.java index 994722f..be992b8 100644 --- a/source/src/main/java/ch/threema/apitool/DataUtils.java +++ b/api/src/main/java/net/klesatschke/threema/api/DataUtils.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; import java.io.BufferedReader; import java.io.File; @@ -30,9 +30,11 @@ import java.io.FileWriter; import java.io.IOException; -import ch.threema.apitool.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; public class DataUtils { + private DataUtils() {} /** * Convert a string in hexadecimal representation to a byte array. @@ -41,10 +43,10 @@ public class DataUtils { * @return decoded byte array */ public static byte[] hexStringToByteArray(String s) { - String sc = s.replaceAll("[^0-9a-fA-F]", ""); - int len = sc.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { + var sc = s.replaceAll("[^0-9a-fA-F]", ""); + var len = sc.length(); + var data = new byte[len / 2]; + for (var i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(sc.charAt(i), 16) << 4) + Character.digit(sc.charAt(i + 1), 16)); } @@ -61,9 +63,9 @@ public static String byteArrayToHexString(byte[] bytes) { final char[] hexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - char[] hexChars = new char[bytes.length * 2]; + var hexChars = new char[bytes.length * 2]; int v; - for (int j = 0; j < bytes.length; j++) { + for (var j = 0; j < bytes.length; j++) { v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; @@ -79,10 +81,9 @@ public static String byteArrayToHexString(byte[] bytes) { * @throws java.io.IOException */ public static byte[] readHexFile(File inFile) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - byte[] data = hexStringToByteArray(br.readLine().trim()); - br.close(); - return data; + try (var br = new BufferedReader(new FileReader(inFile))) { + return hexStringToByteArray(br.readLine().trim()); + } } /** @@ -92,10 +93,10 @@ public static byte[] readHexFile(File inFile) throws IOException { * @param data the data to be written */ public static void writeHexFile(File outFile, byte[] data) throws IOException { - FileWriter fw = new FileWriter(outFile); - fw.write(byteArrayToHexString(data)); - fw.write('\n'); - fw.close(); + try (var fw = new FileWriter(outFile)) { + fw.write(byteArrayToHexString(data)); + fw.write('\n'); + } } /** @@ -106,10 +107,9 @@ public static void writeHexFile(File outFile, byte[] data) throws IOException { * @throws java.io.IOException */ public static Key readKeyFile(File inFile) throws IOException, InvalidKeyException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - String encodedKey = br.readLine().trim(); - br.close(); - return Key.decodeKey(encodedKey); + try (var br = new BufferedReader(new FileReader(inFile))) { + return Key.decodeKey(br.readLine().trim()); + } } /** @@ -120,12 +120,11 @@ 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) + public static Key readKeyFile(File inFile, KeyType expectedKeyType) throws IOException, InvalidKeyException { - BufferedReader br = new BufferedReader(new FileReader(inFile)); - String encodedKey = br.readLine().trim(); - br.close(); - return Key.decodeKey(encodedKey, expectedKeyType); + try (var br = new BufferedReader(new FileReader(inFile))) { + return Key.decodeKey(br.readLine().trim(), expectedKeyType); + } } /** @@ -135,9 +134,9 @@ public static Key readKeyFile(File inFile, String expectedKeyType) * @param key a key that will be encoded and written to a file */ public static void writeKeyFile(File outFile, Key key) throws IOException { - FileWriter fw = new FileWriter(outFile); - fw.write(key.encode()); - fw.write('\n'); - fw.close(); + try (var fw = new FileWriter(outFile)) { + fw.write(key.encode()); + fw.write('\n'); + } } } diff --git a/source/src/main/java/ch/threema/apitool/Key.java b/api/src/main/java/net/klesatschke/threema/api/Key.java similarity index 77% rename from source/src/main/java/ch/threema/apitool/Key.java rename to api/src/main/java/net/klesatschke/threema/api/Key.java index 1012263..faeda2c 100644 --- a/source/src/main/java/ch/threema/apitool/Key.java +++ b/api/src/main/java/net/klesatschke/threema/api/Key.java @@ -3,6 +3,7 @@ * * The MIT License (MIT) * Copyright (c) 2015 Threema GmbH + * Copyright (c) 2022 Kai Klesatschke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,27 +23,23 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; -import ch.threema.apitool.exceptions.InvalidKeyException; +import lombok.Value; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; /** Encapsulates an asymmetric key, either public or private. */ +@Value public class Key { - public static final String separator = ":"; + public static final String SEPARATOR = ":"; - public static class KeyType { - public static final String PRIVATE = "private"; - public static final String PUBLIC = "public"; + public enum KeyType { + PRIVATE, + PUBLIC; } - /* Attributes */ - public byte[] key; - public String type; - - public Key(String type, byte[] key) { - this.key = key; - this.type = type; - } + private final KeyType type; + private final byte[] key; /** * Decodes and validates an encoded key. Encoded key format: type:hex_key @@ -52,14 +49,14 @@ public Key(String type, byte[] key) { */ public static Key decodeKey(String encodedKey) throws InvalidKeyException { // Split key and check length - String[] keyArray = encodedKey.split(Key.separator); + var keyArray = encodedKey.split(Key.SEPARATOR); if (keyArray.length != 2) { throw new InvalidKeyException("Does not contain a valid key format"); } // Unpack key - String keyType = keyArray[0]; - String keyContent = keyArray[1]; + var keyType = KeyType.valueOf(keyArray[0].toUpperCase()); + var keyContent = keyArray[1]; // Is this a valid hex key? if (!keyContent.matches("[0-9a-fA-F]{64}")) { @@ -76,9 +73,9 @@ public static Key decodeKey(String encodedKey) throws InvalidKeyException { * @param expectedKeyType the expected type of the key * @throws InvalidKeyException */ - public static Key decodeKey(String encodedKey, String expectedKeyType) + public static Key decodeKey(String encodedKey, KeyType expectedKeyType) throws InvalidKeyException { - Key key = decodeKey(encodedKey); + var key = decodeKey(encodedKey); // Check key type if (!key.type.equals(expectedKeyType)) { @@ -94,6 +91,8 @@ public static Key decodeKey(String encodedKey, String expectedKeyType) * @return an encoded key */ public String encode() { - return this.type + Key.separator + DataUtils.byteArrayToHexString(this.key); + return this.type.toString().toLowerCase() + + Key.SEPARATOR + + DataUtils.byteArrayToHexString(this.key); } } diff --git a/source/src/main/java/ch/threema/apitool/MessageId.java b/api/src/main/java/net/klesatschke/threema/api/MessageId.java similarity index 92% rename from source/src/main/java/ch/threema/apitool/MessageId.java rename to api/src/main/java/net/klesatschke/threema/api/MessageId.java index e5f96fa..6cd3087 100644 --- a/source/src/main/java/ch/threema/apitool/MessageId.java +++ b/api/src/main/java/net/klesatschke/threema/api/MessageId.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; /** Encapsulates the 8-byte message IDs that Threema uses. */ public class MessageId { @@ -32,15 +32,17 @@ public class MessageId { private final byte[] messageId; public MessageId(byte[] messageId) { - if (messageId.length != MESSAGE_ID_LEN) + if (messageId.length != MESSAGE_ID_LEN) { throw new IllegalArgumentException("Bad message ID length"); + } this.messageId = messageId; } public MessageId(byte[] data, int offset) { - if ((offset + MESSAGE_ID_LEN) > data.length) + if (offset + MESSAGE_ID_LEN > data.length) { throw new IllegalArgumentException("Bad message ID buffer length"); + } this.messageId = new byte[MESSAGE_ID_LEN]; System.arraycopy(data, offset, this.messageId, 0, MESSAGE_ID_LEN); diff --git a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java b/api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java similarity index 82% rename from source/src/main/java/ch/threema/apitool/PublicKeyStore.java rename to api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java index 815b46f..f155288 100644 --- a/source/src/main/java/ch/threema/apitool/PublicKeyStore.java +++ b/api/src/main/java/net/klesatschke/threema/api/PublicKeyStore.java @@ -3,6 +3,7 @@ * * The MIT License (MIT) * Copyright (c) 2015 Threema GmbH + * Copyright (c) 2022 Kai Klesatschke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,17 +23,18 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; -import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; /** * Stores and caches public keys for Threema users. Extend this class to provide your own storage * implementation, e.g. in a file or database. */ public abstract class PublicKeyStore { - private final Map cache = new HashMap<>(); + private final Map cache = new ConcurrentHashMap<>(); /** * Get the public key for a given Threema ID. The cache is checked first; if it is not found in @@ -42,15 +44,7 @@ public abstract class PublicKeyStore { * @return The public key, or null if not found. */ public final byte[] getPublicKey(String threemaId) { - synchronized (this.cache) { - byte[] pk = this.cache.get(threemaId); - - if (pk == null) { - pk = this.fetchPublicKey(threemaId); - this.cache.put(threemaId, pk); - } - return pk; - } + return this.cache.computeIfAbsent(threemaId, this::fetchPublicKey); } /** @@ -60,12 +54,15 @@ public final byte[] getPublicKey(String threemaId) { * @param publicKey The corresponding public key. */ public final void setPublicKey(String threemaId, byte[] publicKey) { - if (publicKey != null) { - synchronized (this.cache) { - this.cache.put(threemaId, publicKey); - this.save(threemaId, publicKey); - } - } + Optional.ofNullable(publicKey) + .ifPresent( + key -> + cache.compute( + threemaId, + (id, old) -> { + save(id, key); + return key; + })); } /** diff --git a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java index 97a6d1f..265d952 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/BadMessageException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/BadMessageException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** Exception that gets thrown if a message has a bad/illegal format after it has been decrypted. */ public class BadMessageException extends MessageParseException { diff --git a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java index d45d351..83aacdc 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/DecryptionFailedException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/DecryptionFailedException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** * Exception that gets thrown when decryption fails (because the keys are incorrect, or the data is diff --git a/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java index d1cb48f..ad7fe86 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/InvalidCommandFieldValueException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** Exception that gets thrown on a illegal call. */ public class InvalidCommandFieldValueException extends Exception { diff --git a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java index 2e7e909..d23f1c4 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/InvalidKeyException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidKeyException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** Exception that gets thrown when an invalid key has been specified (e.g. wrong length). */ public class InvalidKeyException extends Exception { diff --git a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java index 5709e71..35a9570 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/MessageParseException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/MessageParseException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** * Base class for exceptions that may occur when parsing/decrypting an encrypted Threema message. diff --git a/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java index bbfe457..f5ac688 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/NotAllowedException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** Exception that gets thrown on a illegal call. */ public class NotAllowedException extends Exception { diff --git a/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java index cd54309..2dcf97d 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/RequiredCommandFieldMissingException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** Exception that gets thrown on a illegal call. */ public class RequiredCommandFieldMissingException extends Exception { diff --git a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java rename to api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java index fc6f6fe..d6804b8 100644 --- a/source/src/main/java/ch/threema/apitool/exceptions/UnsupportedMessageTypeException.java +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/UnsupportedMessageTypeException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.exceptions; +package net.klesatschke.threema.api.exceptions; /** * Exception that gets thrown when an attempt has been made to decrypt a message of a type that is diff --git a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java b/api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java similarity index 91% rename from source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java rename to api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java index 209bdc6..a792b8b 100644 --- a/source/src/main/java/ch/threema/apitool/messages/DeliveryReceipt.java +++ b/api/src/main/java/net/klesatschke/threema/api/messages/DeliveryReceipt.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.messages; +package net.klesatschke.threema.api.messages; import java.util.List; -import ch.threema.apitool.MessageId; +import net.klesatschke.threema.api.MessageId; /** * A delivery receipt message that can be sent/received with end-to-end encryption via Threema. Each @@ -59,12 +59,14 @@ public int getTypeCode() { @Override public String toString() { - StringBuilder sb = new StringBuilder("Delivery receipt ("); + var sb = new StringBuilder("Delivery receipt ("); sb.append(receiptType); sb.append("): "); - int i = 0; + var i = 0; for (MessageId messageId : ackedMessageIds) { - if (i != 0) sb.append(", "); + if (i != 0) { + sb.append(", "); + } sb.append(messageId); i++; } @@ -99,7 +101,9 @@ public int getCode() { public static Type get(int code) { for (Type t : values()) { - if (t.code == code) return t; + if (t.code == code) { + return t; + } } return null; } diff --git a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java similarity index 92% rename from source/src/main/java/ch/threema/apitool/messages/FileMessage.java rename to api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java index 7d7ab92..2d7a736 100644 --- a/source/src/main/java/ch/threema/apitool/messages/FileMessage.java +++ b/api/src/main/java/net/klesatschke/threema/api/messages/FileMessage.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.messages; +package net.klesatschke.threema.api.messages; import java.io.UnsupportedEncodingException; @@ -30,8 +30,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; -import ch.threema.apitool.DataUtils; -import ch.threema.apitool.exceptions.BadMessageException; +import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.api.exceptions.BadMessageException; /** A file message that can be sent/received with end-to-end encryption via Threema. */ public class FileMessage extends ThreemaMessage { @@ -103,7 +103,7 @@ public String toString() { @Override public byte[] getData() throws BadMessageException { - JsonObject o = new JsonObject(); + var o = new JsonObject(); try { o.addProperty(KEY_BLOB_ID, DataUtils.byteArrayToHexString(this.blobId)); o.addProperty( @@ -129,11 +129,11 @@ public byte[] getData() throws BadMessageException { public static FileMessage fromString(String json) throws BadMessageException { try { - JsonObject o = new Gson().fromJson(json, JsonObject.class); + var o = new Gson().fromJson(json, JsonObject.class); byte[] encryptionKey = DataUtils.hexStringToByteArray(o.get(KEY_ENCRYPTION_KEY).getAsString()); - String mimeType = o.get(KEY_MIME_TYPE).getAsString(); - int fileSize = o.get(KEY_FILE_SIZE).getAsInt(); + var mimeType = o.get(KEY_MIME_TYPE).getAsString(); + var fileSize = o.get(KEY_FILE_SIZE).getAsInt(); byte[] blobId = DataUtils.hexStringToByteArray(o.get(KEY_BLOB_ID).getAsString()); String fileName; diff --git a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java similarity index 93% rename from source/src/main/java/ch/threema/apitool/messages/ImageMessage.java rename to api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java index 0612d49..958fba6 100644 --- a/source/src/main/java/ch/threema/apitool/messages/ImageMessage.java +++ b/api/src/main/java/net/klesatschke/threema/api/messages/ImageMessage.java @@ -22,13 +22,13 @@ * THE SOFTWARE */ -package ch.threema.apitool.messages; +package net.klesatschke.threema.api.messages; import org.apache.commons.io.EndianUtils; import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.DataUtils; +import net.klesatschke.threema.api.DataUtils; /** An image message that can be sent/received with end-to-end encryption via Threema. */ public class ImageMessage extends ThreemaMessage { @@ -69,8 +69,8 @@ public String toString() { @Override public byte[] getData() { - byte[] data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES]; - int pos = 0; + var data = new byte[BLOB_ID_LEN + 4 + NaCl.NONCEBYTES]; + var pos = 0; System.arraycopy(this.blobId, 0, data, pos, BLOB_ID_LEN); pos += BLOB_ID_LEN; diff --git a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java similarity index 97% rename from source/src/main/java/ch/threema/apitool/messages/TextMessage.java rename to api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java index d1bf72f..7a3e8fc 100644 --- a/source/src/main/java/ch/threema/apitool/messages/TextMessage.java +++ b/api/src/main/java/net/klesatschke/threema/api/messages/TextMessage.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.messages; +package net.klesatschke.threema.api.messages; import java.io.UnsupportedEncodingException; diff --git a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java b/api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java similarity index 93% rename from source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java rename to api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java index 85bd53c..e4131f9 100644 --- a/source/src/main/java/ch/threema/apitool/messages/ThreemaMessage.java +++ b/api/src/main/java/net/klesatschke/threema/api/messages/ThreemaMessage.java @@ -22,9 +22,9 @@ * THE SOFTWARE */ -package ch.threema.apitool.messages; +package net.klesatschke.threema.api.messages; -import ch.threema.apitool.exceptions.BadMessageException; +import net.klesatschke.threema.api.exceptions.BadMessageException; /** Abstract base class of messages that can be sent with end-to-end encryption via Threema. */ public abstract class ThreemaMessage { diff --git a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java b/api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java similarity index 88% rename from source/src/main/java/ch/threema/apitool/results/CapabilityResult.java rename to api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java index 04bcdc3..21cd88e 100644 --- a/source/src/main/java/ch/threema/apitool/results/CapabilityResult.java +++ b/api/src/main/java/net/klesatschke/threema/api/results/CapabilityResult.java @@ -22,17 +22,17 @@ * THE SOFTWARE */ -package ch.threema.apitool.results; +package net.klesatschke.threema.api.results; + +import lombok.Value; /** Result of a capability lookup */ +@Value public class CapabilityResult { private final String key; - private final String[] capabilities; - public CapabilityResult(String key, String[] capabilities) { - this.key = key; - this.capabilities = capabilities; - } + /** capabilities as a string array. */ + private final String[] capabilities; /** Get all capabilities as a string array. */ public String[] getCapabilities() { @@ -75,9 +75,9 @@ private boolean can(String key) { @Override public String toString() { - StringBuilder b = new StringBuilder(); + var b = new StringBuilder(); b.append(this.key).append(": "); - for (int n = 0; n < this.capabilities.length; n++) { + for (var n = 0; n < this.capabilities.length; n++) { if (n > 0) { b.append(","); } @@ -85,8 +85,4 @@ public String toString() { } return b.toString(); } - - public String getKey() { - return key; - } } diff --git a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java b/api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java similarity index 72% rename from source/src/main/java/ch/threema/apitool/results/EncryptResult.java rename to api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java index f20685c..25aa3a4 100644 --- a/source/src/main/java/ch/threema/apitool/results/EncryptResult.java +++ b/api/src/main/java/net/klesatschke/threema/api/results/EncryptResult.java @@ -22,37 +22,22 @@ * THE SOFTWARE */ -package ch.threema.apitool.results; +package net.klesatschke.threema.api.results; + +import lombok.Value; /** Result of a data encryption */ +@Value public class EncryptResult { + /** the encrypted data */ private final byte[] result; + /** the secret that was used for encryption (only for symmetric encryption, e.g. files) */ private final byte[] secret; + /** the nonce that was used for encryption */ private final byte[] nonce; - public EncryptResult(byte[] result, byte[] secret, byte[] nonce) { - this.result = result; - this.secret = secret; - this.nonce = nonce; - } - - /** @return the encrypted data */ - public byte[] getResult() { - return this.result; - } - /** @return the size (in bytes) of the encrypted data */ public int getSize() { return this.result.length; } - - /** @return the nonce that was used for encryption */ - public byte[] getNonce() { - return this.nonce; - } - - /** @return the secret that was used for encryption (only for symmetric encryption, e.g. files) */ - public byte[] getSecret() { - return secret; - } } diff --git a/source/src/main/java/ch/threema/apitool/results/UploadResult.java b/api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java similarity index 78% rename from source/src/main/java/ch/threema/apitool/results/UploadResult.java rename to api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java index fae9d2d..72595c5 100644 --- a/source/src/main/java/ch/threema/apitool/results/UploadResult.java +++ b/api/src/main/java/net/klesatschke/threema/api/results/UploadResult.java @@ -22,30 +22,18 @@ * THE SOFTWARE */ -package ch.threema.apitool.results; +package net.klesatschke.threema.api.results; + +import lombok.Value; /** Result of a file upload */ +@Value public class UploadResult { private final int responseCode; private final byte[] blobId; - public UploadResult(int responseCode, byte[] blobId) { - this.responseCode = responseCode; - this.blobId = blobId; - } - - /** @return the blob ID that has been created */ - public byte[] getBlobId() { - return this.blobId; - } - /** @return whether the upload succeeded */ public boolean isSuccess() { return this.responseCode == 200; } - - /** @return the response code of the upload */ - public int getResponseCode() { - return this.responseCode; - } } diff --git a/source/src/test/java/ch/threema/apitool/Common.java b/api/src/test/java/net/klesatschke/threema/api/Common.java similarity index 98% rename from source/src/test/java/ch/threema/apitool/Common.java rename to api/src/test/java/net/klesatschke/threema/api/Common.java index 90f6f38..bfe4e36 100644 --- a/source/src/test/java/ch/threema/apitool/Common.java +++ b/api/src/test/java/net/klesatschke/threema/api/Common.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; /** Common Stuff */ public abstract class Common { diff --git a/source/src/test/java/ch/threema/apitool/CryptToolTest.java b/api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java similarity index 91% rename from source/src/test/java/ch/threema/apitool/CryptToolTest.java rename to api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java index f724b3f..14cf182 100644 --- a/source/src/test/java/ch/threema/apitool/CryptToolTest.java +++ b/api/src/test/java/net/klesatschke/threema/api/CryptToolTest.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; import static org.assertj.core.api.Assertions.assertThat; @@ -30,7 +30,7 @@ import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.messages.TextMessage; +import net.klesatschke.threema.api.messages.TextMessage; class CryptToolTest { @@ -65,8 +65,8 @@ void testDecrypt() throws Exception { var message = CryptTool.decryptMessage( DataUtils.hexStringToByteArray(box), - privateKey.key, - publicKey.key, + privateKey.getKey(), + publicKey.getKey(), DataUtils.hexStringToByteArray(nonce)); assertThat(message) @@ -82,7 +82,7 @@ void testEncrypt() throws Exception { var privateKey = Key.decodeKey(Common.myPrivateKey); var publicKey = Key.decodeKey(Common.otherPublicKey); - var res = CryptTool.encryptTextMessage(text, privateKey.key, publicKey.key); + var res = CryptTool.encryptTextMessage(text, privateKey.getKey(), publicKey.getKey()); assertThat(res).isNotNull(); assertThat(res.getNonce()).isNotNull().satisfies(Common::hasContent); @@ -93,7 +93,7 @@ void testEncrypt() throws Exception { void testDerivePublicKey() throws Exception { var privateKey = Key.decodeKey(Common.myPrivateKey); var publicKey = Key.decodeKey(Common.myPublicKey); - var derivedPublicKey = CryptTool.derivePublicKey(privateKey.key); - assertThat(derivedPublicKey).isEqualTo(publicKey.key); + var derivedPublicKey = CryptTool.derivePublicKey(privateKey.getKey()); + assertThat(derivedPublicKey).isEqualTo(publicKey.getKey()); } } diff --git a/source/src/test/java/ch/threema/apitool/KeyTest.java b/api/src/test/java/net/klesatschke/threema/api/KeyTest.java similarity index 86% rename from source/src/test/java/ch/threema/apitool/KeyTest.java rename to api/src/test/java/net/klesatschke/threema/api/KeyTest.java index 7bb5d5f..8510d78 100644 --- a/source/src/test/java/ch/threema/apitool/KeyTest.java +++ b/api/src/test/java/net/klesatschke/threema/api/KeyTest.java @@ -20,14 +20,14 @@ * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.api; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import org.junit.jupiter.api.Test; -import ch.threema.apitool.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; class KeyTest { @@ -43,8 +43,8 @@ void testDecodeKeyPrivate() throws Exception { Key.decodeKey("private:1234567890123456789012345678901234567890123456789012345678901234"); assertThat(key).isNotNull(); - assertThat(key.type).isEqualTo(Key.KeyType.PRIVATE); - assertThat(key.key) + assertThat(key.getType()).isEqualTo(Key.KeyType.PRIVATE); + assertThat(key.getKey()) .isEqualTo( DataUtils.hexStringToByteArray( "1234567890123456789012345678901234567890123456789012345678901234")); @@ -55,8 +55,8 @@ void testDecodeKeyPublic() throws Exception { var key = Key.decodeKey("public:1234567890123456789012345678901234567890123456789012345678901234"); assertThat(key).isNotNull(); - assertThat(key.type).isEqualTo(Key.KeyType.PUBLIC); - assertThat(key.key) + assertThat(key.getType()).isEqualTo(Key.KeyType.PUBLIC); + assertThat(key.getKey()) .isEqualTo( DataUtils.hexStringToByteArray( "1234567890123456789012345678901234567890123456789012345678901234")); @@ -66,8 +66,8 @@ void testDecodeKeyPublic() throws Exception { void testEncodePrivate() throws Exception { var keyAsByte = DataUtils.hexStringToByteArray(Common.myPrivateKeyExtract); var key = new Key(Key.KeyType.PRIVATE, keyAsByte); - assertThat(key.type).isEqualTo(Key.KeyType.PRIVATE); - assertThat(key.key).isEqualTo(keyAsByte); + assertThat(key.getType()).isEqualTo(Key.KeyType.PRIVATE); + assertThat(key.getKey()).isEqualTo(keyAsByte); assertThat(key.encode()).isEqualTo(Common.myPrivateKey); } } diff --git a/cli/pom.xml b/cli/pom.xml new file mode 100644 index 0000000..fae8b26 --- /dev/null +++ b/cli/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + net.klesatschke.threema + msgapi-sdk-java + 1.1.5-SNAPSHOT + + cli + Threema Messsge Gateway CLI + + UTF-8 + ${source.encoding} + ${source.encoding} + + https://github.com/lordyavin/threema-msgapi-sdk-java + + + MIT-License + http://opensource.org/licenses/mit-license.php + repo + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${source.encoding} + 17 + 17 + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + true + lib/ + ch.threema.apitool.Console + + + + + + + maven-assembly-plugin + 3.3.0 + + + + ch.threema.apitool.ConsoleMain + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + + github + GitHub lordyavin Apache Maven Packages + https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java + + + + + net.klesatschke.threema + api + 1.1.5-SNAPSHOT + + + diff --git a/source/src/main/java/ch/threema/apitool/ConsoleMain.java b/cli/src/main/java/ch/threema/apitool/ConsoleMain.java similarity index 98% rename from source/src/main/java/ch/threema/apitool/ConsoleMain.java rename to cli/src/main/java/ch/threema/apitool/ConsoleMain.java index 17b5d48..0114676 100644 --- a/source/src/main/java/ch/threema/apitool/ConsoleMain.java +++ b/cli/src/main/java/ch/threema/apitool/ConsoleMain.java @@ -45,6 +45,8 @@ import ch.threema.apitool.console.commands.SendE2EImageMessageCommand; import ch.threema.apitool.console.commands.SendE2ETextMessageCommand; import ch.threema.apitool.console.commands.SendSimpleMessageCommand; +import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.api.CryptTool; /** * Command line interface for {@link CryptTool} and {@link APIConnector} operations for testing diff --git a/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java similarity index 97% rename from source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java index 500d968..56e4b9d 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java @@ -26,7 +26,7 @@ import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.results.CapabilityResult; +import net.klesatschke.threema.api.results.CapabilityResult; public class CapabilityCommand extends Command { private final ThreemaIDField threemaIdField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/Command.java b/cli/src/main/java/ch/threema/apitool/console/commands/Command.java similarity index 98% rename from source/src/main/java/ch/threema/apitool/console/commands/Command.java rename to cli/src/main/java/ch/threema/apitool/console/commands/Command.java index 6ef693e..a352091 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/Command.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/Command.java @@ -32,8 +32,6 @@ import java.util.LinkedList; import java.util.List; -import ch.threema.apitool.APIConnector; -import ch.threema.apitool.PublicKeyStore; import ch.threema.apitool.console.commands.fields.ByteArrayField; import ch.threema.apitool.console.commands.fields.Field; import ch.threema.apitool.console.commands.fields.FileField; @@ -42,6 +40,8 @@ import ch.threema.apitool.console.commands.fields.PublicKeyField; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.api.PublicKeyStore; public abstract class Command { private final List fields = new LinkedList<>(); diff --git a/source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java similarity index 98% rename from source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java index 22b101c..9d2b4df 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java @@ -26,13 +26,13 @@ import java.nio.file.Path; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.ByteArrayField; import ch.threema.apitool.console.commands.fields.FolderField; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; import ch.threema.apitool.helpers.E2EHelper; +import net.klesatschke.threema.api.DataUtils; public class DecryptAndDownloadCommand extends Command { private final ThreemaIDField threemaId; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java similarity index 94% rename from source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java index ef795a5..43fb107 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java @@ -24,12 +24,12 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.ByteArrayField; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.PublicKeyField; -import ch.threema.apitool.messages.ThreemaMessage; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.api.messages.ThreemaMessage; public class DecryptCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java similarity index 84% rename from source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java index b3099ef..ae1079f 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java @@ -24,9 +24,10 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.Key; import ch.threema.apitool.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.Key; +import net.klesatschke.threema.api.Key.KeyType; public class DerivePublicKeyCommand extends Command { private final PrivateKeyField privateKeyField; @@ -40,10 +41,10 @@ public DerivePublicKeyCommand() { @Override protected void execute() throws Exception { - byte[] privateKey = this.privateKeyField.getValue(); - byte[] publicKey = CryptTool.derivePublicKey(privateKey); + var privateKey = this.privateKeyField.getValue(); + var publicKey = CryptTool.derivePublicKey(privateKey); System.out.println("res"); - System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); + System.out.println(new Key(KeyType.PUBLIC, publicKey).encode()); } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java similarity index 93% rename from source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java index 38cb973..2d13695 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java @@ -24,11 +24,11 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.PrivateKeyField; import ch.threema.apitool.console.commands.fields.PublicKeyField; -import ch.threema.apitool.results.EncryptResult; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.api.results.EncryptResult; public class EncryptCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java b/cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java similarity index 81% rename from source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java rename to cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java index b0b9b9c..3eec958 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java @@ -24,10 +24,10 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.APIConnector; -import ch.threema.apitool.Key; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.api.Key; +import net.klesatschke.threema.api.Key.KeyType; public class FetchPublicKey extends Command { private final ThreemaIDField threemaIdField; @@ -44,14 +44,14 @@ public FetchPublicKey() { @Override protected void execute() throws Exception { - String threemaId = this.threemaIdField.getValue(); - String from = this.fromField.getValue(); - String secret = this.secretField.getValue(); + var threemaId = this.threemaIdField.getValue(); + var from = this.fromField.getValue(); + var secret = this.secretField.getValue(); - APIConnector apiConnector = this.createConnector(from, secret); - byte[] publicKey = apiConnector.lookupKey(threemaId); + var apiConnector = this.createConnector(from, secret); + var publicKey = apiConnector.lookupKey(threemaId); if (publicKey != null) { - System.out.println(new Key(Key.KeyType.PUBLIC, publicKey).encode()); + System.out.println(new Key(KeyType.PUBLIC, publicKey).encode()); } } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java similarity index 81% rename from source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java index 0829ee3..7bd4b39 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java @@ -28,10 +28,11 @@ import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; -import ch.threema.apitool.Key; import ch.threema.apitool.console.commands.fields.TextField; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.api.Key; +import net.klesatschke.threema.api.Key.KeyType; public class GenerateKeyPairCommand extends Command { private final TextField privateKeyPath; @@ -47,15 +48,15 @@ public GenerateKeyPairCommand() { @Override protected void execute() throws Exception { - byte[] privateKey = new byte[NaCl.SECRETKEYBYTES]; - byte[] publicKey = new byte[NaCl.PUBLICKEYBYTES]; + var privateKey = new byte[NaCl.SECRETKEYBYTES]; + var publicKey = new byte[NaCl.PUBLICKEYBYTES]; CryptTool.generateKeyPair(privateKey, publicKey); // Write both keys to file DataUtils.writeKeyFile( - new File(this.privateKeyPath.getValue()), new Key(Key.KeyType.PRIVATE, privateKey)); + new File(this.privateKeyPath.getValue()), new Key(KeyType.PRIVATE, privateKey)); DataUtils.writeKeyFile( - new File(this.publicKeyPath.getValue()), new Key(Key.KeyType.PUBLIC, publicKey)); + new File(this.publicKeyPath.getValue()), new Key(KeyType.PUBLIC, publicKey)); } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java similarity index 95% rename from source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java index 7860e9c..f2b8863 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.TextField; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.DataUtils; public class HashEmailCommand extends Command { private final TextField emailField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java similarity index 95% rename from source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java index 0d7be86..38e90d2 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.DataUtils; import ch.threema.apitool.console.commands.fields.TextField; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.DataUtils; public class HashPhoneCommand extends Command { private final TextField phoneNo; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java b/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java similarity index 97% rename from source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java rename to cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java index 8f483e6..cb3e9b7 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.APIConnector; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.api.APIConnector; public class IDLookupByEmail extends Command { private final TextField emailField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java b/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java similarity index 97% rename from source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java rename to cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java index cbd99b2..a36fe8d 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.APIConnector; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.api.APIConnector; public class IDLookupByPhoneNo extends Command { private final TextField phoneNoField; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java b/cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java similarity index 97% rename from source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java rename to cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java index 1c2d8c7..878ca20 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java @@ -24,9 +24,9 @@ package ch.threema.apitool.console.commands; -import ch.threema.apitool.APIConnector; import ch.threema.apitool.console.commands.fields.TextField; import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.api.APIConnector; public class SendSimpleMessageCommand extends Command { private final ThreemaIDField threemaId; diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java similarity index 96% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java index 1372815..a9b1da8 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java @@ -24,7 +24,7 @@ package ch.threema.apitool.console.commands.fields; -import ch.threema.apitool.DataUtils; +import net.klesatschke.threema.api.DataUtils; public class ByteArrayField extends Field { public ByteArrayField(String key, boolean required) { diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java similarity index 92% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java index 81ade93..68c0b39 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/Field.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java @@ -24,8 +24,8 @@ package ch.threema.apitool.console.commands.fields; -import ch.threema.apitool.exceptions.InvalidCommandFieldValueException; -import ch.threema.apitool.exceptions.RequiredCommandFieldMissingException; +import net.klesatschke.threema.api.exceptions.InvalidCommandFieldValueException; +import net.klesatschke.threema.api.exceptions.RequiredCommandFieldMissingException; public abstract class Field { diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java similarity index 78% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java index b9e644e..b644028 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java @@ -27,26 +27,27 @@ import java.io.File; import java.io.IOException; -import ch.threema.apitool.DataUtils; -import ch.threema.apitool.Key; -import ch.threema.apitool.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.api.Key; +import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; public abstract class KeyField extends Field { - public KeyField(String key, boolean required) { + protected KeyField(String key, boolean required) { super(key, required); } - byte[] readKey(String argument, String expectedKeyType) throws IOException, InvalidKeyException { + byte[] readKey(String argument, KeyType expectedKeyType) throws IOException, InvalidKeyException { Key key; // Try to open a file with that name - File keyFile = new File(argument); + var keyFile = new File(argument); if (keyFile.isFile()) { key = DataUtils.readKeyFile(keyFile, expectedKeyType); } else { key = Key.decodeKey(argument, expectedKeyType); } - return key.key; + return key.getKey(); } } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java similarity index 89% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java index 8ce45ea..98bac02 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java @@ -24,8 +24,8 @@ package ch.threema.apitool.console.commands.fields; -import ch.threema.apitool.Key; -import ch.threema.apitool.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; public class PrivateKeyField extends KeyField { public PrivateKeyField(String key, boolean required) { @@ -34,7 +34,7 @@ public PrivateKeyField(String key, boolean required) { public byte[] getValue() throws InvalidKeyException { try { - return this.readKey(this.value, Key.KeyType.PRIVATE); + return this.readKey(this.value, KeyType.PRIVATE); } catch (Exception e) { throw new InvalidKeyException("invalid private key"); } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java similarity index 89% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java index ead0db3..1daab8d 100644 --- a/source/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java +++ b/cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java @@ -24,8 +24,8 @@ package ch.threema.apitool.console.commands.fields; -import ch.threema.apitool.Key; -import ch.threema.apitool.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; public class PublicKeyField extends KeyField { public PublicKeyField(String key, boolean required) { @@ -34,7 +34,7 @@ public PublicKeyField(String key, boolean required) { public byte[] getValue() throws InvalidKeyException { try { - return this.readKey(this.value, Key.KeyType.PUBLIC); + return this.readKey(this.value, KeyType.PUBLIC); } catch (Exception e) { throw new InvalidKeyException("invalid public key"); } diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java diff --git a/source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java b/cli/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java similarity index 100% rename from source/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java rename to cli/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java diff --git a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java b/cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java similarity index 74% rename from source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java rename to cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java index e6a442e..9e19178 100644 --- a/source/src/main/java/ch/threema/apitool/helpers/E2EHelper.java +++ b/cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java @@ -38,17 +38,17 @@ import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.APIConnector; -import ch.threema.apitool.CryptTool; -import ch.threema.apitool.exceptions.InvalidKeyException; -import ch.threema.apitool.exceptions.MessageParseException; -import ch.threema.apitool.exceptions.NotAllowedException; -import ch.threema.apitool.messages.FileMessage; -import ch.threema.apitool.messages.ImageMessage; -import ch.threema.apitool.messages.ThreemaMessage; -import ch.threema.apitool.results.CapabilityResult; -import ch.threema.apitool.results.EncryptResult; -import ch.threema.apitool.results.UploadResult; +import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.api.exceptions.InvalidKeyException; +import net.klesatschke.threema.api.exceptions.MessageParseException; +import net.klesatschke.threema.api.exceptions.NotAllowedException; +import net.klesatschke.threema.api.messages.FileMessage; +import net.klesatschke.threema.api.messages.ImageMessage; +import net.klesatschke.threema.api.messages.ThreemaMessage; +import net.klesatschke.threema.api.results.CapabilityResult; +import net.klesatschke.threema.api.results.EncryptResult; +import net.klesatschke.threema.api.results.UploadResult; /** Helper to handle Threema end-to-end encryption. */ public class E2EHelper { @@ -97,12 +97,12 @@ public E2EHelper(APIConnector apiConnector, byte[] privateKey) { */ public String sendTextMessage(String threemaId, String text) throws Exception { // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); + var publicKey = this.apiConnector.lookupKey(threemaId); if (publicKey == null) { throw new Exception("invalid threema id"); } - EncryptResult res = CryptTool.encryptTextMessage(text, this.privateKey, publicKey); + var res = CryptTool.encryptTextMessage(text, this.privateKey, publicKey); return this.apiConnector.sendE2EMessage(threemaId, res.getNonce(), res.getResult()); } @@ -120,28 +120,28 @@ public String sendTextMessage(String threemaId, String text) throws Exception { public String sendImageMessage(String threemaId, String imageFilePath) throws NotAllowedException, IOException, InvalidKeyException { // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); + var publicKey = this.apiConnector.lookupKey(threemaId); if (publicKey == null) { throw new InvalidKeyException("invalid threema id"); } // check capability of a key - CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); + var capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); if (capabilityResult == null || !capabilityResult.canImage()) { throw new NotAllowedException(); } - byte[] fileData = Files.readAllBytes(Paths.get(imageFilePath)); + var fileData = Files.readAllBytes(Paths.get(imageFilePath)); if (fileData == null) { throw new IOException("invalid file"); } // encrypt the image - EncryptResult encryptResult = CryptTool.encrypt(fileData, this.privateKey, publicKey); + var encryptResult = CryptTool.encrypt(fileData, this.privateKey, publicKey); // upload the image - UploadResult uploadResult = apiConnector.uploadFile(encryptResult); + var uploadResult = apiConnector.uploadFile(encryptResult); if (!uploadResult.isSuccess()) { throw new IOException( @@ -149,7 +149,7 @@ public String sendImageMessage(String threemaId, String imageFilePath) } // send it - EncryptResult imageMessage = + var imageMessage = CryptTool.encryptImageMessage(encryptResult, uploadResult, privateKey, publicKey); return apiConnector.sendE2EMessage( @@ -171,14 +171,14 @@ public String sendImageMessage(String threemaId, String imageFilePath) public String sendFileMessage(String threemaId, File fileMessageFile, File thumbnailMessagePath) throws InvalidKeyException, IOException, NotAllowedException { // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); + var publicKey = this.apiConnector.lookupKey(threemaId); if (publicKey == null) { throw new InvalidKeyException("invalid threema id"); } // check capability of a key - CapabilityResult capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); + var capabilityResult = this.apiConnector.lookupKeyCapability(threemaId); if (capabilityResult == null || !capabilityResult.canImage()) { throw new NotAllowedException(); } @@ -187,17 +187,17 @@ public String sendFileMessage(String threemaId, File fileMessageFile, File thumb throw new IOException("invalid file"); } - byte[] fileData = this.readFile(fileMessageFile); + var fileData = this.readFile(fileMessageFile); if (fileData == null) { throw new IOException("invalid file"); } // encrypt the image - EncryptResult encryptResult = CryptTool.encryptFileData(fileData); + var encryptResult = CryptTool.encryptFileData(fileData); // upload the image - UploadResult uploadResult = apiConnector.uploadFile(encryptResult); + var uploadResult = apiConnector.uploadFile(encryptResult); if (!uploadResult.isSuccess()) { throw new IOException( @@ -207,13 +207,13 @@ public String sendFileMessage(String threemaId, File fileMessageFile, File thumb UploadResult uploadResultThumbnail = null; if (thumbnailMessagePath != null && thumbnailMessagePath.isFile()) { - byte[] thumbnailData = this.readFile(thumbnailMessagePath); + var thumbnailData = this.readFile(thumbnailMessagePath); if (thumbnailData == null) { throw new IOException("invalid thumbnail file"); } // encrypt the thumbnail - EncryptResult encryptResultThumbnail = + var encryptResultThumbnail = CryptTool.encryptFileThumbnailData(fileData, encryptResult.getSecret()); // upload the thumbnail @@ -221,7 +221,7 @@ public String sendFileMessage(String threemaId, File fileMessageFile, File thumb } // send it - EncryptResult fileMessage = + var fileMessage = CryptTool.encryptFileMessage( encryptResult, uploadResult, @@ -253,57 +253,53 @@ public ReceiveMessageResult receiveMessage( String threemaId, String messageId, byte[] box, byte[] nonce, Path outputFolder) throws IOException, InvalidKeyException, MessageParseException { // fetch public key - byte[] publicKey = this.apiConnector.lookupKey(threemaId); + var publicKey = this.apiConnector.lookupKey(threemaId); if (publicKey == null) { throw new InvalidKeyException("invalid threema id"); } - ThreemaMessage message = CryptTool.decryptMessage(box, this.privateKey, publicKey, nonce); + var message = CryptTool.decryptMessage(box, this.privateKey, publicKey, nonce); if (message == null) { return null; } - ReceiveMessageResult result = new ReceiveMessageResult(messageId, message); + var result = new ReceiveMessageResult(messageId, message); - if (message instanceof ImageMessage) { - // download image - ImageMessage imageMessage = (ImageMessage) message; - byte[] fileData = this.apiConnector.downloadFile(imageMessage.getBlobId()); + if (message instanceof ImageMessage imageMessage) { + var fileData = this.apiConnector.downloadFile(imageMessage.getBlobId()); if (fileData == null) { throw new MessageParseException(); } - byte[] decryptedFileContent = + var decryptedFileContent = CryptTool.decrypt(fileData, privateKey, publicKey, imageMessage.getNonce()); - File imageFile = new File(outputFolder.toString() + "/" + messageId + ".jpg"); - FileOutputStream fos = new FileOutputStream(imageFile); + var imageFile = new File(outputFolder.toString() + "/" + messageId + ".jpg"); + var fos = new FileOutputStream(imageFile); fos.write(decryptedFileContent); fos.close(); result.files.add(imageFile); - } else if (message instanceof FileMessage) { - // download file - FileMessage fileMessage = (FileMessage) message; - byte[] fileData = this.apiConnector.downloadFile(fileMessage.getBlobId()); + } else if (message instanceof FileMessage fileMessage) { + var fileData = this.apiConnector.downloadFile(fileMessage.getBlobId()); - byte[] decryptedFileData = + var decryptedFileData = CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey()); - File file = + var file = new File(outputFolder.toString() + "/" + messageId + "-" + fileMessage.getFileName()); - FileOutputStream fos = new FileOutputStream(file); + var fos = new FileOutputStream(file); fos.write(decryptedFileData); fos.close(); result.files.add(file); if (fileMessage.getThumbnailBlobId() != null) { - byte[] thumbnailData = this.apiConnector.downloadFile(fileMessage.getThumbnailBlobId()); + var thumbnailData = this.apiConnector.downloadFile(fileMessage.getThumbnailBlobId()); - byte[] decryptedThumbnailData = + var decryptedThumbnailData = CryptTool.decryptFileThumbnailData(thumbnailData, fileMessage.getEncryptionKey()); - File thumbnailFile = new File(outputFolder.toString() + "/" + messageId + "-thumbnail.jpg"); + var thumbnailFile = new File(outputFolder.toString() + "/" + messageId + "-thumbnail.jpg"); fos = new FileOutputStream(thumbnailFile); fos.write(decryptedThumbnailData); fos.close(); @@ -323,8 +319,8 @@ public ReceiveMessageResult receiveMessage( * @throws IOException */ private byte[] readFile(File file) throws IOException { - int fileLength = (int) file.length(); - byte[] fileData = new byte[fileLength + NaCl.BOXOVERHEAD]; + var fileLength = (int) file.length(); + var fileData = new byte[fileLength + NaCl.BOXOVERHEAD]; IOUtils.readFully(new FileInputStream(file), fileData, NaCl.BOXOVERHEAD, fileLength); return fileData; } diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..19800ee --- /dev/null +++ b/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + net.klesatschke.threema + msgapi-sdk-java + 1.1.5-SNAPSHOT + Threema Messaging SDK + pom + + + + org.junit.jupiter + junit-jupiter-api + 5.9.1 + test + + + org.assertj + assertj-core + 3.23.1 + test + + + commons-io + commons-io + 2.10.0 + + + org.apache.commons + commons-text + 1.10.0 + + + com.google.code.gson + gson + 2.8.6 + + + + + api + cli + + \ No newline at end of file From d9beeb71a528889607c055e813a68c3d94d4a12e Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Thu, 27 Oct 2022 00:00:01 +0200 Subject: [PATCH 06/11] Renames packages --- .github/workflows/maven.yml | 4 +-- .../klesatschke/threema/cli}/ConsoleMain.java | 36 +++++++++---------- .../console/commands/CapabilityCommand.java | 6 ++-- .../cli}/console/commands/Command.java | 18 +++++----- .../cli}/console/commands/CreditsCommand.java | 6 ++-- .../commands/DecryptAndDownloadCommand.java | 14 ++++---- .../cli}/console/commands/DecryptCommand.java | 8 ++--- .../commands/DerivePublicKeyCommand.java | 4 +-- .../cli}/console/commands/EncryptCommand.java | 6 ++-- .../cli}/console/commands/FetchPublicKey.java | 6 ++-- .../commands/GenerateKeyPairCommand.java | 4 +-- .../console/commands/HashEmailCommand.java | 4 +-- .../console/commands/HashPhoneCommand.java | 4 +-- .../console/commands/IDLookupByEmail.java | 6 ++-- .../console/commands/IDLookupByPhoneNo.java | 6 ++-- .../commands/SendE2EFileMessageCommand.java | 12 +++---- .../commands/SendE2EImageMessageCommand.java | 12 +++---- .../commands/SendE2ETextMessageCommand.java | 10 +++--- .../commands/SendSimpleMessageCommand.java | 6 ++-- .../commands/fields/ByteArrayField.java | 2 +- .../cli}/console/commands/fields/Field.java | 2 +- .../console/commands/fields/FileField.java | 2 +- .../console/commands/fields/FolderField.java | 2 +- .../console/commands/fields/KeyField.java | 2 +- .../commands/fields/PrivateKeyField.java | 2 +- .../commands/fields/PublicKeyField.java | 2 +- .../console/commands/fields/TextField.java | 2 +- .../commands/fields/ThreemaIDField.java | 2 +- .../threema/cli}/helpers/E2EHelper.java | 2 +- 29 files changed, 95 insertions(+), 97 deletions(-) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/ConsoleMain.java (85%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/CapabilityCommand.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/Command.java (90%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/CreditsCommand.java (90%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/DecryptAndDownloadCommand.java (86%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/DecryptCommand.java (89%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/DerivePublicKeyCommand.java (93%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/EncryptCommand.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/FetchPublicKey.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/GenerateKeyPairCommand.java (94%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/HashEmailCommand.java (93%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/HashPhoneCommand.java (93%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/IDLookupByEmail.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/IDLookupByPhoneNo.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/SendE2EFileMessageCommand.java (87%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/SendE2EImageMessageCommand.java (86%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/SendE2ETextMessageCommand.java (87%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/SendSimpleMessageCommand.java (91%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/ByteArrayField.java (95%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/Field.java (97%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/FileField.java (96%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/FolderField.java (95%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/KeyField.java (96%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/PrivateKeyField.java (96%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/PublicKeyField.java (96%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/TextField.java (95%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/console/commands/fields/ThreemaIDField.java (95%) rename cli/src/main/java/{ch/threema/apitool => net/klesatschke/threema/cli}/helpers/E2EHelper.java (99%) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f32797f..8020a14 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,10 +28,8 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven - run: mvn -B package --file source/pom.xml + run: mvn -B package --file pom.xml # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - name: Update dependency graph uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 - with: - directory: source diff --git a/cli/src/main/java/ch/threema/apitool/ConsoleMain.java b/cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java similarity index 85% rename from cli/src/main/java/ch/threema/apitool/ConsoleMain.java rename to cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java index 0114676..3949715 100644 --- a/cli/src/main/java/ch/threema/apitool/ConsoleMain.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/ConsoleMain.java @@ -19,7 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE */ -package ch.threema.apitool; +package net.klesatschke.threema.cli; import java.util.ArrayList; import java.util.List; @@ -28,25 +28,25 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; -import ch.threema.apitool.console.commands.CapabilityCommand; -import ch.threema.apitool.console.commands.Command; -import ch.threema.apitool.console.commands.CreditsCommand; -import ch.threema.apitool.console.commands.DecryptAndDownloadCommand; -import ch.threema.apitool.console.commands.DecryptCommand; -import ch.threema.apitool.console.commands.DerivePublicKeyCommand; -import ch.threema.apitool.console.commands.EncryptCommand; -import ch.threema.apitool.console.commands.FetchPublicKey; -import ch.threema.apitool.console.commands.GenerateKeyPairCommand; -import ch.threema.apitool.console.commands.HashEmailCommand; -import ch.threema.apitool.console.commands.HashPhoneCommand; -import ch.threema.apitool.console.commands.IDLookupByEmail; -import ch.threema.apitool.console.commands.IDLookupByPhoneNo; -import ch.threema.apitool.console.commands.SendE2EFileMessageCommand; -import ch.threema.apitool.console.commands.SendE2EImageMessageCommand; -import ch.threema.apitool.console.commands.SendE2ETextMessageCommand; -import ch.threema.apitool.console.commands.SendSimpleMessageCommand; import net.klesatschke.threema.api.APIConnector; import net.klesatschke.threema.api.CryptTool; +import net.klesatschke.threema.cli.console.commands.CapabilityCommand; +import net.klesatschke.threema.cli.console.commands.Command; +import net.klesatschke.threema.cli.console.commands.CreditsCommand; +import net.klesatschke.threema.cli.console.commands.DecryptAndDownloadCommand; +import net.klesatschke.threema.cli.console.commands.DecryptCommand; +import net.klesatschke.threema.cli.console.commands.DerivePublicKeyCommand; +import net.klesatschke.threema.cli.console.commands.EncryptCommand; +import net.klesatschke.threema.cli.console.commands.FetchPublicKey; +import net.klesatschke.threema.cli.console.commands.GenerateKeyPairCommand; +import net.klesatschke.threema.cli.console.commands.HashEmailCommand; +import net.klesatschke.threema.cli.console.commands.HashPhoneCommand; +import net.klesatschke.threema.cli.console.commands.IDLookupByEmail; +import net.klesatschke.threema.cli.console.commands.IDLookupByPhoneNo; +import net.klesatschke.threema.cli.console.commands.SendE2EFileMessageCommand; +import net.klesatschke.threema.cli.console.commands.SendE2EImageMessageCommand; +import net.klesatschke.threema.cli.console.commands.SendE2ETextMessageCommand; +import net.klesatschke.threema.cli.console.commands.SendSimpleMessageCommand; /** * Command line interface for {@link CryptTool} and {@link APIConnector} operations for testing diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CapabilityCommand.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/CapabilityCommand.java index 56e4b9d..d6c45ad 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/CapabilityCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CapabilityCommand.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.results.CapabilityResult; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class CapabilityCommand extends Command { private final ThreemaIDField threemaIdField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/Command.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/Command.java similarity index 90% rename from cli/src/main/java/ch/threema/apitool/console/commands/Command.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/Command.java index a352091..b9ae086 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/Command.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/Command.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; import java.io.BufferedReader; import java.io.IOException; @@ -32,16 +32,16 @@ import java.util.LinkedList; import java.util.List; -import ch.threema.apitool.console.commands.fields.ByteArrayField; -import ch.threema.apitool.console.commands.fields.Field; -import ch.threema.apitool.console.commands.fields.FileField; -import ch.threema.apitool.console.commands.fields.FolderField; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.PublicKeyField; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.APIConnector; import net.klesatschke.threema.api.PublicKeyStore; +import net.klesatschke.threema.cli.console.commands.fields.ByteArrayField; +import net.klesatschke.threema.cli.console.commands.fields.Field; +import net.klesatschke.threema.cli.console.commands.fields.FileField; +import net.klesatschke.threema.cli.console.commands.fields.FolderField; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.PublicKeyField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public abstract class Command { private final List fields = new LinkedList<>(); diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java similarity index 90% rename from cli/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java index 0bc9c89..59911f0 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/CreditsCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java @@ -22,10 +22,10 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class CreditsCommand extends Command { private final ThreemaIDField fromField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptAndDownloadCommand.java similarity index 86% rename from cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptAndDownloadCommand.java index 9d2b4df..c071f59 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/DecryptAndDownloadCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptAndDownloadCommand.java @@ -22,17 +22,17 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; import java.nio.file.Path; -import ch.threema.apitool.console.commands.fields.ByteArrayField; -import ch.threema.apitool.console.commands.fields.FolderField; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.helpers.E2EHelper; import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.cli.console.commands.fields.ByteArrayField; +import net.klesatschke.threema.cli.console.commands.fields.FolderField; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.cli.helpers.E2EHelper; public class DecryptAndDownloadCommand extends Command { private final ThreemaIDField threemaId; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptCommand.java similarity index 89% rename from cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptCommand.java index 43fb107..bc18ec0 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/DecryptCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DecryptCommand.java @@ -22,14 +22,14 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.ByteArrayField; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.PublicKeyField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.DataUtils; import net.klesatschke.threema.api.messages.ThreemaMessage; +import net.klesatschke.threema.cli.console.commands.fields.ByteArrayField; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.PublicKeyField; public class DecryptCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DerivePublicKeyCommand.java similarity index 93% rename from cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/DerivePublicKeyCommand.java index ae1079f..f9913f8 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/DerivePublicKeyCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/DerivePublicKeyCommand.java @@ -22,12 +22,12 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.Key; import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; public class DerivePublicKeyCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/EncryptCommand.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/EncryptCommand.java index 2d13695..abf798b 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/EncryptCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/EncryptCommand.java @@ -22,13 +22,13 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.PublicKeyField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.DataUtils; import net.klesatschke.threema.api.results.EncryptResult; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.PublicKeyField; public class EncryptCommand extends Command { private final PrivateKeyField privateKeyField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/FetchPublicKey.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/FetchPublicKey.java index 3eec958..4a8432b 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/FetchPublicKey.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/FetchPublicKey.java @@ -22,12 +22,12 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.Key; import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class FetchPublicKey extends Command { private final ThreemaIDField threemaIdField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/GenerateKeyPairCommand.java similarity index 94% rename from cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/GenerateKeyPairCommand.java index 7bd4b39..32a2fe3 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/GenerateKeyPairCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/GenerateKeyPairCommand.java @@ -22,17 +22,17 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; import java.io.File; import com.neilalexander.jnacl.NaCl; -import ch.threema.apitool.console.commands.fields.TextField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.DataUtils; import net.klesatschke.threema.api.Key; import net.klesatschke.threema.api.Key.KeyType; +import net.klesatschke.threema.cli.console.commands.fields.TextField; public class GenerateKeyPairCommand extends Command { private final TextField privateKeyPath; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashEmailCommand.java similarity index 93% rename from cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashEmailCommand.java index f2b8863..a05909d 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/HashEmailCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashEmailCommand.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.cli.console.commands.fields.TextField; public class HashEmailCommand extends Command { private final TextField emailField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashPhoneCommand.java similarity index 93% rename from cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashPhoneCommand.java index 38e90d2..4e7b6ee 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/HashPhoneCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/HashPhoneCommand.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.DataUtils; +import net.klesatschke.threema.cli.console.commands.fields.TextField; public class HashPhoneCommand extends Command { private final TextField phoneNo; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByEmail.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByEmail.java index cb3e9b7..bef664c 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByEmail.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByEmail.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class IDLookupByEmail extends Command { private final TextField emailField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByPhoneNo.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByPhoneNo.java index a36fe8d..007a8d8 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/IDLookupByPhoneNo.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/IDLookupByPhoneNo.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class IDLookupByPhoneNo extends Command { private final TextField phoneNoField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EFileMessageCommand.java similarity index 87% rename from cli/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EFileMessageCommand.java index 82e103f..a59edad 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EFileMessageCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EFileMessageCommand.java @@ -22,15 +22,15 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; import java.io.File; -import ch.threema.apitool.console.commands.fields.FileField; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.helpers.E2EHelper; +import net.klesatschke.threema.cli.console.commands.fields.FileField; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.cli.helpers.E2EHelper; public class SendE2EFileMessageCommand extends Command { private final ThreemaIDField threemaId; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EImageMessageCommand.java similarity index 86% rename from cli/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EImageMessageCommand.java index 0640a44..4f3556b 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2EImageMessageCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2EImageMessageCommand.java @@ -22,15 +22,15 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; import java.nio.file.Path; -import ch.threema.apitool.console.commands.fields.FolderField; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.helpers.E2EHelper; +import net.klesatschke.threema.cli.console.commands.fields.FolderField; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.cli.helpers.E2EHelper; public class SendE2EImageMessageCommand extends Command { private final ThreemaIDField toField; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2ETextMessageCommand.java similarity index 87% rename from cli/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2ETextMessageCommand.java index 9c74496..70c0920 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/SendE2ETextMessageCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendE2ETextMessageCommand.java @@ -22,12 +22,12 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.PrivateKeyField; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; -import ch.threema.apitool.helpers.E2EHelper; +import net.klesatschke.threema.cli.console.commands.fields.PrivateKeyField; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; +import net.klesatschke.threema.cli.helpers.E2EHelper; public class SendE2ETextMessageCommand extends Command { private final ThreemaIDField threemaId; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendSimpleMessageCommand.java similarity index 91% rename from cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendSimpleMessageCommand.java index 878ca20..2ca3472 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/SendSimpleMessageCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/SendSimpleMessageCommand.java @@ -22,11 +22,11 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands; +package net.klesatschke.threema.cli.console.commands; -import ch.threema.apitool.console.commands.fields.TextField; -import ch.threema.apitool.console.commands.fields.ThreemaIDField; import net.klesatschke.threema.api.APIConnector; +import net.klesatschke.threema.cli.console.commands.fields.TextField; +import net.klesatschke.threema.cli.console.commands.fields.ThreemaIDField; public class SendSimpleMessageCommand extends Command { private final ThreemaIDField threemaId; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ByteArrayField.java similarity index 95% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ByteArrayField.java index a9b1da8..42f9245 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/ByteArrayField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ByteArrayField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import net.klesatschke.threema.api.DataUtils; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java similarity index 97% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java index 68c0b39..cb8b8cd 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/Field.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import net.klesatschke.threema.api.exceptions.InvalidCommandFieldValueException; import net.klesatschke.threema.api.exceptions.RequiredCommandFieldMissingException; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FileField.java similarity index 96% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FileField.java index d650b3d..0d02c8a 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/FileField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FileField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import java.io.File; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FolderField.java similarity index 95% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FolderField.java index efc8cf6..a3f8363 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/FolderField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/FolderField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/KeyField.java similarity index 96% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/KeyField.java index b644028..d15df28 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/KeyField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/KeyField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import java.io.File; import java.io.IOException; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PrivateKeyField.java similarity index 96% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PrivateKeyField.java index 98bac02..c0d74a2 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/PrivateKeyField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PrivateKeyField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import net.klesatschke.threema.api.Key.KeyType; import net.klesatschke.threema.api.exceptions.InvalidKeyException; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PublicKeyField.java similarity index 96% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PublicKeyField.java index 1daab8d..a51cafe 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/PublicKeyField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/PublicKeyField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; import net.klesatschke.threema.api.Key.KeyType; import net.klesatschke.threema.api.exceptions.InvalidKeyException; diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/TextField.java similarity index 95% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/TextField.java index 9d73bae..c2897c5 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/TextField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/TextField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; public class TextField extends Field { public TextField(String key, boolean required) { diff --git a/cli/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ThreemaIDField.java similarity index 95% rename from cli/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java rename to cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ThreemaIDField.java index 31f3e6a..ba3ed00 100644 --- a/cli/src/main/java/ch/threema/apitool/console/commands/fields/ThreemaIDField.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/ThreemaIDField.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.console.commands.fields; +package net.klesatschke.threema.cli.console.commands.fields; public class ThreemaIDField extends Field { public ThreemaIDField(String key, boolean required) { diff --git a/cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java similarity index 99% rename from cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java rename to cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java index 9e19178..f71d2e2 100644 --- a/cli/src/main/java/ch/threema/apitool/helpers/E2EHelper.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package ch.threema.apitool.helpers; +package net.klesatschke.threema.cli.helpers; import java.io.File; import java.io.FileInputStream; From c07a2dabd2bafa117dd3e227069263aa9a0a7bc6 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Thu, 27 Oct 2022 00:21:10 +0200 Subject: [PATCH 07/11] make it building --- api/pom.xml | 28 +------------ .../klesatschke/threema/api/CryptTool.java | 2 +- cli/pom.xml | 39 +++++++------------ .../cli/console/commands/CreditsCommand.java | 1 - .../threema/cli/helpers/E2EHelper.java | 5 +-- pom.xml | 33 ++++++++++++++++ 6 files changed, 49 insertions(+), 59 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 1b0b29a..e1bd413 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ 1.1.5-SNAPSHOT api - Threema Messsge Gateway API + Threema Messsage Gateway API MIT-License @@ -15,31 +15,6 @@ https://github.com/lordyavin/threema-msgapi-sdk-java - - UTF-8 - ${source.encoding} - ${source.encoding} - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - ${source.encoding} - 17 - 17 - - - org.projectlombok - lombok - - - - - - org.junit.jupiter @@ -66,7 +41,6 @@ org.projectlombok lombok - 1.18.22 provided diff --git a/api/src/main/java/net/klesatschke/threema/api/CryptTool.java b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java index 72b2da3..a5ea37b 100644 --- a/api/src/main/java/net/klesatschke/threema/api/CryptTool.java +++ b/api/src/main/java/net/klesatschke/threema/api/CryptTool.java @@ -53,7 +53,7 @@ public class CryptTool { private static final String HMAC_SHA256 = "HmacSHA256"; -private CryptTool() {} + private CryptTool() {} /* HMAC-SHA256 keys for email/mobile phone hashing */ private static final byte[] EMAIL_HMAC_KEY = { diff --git a/cli/pom.xml b/cli/pom.xml index fae8b26..e1b5937 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -9,12 +9,7 @@ 1.1.5-SNAPSHOT cli - Threema Messsge Gateway CLI - - UTF-8 - ${source.encoding} - ${source.encoding} - + Threema Messsage Gateway CLI https://github.com/lordyavin/threema-msgapi-sdk-java @@ -23,19 +18,11 @@ repo - + + net.klesatschke.threema.cli.ConsoleMain + - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - ${source.encoding} - 17 - 17 - - org.apache.maven.plugins @@ -46,7 +33,7 @@ true lib/ - ch.threema.apitool.Console + ${mainClass} @@ -58,7 +45,7 @@ - ch.threema.apitool.ConsoleMain + ${mainClass} @@ -77,13 +64,6 @@ - - - github - GitHub lordyavin Apache Maven Packages - https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java - - net.klesatschke.threema @@ -91,4 +71,11 @@ 1.1.5-SNAPSHOT + + + github + GitHub lordyavin Apache Maven Packages + https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java + + diff --git a/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java index 59911f0..27451c8 100644 --- a/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/CreditsCommand.java @@ -49,7 +49,6 @@ protected void execute() throws Exception { System.out.println("Remaining credits: " + credits); } else { System.out.println("Error fetching credits"); - } } } diff --git a/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java index f71d2e2..7a7e9c2 100644 --- a/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java @@ -46,8 +46,6 @@ import net.klesatschke.threema.api.messages.FileMessage; import net.klesatschke.threema.api.messages.ImageMessage; import net.klesatschke.threema.api.messages.ThreemaMessage; -import net.klesatschke.threema.api.results.CapabilityResult; -import net.klesatschke.threema.api.results.EncryptResult; import net.klesatschke.threema.api.results.UploadResult; /** Helper to handle Threema end-to-end encryption. */ @@ -284,8 +282,7 @@ public ReceiveMessageResult receiveMessage( } else if (message instanceof FileMessage fileMessage) { var fileData = this.apiConnector.downloadFile(fileMessage.getBlobId()); - var decryptedFileData = - CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey()); + var decryptedFileData = CryptTool.decryptFileData(fileData, fileMessage.getEncryptionKey()); var file = new File(outputFolder.toString() + "/" + messageId + "-" + fileMessage.getFileName()); var fos = new FileOutputStream(file); diff --git a/pom.xml b/pom.xml index 19800ee..2713c34 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,12 @@ 1.1.5-SNAPSHOT Threema Messaging SDK pom + + UTF-8 + ${source.encoding} + ${source.encoding} + 1.18.22 + @@ -22,6 +28,11 @@ 3.23.1 test + + org.projectlombok + lombok + ${lombok.version} + commons-io commons-io @@ -39,6 +50,28 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${source.encoding} + 17 + 17 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + api cli From cb1ccc567ae70c824e27c298b18683ba2e8173c5 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 27 Oct 2022 00:30:56 +0200 Subject: [PATCH 08/11] Update maven.yml --- .github/workflows/maven.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 8020a14..ab4f43c 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -31,5 +31,12 @@ jobs: run: mvn -B package --file pom.xml # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - - name: Update dependency graph + - name: Update dependency graph API uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 + with: + directory: api + + - name: Update dependency graph CLI + uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 + with: + directory: cli From 5c4d171fb3daf885785645818996af96480c7316 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 2 Nov 2022 20:15:34 +0100 Subject: [PATCH 09/11] removes jnacl sources --- api/pom.xml | 4 + .../java/com/neilalexander/jnacl/NaCl.java | 696 ------------------ .../jnacl/crypto/curve25519.java | 428 ----------- .../crypto/curve25519xsalsa20poly1305.java | 99 --- .../neilalexander/jnacl/crypto/hsalsa20.java | 157 ---- .../neilalexander/jnacl/crypto/poly1305.java | 158 ---- .../neilalexander/jnacl/crypto/salsa20.java | 303 -------- .../neilalexander/jnacl/crypto/verify_16.java | 40 - .../neilalexander/jnacl/crypto/xsalsa20.java | 76 -- .../jnacl/crypto/xsalsa20poly1305.java | 93 --- pom.xml | 8 +- source/pom.xml | 116 --- 12 files changed, 10 insertions(+), 2168 deletions(-) delete mode 100644 api/src/main/java/com/neilalexander/jnacl/NaCl.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java delete mode 100644 api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java delete mode 100644 source/pom.xml diff --git a/api/pom.xml b/api/pom.xml index e1bd413..e6bd726 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -43,6 +43,10 @@ lombok provided
+ + eu.neilalexander + jnacl +
diff --git a/api/src/main/java/com/neilalexander/jnacl/NaCl.java b/api/src/main/java/com/neilalexander/jnacl/NaCl.java deleted file mode 100644 index dc7f20d..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/NaCl.java +++ /dev/null @@ -1,696 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl; - -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Formatter; - -import com.neilalexander.jnacl.crypto.curve25519xsalsa20poly1305; -import com.neilalexander.jnacl.crypto.xsalsa20; -import com.neilalexander.jnacl.crypto.xsalsa20poly1305; - -public class NaCl { - public static final int PUBLICKEYBYTES = 32; - public static final int SECRETKEYBYTES = 32; - public static final int BEFORENMBYTES = 32; - public static final int NONCEBYTES = 24; - public static final int ZEROBYTES = 32; - public static final int BOXZEROBYTES = 16; - public static final int BOXOVERHEAD = ZEROBYTES - BOXZEROBYTES; - public static final int SYMMKEYBYTES = 32; - public static final int STREAMKEYBYTES = 32; - - private final byte[] precomputed = new byte[BEFORENMBYTES]; - - /* Perform self test before anything else */ - static { - selfTest(); - } - - public NaCl(byte[] privatekey, byte[] publickey) { - if (privatekey.length != SECRETKEYBYTES) throw new Error("Invalid private key length"); - - if (publickey.length != PUBLICKEYBYTES) throw new Error("Invalid public key length"); - - curve25519xsalsa20poly1305.crypto_box_beforenm(this.precomputed, publickey, privatekey); - } - - public NaCl(String privatekey, String publickey) { - this(getBinary(privatekey), getBinary(publickey)); - } - - public byte[] encrypt(byte[] input, byte[] nonce) { - return encrypt(input, input.length, nonce); - } - - public byte[] encrypt(byte[] input, int inputlength, byte[] nonce) { - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - byte[] output = new byte[inputlength + BOXOVERHEAD]; - curve25519xsalsa20poly1305.crypto_box_afternm_nopad( - output, 0, input, 0, input.length, nonce, this.precomputed); - - return output; - } - - public byte[] decrypt(byte[] input, byte[] nonce) { - return decrypt(input, input.length, nonce); - } - - public byte[] decrypt(byte[] input, int inputlength, byte[] nonce) { - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - if (inputlength < BOXOVERHEAD) return null; - - byte[] output = new byte[inputlength - BOXOVERHEAD]; - if (curve25519xsalsa20poly1305.crypto_box_open_afternm_nopad( - output, 0, input, 0, input.length, nonce, this.precomputed) - != 0) return null; - - return output; - } - - public static void genkeypair(byte[] publickey, byte[] privatekey) { - genkeypair(publickey, privatekey, null); - } - - public static void genkeypair(byte[] publickey, byte[] privatekey, byte[] seed) { - SecureRandom random = new SecureRandom(); - - random.nextBytes(privatekey); - - if (seed != null) { - if (seed.length != SECRETKEYBYTES) throw new Error("Invalid seed length"); - - for (int i = 0; i < SECRETKEYBYTES; i++) privatekey[i] ^= seed[i]; - } - - curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); - } - - public static byte[] derivePublicKey(byte[] privatekey) { - if (privatekey.length != SECRETKEYBYTES) throw new Error("Invalid private key length"); - - byte[] publickey = new byte[PUBLICKEYBYTES]; - curve25519xsalsa20poly1305.crypto_box_getpublickey(publickey, privatekey); - return publickey; - } - - public static byte[] symmetricEncryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - byte[] output = new byte[input.length + BOXOVERHEAD]; - xsalsa20poly1305.crypto_secretbox_nopad(output, 0, input, 0, input.length, nonce, key); - - return output; - } - - /** - * In-place version of {@link #symmetricEncryptData(byte[], byte[], byte[])} that stores the - * output in the same byte array as the input. The input data must begin at offset {@link - * #BOXOVERHEAD} in the array (the first BOXOVERHEAD bytes are ignored and will be overwritten - * with the message authentication code during encryption). - * - * @param io plaintext on input (starting at offset BOXOVERHEAD), ciphertext on return (full - * array) - * @param key encryption key - * @param nonce encryption nonce - */ - public static void symmetricEncryptDataInplace(byte[] io, byte[] key, byte[] nonce) { - - if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - if (io.length < BOXOVERHEAD) throw new Error("Invalid I/O length"); - - xsalsa20poly1305.crypto_secretbox_nopad( - io, 0, io, BOXOVERHEAD, io.length - BOXOVERHEAD, nonce, key); - } - - public static byte[] symmetricDecryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - byte[] output = new byte[input.length - BOXOVERHEAD]; - if (xsalsa20poly1305.crypto_secretbox_open_nopad(output, 0, input, 0, input.length, nonce, key) - != 0) return null; - - return output; - } - - /** - * In-place version of {@link #symmetricDecryptData(byte[], byte[], byte[])} that stores the - * output in the same byte array as the input. Note that the decrypted output is shorter than the - * input, so the last {@link #BOXOVERHEAD} bytes should be ignored in the decrypted output. - * - * @param io ciphertext on input (full array), plaintext on output (last BOXOVERHEAD bytes set to - * zero) - * @param key encryption key - * @param nonce encryption nonce - * @return decryption successful true/false - */ - public static boolean symmetricDecryptDataInplace(byte[] io, byte[] key, byte[] nonce) { - if (key.length != SYMMKEYBYTES) throw new Error("Invalid symmetric key length"); - - if (nonce.length != NONCEBYTES) throw new Error("Invalid nonce length"); - - if (io.length < BOXOVERHEAD) throw new Error("Invalid I/O length"); - - if (xsalsa20poly1305.crypto_secretbox_open_nopad(io, 0, io, 0, io.length, nonce, key) != 0) - return false; - - /* zeroize last bytes */ - for (int i = io.length - BOXOVERHEAD; i < io.length; i++) io[i] = 0; - - return true; - } - - public static byte[] streamCryptData(byte[] input, byte[] key, byte[] nonce) { - if (key.length != STREAMKEYBYTES) throw new Error("Invalid symmetric key length"); - - byte[] output = new byte[input.length]; - xsalsa20.crypto_stream_xor(output, input, input.length, nonce, key); - - return output; - } - - public static byte[] getBinary(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - - for (int i = 0; i < len; i += 2) - data[i / 2] = - (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); - - return data; - } - - public static String asHex(byte[] buf) { - try (Formatter formatter = new Formatter()) { - for (byte b : buf) formatter.format("%02x", b); - return formatter.toString(); - } - } - - public static String asHex(int[] buf) { - try (Formatter formatter = new Formatter()) { - for (int b : buf) formatter.format("%02x", b); - return formatter.toString(); - } - } - - public static void selfTest() { - /* test vectors from tests/box.* in nacl distribution */ - byte alicepk[] = - new byte[] { - (byte) 0x85, - (byte) 0x20, - (byte) 0xf0, - (byte) 0x09, - (byte) 0x89, - (byte) 0x30, - (byte) 0xa7, - (byte) 0x54, - (byte) 0x74, - (byte) 0x8b, - (byte) 0x7d, - (byte) 0xdc, - (byte) 0xb4, - (byte) 0x3e, - (byte) 0xf7, - (byte) 0x5a, - (byte) 0x0d, - (byte) 0xbf, - (byte) 0x3a, - (byte) 0x0d, - (byte) 0x26, - (byte) 0x38, - (byte) 0x1a, - (byte) 0xf4, - (byte) 0xeb, - (byte) 0xa4, - (byte) 0xa9, - (byte) 0x8e, - (byte) 0xaa, - (byte) 0x9b, - (byte) 0x4e, - (byte) 0x6a - }; - - byte alicesk[] = { - (byte) 0x77, - (byte) 0x07, - (byte) 0x6d, - (byte) 0x0a, - (byte) 0x73, - (byte) 0x18, - (byte) 0xa5, - (byte) 0x7d, - (byte) 0x3c, - (byte) 0x16, - (byte) 0xc1, - (byte) 0x72, - (byte) 0x51, - (byte) 0xb2, - (byte) 0x66, - (byte) 0x45, - (byte) 0xdf, - (byte) 0x4c, - (byte) 0x2f, - (byte) 0x87, - (byte) 0xeb, - (byte) 0xc0, - (byte) 0x99, - (byte) 0x2a, - (byte) 0xb1, - (byte) 0x77, - (byte) 0xfb, - (byte) 0xa5, - (byte) 0x1d, - (byte) 0xb9, - (byte) 0x2c, - (byte) 0x2a - }; - - byte bobpk[] = { - (byte) 0xde, - (byte) 0x9e, - (byte) 0xdb, - (byte) 0x7d, - (byte) 0x7b, - (byte) 0x7d, - (byte) 0xc1, - (byte) 0xb4, - (byte) 0xd3, - (byte) 0x5b, - (byte) 0x61, - (byte) 0xc2, - (byte) 0xec, - (byte) 0xe4, - (byte) 0x35, - (byte) 0x37, - (byte) 0x3f, - (byte) 0x83, - (byte) 0x43, - (byte) 0xc8, - (byte) 0x5b, - (byte) 0x78, - (byte) 0x67, - (byte) 0x4d, - (byte) 0xad, - (byte) 0xfc, - (byte) 0x7e, - (byte) 0x14, - (byte) 0x6f, - (byte) 0x88, - (byte) 0x2b, - (byte) 0x4f - }; - - byte bobsk[] = { - (byte) 0x5d, - (byte) 0xab, - (byte) 0x08, - (byte) 0x7e, - (byte) 0x62, - (byte) 0x4a, - (byte) 0x8a, - (byte) 0x4b, - (byte) 0x79, - (byte) 0xe1, - (byte) 0x7f, - (byte) 0x8b, - (byte) 0x83, - (byte) 0x80, - (byte) 0x0e, - (byte) 0xe6, - (byte) 0x6f, - (byte) 0x3b, - (byte) 0xb1, - (byte) 0x29, - (byte) 0x26, - (byte) 0x18, - (byte) 0xb6, - (byte) 0xfd, - (byte) 0x1c, - (byte) 0x2f, - (byte) 0x8b, - (byte) 0x27, - (byte) 0xff, - (byte) 0x88, - (byte) 0xe0, - (byte) 0xeb - }; - - byte nonce[] = { - (byte) 0x69, - (byte) 0x69, - (byte) 0x6e, - (byte) 0xe9, - (byte) 0x55, - (byte) 0xb6, - (byte) 0x2b, - (byte) 0x73, - (byte) 0xcd, - (byte) 0x62, - (byte) 0xbd, - (byte) 0xa8, - (byte) 0x75, - (byte) 0xfc, - (byte) 0x73, - (byte) 0xd6, - (byte) 0x82, - (byte) 0x19, - (byte) 0xe0, - (byte) 0x03, - (byte) 0x6b, - (byte) 0x7a, - (byte) 0x0b, - (byte) 0x37 - }; - - byte m[] = { - (byte) 0xbe, - (byte) 0x07, - (byte) 0x5f, - (byte) 0xc5, - (byte) 0x3c, - (byte) 0x81, - (byte) 0xf2, - (byte) 0xd5, - (byte) 0xcf, - (byte) 0x14, - (byte) 0x13, - (byte) 0x16, - (byte) 0xeb, - (byte) 0xeb, - (byte) 0x0c, - (byte) 0x7b, - (byte) 0x52, - (byte) 0x28, - (byte) 0xc5, - (byte) 0x2a, - (byte) 0x4c, - (byte) 0x62, - (byte) 0xcb, - (byte) 0xd4, - (byte) 0x4b, - (byte) 0x66, - (byte) 0x84, - (byte) 0x9b, - (byte) 0x64, - (byte) 0x24, - (byte) 0x4f, - (byte) 0xfc, - (byte) 0xe5, - (byte) 0xec, - (byte) 0xba, - (byte) 0xaf, - (byte) 0x33, - (byte) 0xbd, - (byte) 0x75, - (byte) 0x1a, - (byte) 0x1a, - (byte) 0xc7, - (byte) 0x28, - (byte) 0xd4, - (byte) 0x5e, - (byte) 0x6c, - (byte) 0x61, - (byte) 0x29, - (byte) 0x6c, - (byte) 0xdc, - (byte) 0x3c, - (byte) 0x01, - (byte) 0x23, - (byte) 0x35, - (byte) 0x61, - (byte) 0xf4, - (byte) 0x1d, - (byte) 0xb6, - (byte) 0x6c, - (byte) 0xce, - (byte) 0x31, - (byte) 0x4a, - (byte) 0xdb, - (byte) 0x31, - (byte) 0x0e, - (byte) 0x3b, - (byte) 0xe8, - (byte) 0x25, - (byte) 0x0c, - (byte) 0x46, - (byte) 0xf0, - (byte) 0x6d, - (byte) 0xce, - (byte) 0xea, - (byte) 0x3a, - (byte) 0x7f, - (byte) 0xa1, - (byte) 0x34, - (byte) 0x80, - (byte) 0x57, - (byte) 0xe2, - (byte) 0xf6, - (byte) 0x55, - (byte) 0x6a, - (byte) 0xd6, - (byte) 0xb1, - (byte) 0x31, - (byte) 0x8a, - (byte) 0x02, - (byte) 0x4a, - (byte) 0x83, - (byte) 0x8f, - (byte) 0x21, - (byte) 0xaf, - (byte) 0x1f, - (byte) 0xde, - (byte) 0x04, - (byte) 0x89, - (byte) 0x77, - (byte) 0xeb, - (byte) 0x48, - (byte) 0xf5, - (byte) 0x9f, - (byte) 0xfd, - (byte) 0x49, - (byte) 0x24, - (byte) 0xca, - (byte) 0x1c, - (byte) 0x60, - (byte) 0x90, - (byte) 0x2e, - (byte) 0x52, - (byte) 0xf0, - (byte) 0xa0, - (byte) 0x89, - (byte) 0xbc, - (byte) 0x76, - (byte) 0x89, - (byte) 0x70, - (byte) 0x40, - (byte) 0xe0, - (byte) 0x82, - (byte) 0xf9, - (byte) 0x37, - (byte) 0x76, - (byte) 0x38, - (byte) 0x48, - (byte) 0x64, - (byte) 0x5e, - (byte) 0x07, - (byte) 0x05 - }; - - byte c_expected[] = { - (byte) 0xf3, - (byte) 0xff, - (byte) 0xc7, - (byte) 0x70, - (byte) 0x3f, - (byte) 0x94, - (byte) 0x00, - (byte) 0xe5, - (byte) 0x2a, - (byte) 0x7d, - (byte) 0xfb, - (byte) 0x4b, - (byte) 0x3d, - (byte) 0x33, - (byte) 0x05, - (byte) 0xd9, - (byte) 0x8e, - (byte) 0x99, - (byte) 0x3b, - (byte) 0x9f, - (byte) 0x48, - (byte) 0x68, - (byte) 0x12, - (byte) 0x73, - (byte) 0xc2, - (byte) 0x96, - (byte) 0x50, - (byte) 0xba, - (byte) 0x32, - (byte) 0xfc, - (byte) 0x76, - (byte) 0xce, - (byte) 0x48, - (byte) 0x33, - (byte) 0x2e, - (byte) 0xa7, - (byte) 0x16, - (byte) 0x4d, - (byte) 0x96, - (byte) 0xa4, - (byte) 0x47, - (byte) 0x6f, - (byte) 0xb8, - (byte) 0xc5, - (byte) 0x31, - (byte) 0xa1, - (byte) 0x18, - (byte) 0x6a, - (byte) 0xc0, - (byte) 0xdf, - (byte) 0xc1, - (byte) 0x7c, - (byte) 0x98, - (byte) 0xdc, - (byte) 0xe8, - (byte) 0x7b, - (byte) 0x4d, - (byte) 0xa7, - (byte) 0xf0, - (byte) 0x11, - (byte) 0xec, - (byte) 0x48, - (byte) 0xc9, - (byte) 0x72, - (byte) 0x71, - (byte) 0xd2, - (byte) 0xc2, - (byte) 0x0f, - (byte) 0x9b, - (byte) 0x92, - (byte) 0x8f, - (byte) 0xe2, - (byte) 0x27, - (byte) 0x0d, - (byte) 0x6f, - (byte) 0xb8, - (byte) 0x63, - (byte) 0xd5, - (byte) 0x17, - (byte) 0x38, - (byte) 0xb4, - (byte) 0x8e, - (byte) 0xee, - (byte) 0xe3, - (byte) 0x14, - (byte) 0xa7, - (byte) 0xcc, - (byte) 0x8a, - (byte) 0xb9, - (byte) 0x32, - (byte) 0x16, - (byte) 0x45, - (byte) 0x48, - (byte) 0xe5, - (byte) 0x26, - (byte) 0xae, - (byte) 0x90, - (byte) 0x22, - (byte) 0x43, - (byte) 0x68, - (byte) 0x51, - (byte) 0x7a, - (byte) 0xcf, - (byte) 0xea, - (byte) 0xbd, - (byte) 0x6b, - (byte) 0xb3, - (byte) 0x73, - (byte) 0x2b, - (byte) 0xc0, - (byte) 0xe9, - (byte) 0xda, - (byte) 0x99, - (byte) 0x83, - (byte) 0x2b, - (byte) 0x61, - (byte) 0xca, - (byte) 0x01, - (byte) 0xb6, - (byte) 0xde, - (byte) 0x56, - (byte) 0x24, - (byte) 0x4a, - (byte) 0x9e, - (byte) 0x88, - (byte) 0xd5, - (byte) 0xf9, - (byte) 0xb3, - (byte) 0x79, - (byte) 0x73, - (byte) 0xf6, - (byte) 0x22, - (byte) 0xa4, - (byte) 0x3d, - (byte) 0x14, - (byte) 0xa6, - (byte) 0x59, - (byte) 0x9b, - (byte) 0x1f, - (byte) 0x65, - (byte) 0x4c, - (byte) 0xb4, - (byte) 0x5a, - (byte) 0x74, - (byte) 0xe3, - (byte) 0x55, - (byte) 0xa5 - }; - - /* encrypt data and compare with expected result */ - NaCl nacl = new NaCl(alicesk, bobpk); - byte[] c = nacl.encrypt(m, nonce); - if (!Arrays.equals(c, c_expected)) throw new RuntimeException("Crypto self-test failed (1)"); - - /* decrypt data and compare with plaintext */ - nacl = new NaCl(bobsk, alicepk); - byte[] p_d = nacl.decrypt(c, nonce); - if (!Arrays.equals(p_d, m)) throw new RuntimeException("Crypto self-test failed (2)"); - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java b/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java deleted file mode 100644 index 1276233..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519.java +++ /dev/null @@ -1,428 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class curve25519 { - final int CRYPTO_BYTES = 32; - final int CRYPTO_SCALARBYTES = 32; - - static byte[] basev = { - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - static int[] minusp = { - 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 128 - }; - - public static int crypto_scalarmult_base(byte[] q, byte[] n) { - byte[] basevp = basev; - return crypto_scalarmult(q, n, basevp); - } - - static void add(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { - int u = 0; - - for (int j = 0; j < 31; ++j) { - u += a[aoffset + j] + b[boffset + j]; - outv[outvoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31] + b[boffset + 31]; - outv[outvoffset + 31] = u; - } - - static void sub(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { - int u = 218; - - for (int j = 0; j < 31; ++j) { - u += a[aoffset + j] + 65280 - b[boffset + j]; - outv[outvoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31] - b[boffset + 31]; - outv[outvoffset + 31] = u; - } - - static void squeeze(int[] a, int aoffset) { - int u = 0; - - for (int j = 0; j < 31; ++j) { - u += a[aoffset + j]; - a[aoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31]; - a[aoffset + 31] = u & 127; - u = 19 * (u >>> 7); - - for (int j = 0; j < 31; ++j) { - u += a[aoffset + j]; - a[aoffset + j] = u & 255; - u >>>= 8; - } - - u += a[aoffset + 31]; - a[aoffset + 31] = u; - } - - static void freeze(int[] a, int aoffset) { - int[] aorig = new int[32]; - - for (int j = 0; j < 32; ++j) aorig[j] = a[aoffset + j]; - - int[] minuspp = minusp; - - add(a, 0, a, 0, minuspp, 0); - - int negative = (-((a[aoffset + 31] >>> 7) & 1)); - - for (int j = 0; j < 32; ++j) a[aoffset + j] ^= negative & (aorig[j] ^ a[aoffset + j]); - } - - static void mult(int[] outv, int outvoffset, int[] a, int aoffset, int[] b, int boffset) { - int j; - - for (int i = 0; i < 32; ++i) { - int u = 0; - - for (j = 0; j <= i; ++j) u += a[aoffset + j] * b[boffset + i - j]; - - for (j = i + 1; j < 32; ++j) u += 38 * a[aoffset + j] * b[boffset + i + 32 - j]; - - outv[outvoffset + i] = u; - } - - squeeze(outv, outvoffset); - } - - static void mult121665(int[] outv, int[] a) { - int j; - int u = 0; - - for (j = 0; j < 31; ++j) { - u += 121665 * a[j]; - outv[j] = u & 255; - u >>>= 8; - } - - u += 121665 * a[31]; - outv[31] = u & 127; - u = 19 * (u >>> 7); - - for (j = 0; j < 31; ++j) { - u += outv[j]; - outv[j] = u & 255; - u >>>= 8; - } - - u += outv[j]; - outv[j] = u; - } - - static void square(int[] outv, int outvoffset, int[] a, int aoffset) { - int j; - - for (int i = 0; i < 32; ++i) { - int u = 0; - - for (j = 0; j < i - j; ++j) u += a[aoffset + j] * a[aoffset + i - j]; - - for (j = i + 1; j < i + 32 - j; ++j) u += 38 * a[aoffset + j] * a[aoffset + i + 32 - j]; - - u *= 2; - - if ((i & 1) == 0) { - u += a[aoffset + i / 2] * a[aoffset + i / 2]; - u += 38 * a[aoffset + i / 2 + 16] * a[aoffset + i / 2 + 16]; - } - - outv[outvoffset + i] = u; - } - - squeeze(outv, outvoffset); - } - - static void select(int[] p, int[] q, int[] r, int[] s, int b) { - int bminus1 = b - 1; - - for (int j = 0; j < 64; ++j) { - int t = bminus1 & (r[j] ^ s[j]); - p[j] = s[j] ^ t; - q[j] = r[j] ^ t; - } - } - - static void mainloop(int[] work, byte[] e) { - int[] xzm1 = new int[64]; - int[] xzm = new int[64]; - int[] xzmb = new int[64]; - int[] xzm1b = new int[64]; - int[] xznb = new int[64]; - int[] xzn1b = new int[64]; - int[] a0 = new int[64]; - int[] a1 = new int[64]; - int[] b0 = new int[64]; - int[] b1 = new int[64]; - int[] c1 = new int[64]; - int[] r = new int[32]; - int[] s = new int[32]; - int[] t = new int[32]; - int[] u = new int[32]; - - for (int j = 0; j < 32; ++j) xzm1[j] = work[j]; - - xzm1[32] = 1; - - for (int j = 33; j < 64; ++j) xzm1[j] = 0; - - xzm[0] = 1; - - for (int j = 1; j < 64; ++j) xzm[j] = 0; - - int[] xzmbp = xzmb, a0p = a0, xzm1bp = xzm1b; - int[] a1p = a1, b0p = b0, b1p = b1, c1p = c1; - int[] xznbp = xznb, up = u, xzn1bp = xzn1b; - int[] workp = work, sp = s, rp = r; - - for (int pos = 254; pos >= 0; --pos) { - int b = ((e[pos / 8] & 0xFF) >>> (pos & 7)); - b &= 1; - select(xzmb, xzm1b, xzm, xzm1, b); - add(a0, 0, xzmb, 0, xzmbp, 32); - sub(a0p, 32, xzmb, 0, xzmbp, 32); - add(a1, 0, xzm1b, 0, xzm1bp, 32); - sub(a1p, 32, xzm1b, 0, xzm1bp, 32); - square(b0p, 0, a0p, 0); - square(b0p, 32, a0p, 32); - mult(b1p, 0, a1p, 0, a0p, 32); - mult(b1p, 32, a1p, 32, a0p, 0); - add(c1, 0, b1, 0, b1p, 32); - sub(c1p, 32, b1, 0, b1p, 32); - square(rp, 0, c1p, 32); - sub(sp, 0, b0, 0, b0p, 32); - mult121665(t, s); - add(u, 0, t, 0, b0p, 0); - mult(xznbp, 0, b0p, 0, b0p, 32); - mult(xznbp, 32, sp, 0, up, 0); - square(xzn1bp, 0, c1p, 0); - mult(xzn1bp, 32, rp, 0, workp, 0); - select(xzm, xzm1, xznb, xzn1b, b); - } - - for (int j = 0; j < 64; ++j) work[j] = xzm[j]; - } - - static void recip(int[] outv, int outvoffset, int[] z, int zoffset) { - int[] z2 = new int[32]; - int[] z9 = new int[32]; - int[] z11 = new int[32]; - int[] z2_5_0 = new int[32]; - int[] z2_10_0 = new int[32]; - int[] z2_20_0 = new int[32]; - int[] z2_50_0 = new int[32]; - int[] z2_100_0 = new int[32]; - int[] t0 = new int[32]; - int[] t1 = new int[32]; - - /* 2 */ - int[] z2p = z2; - square(z2p, 0, z, zoffset); - - /* 4 */ - square(t1, 0, z2, 0); - - /* 8 */ - square(t0, 0, t1, 0); - - /* 9 */ - int[] z9p = z9, t0p = t0; - mult(z9p, 0, t0p, 0, z, zoffset); - - /* 11 */ - mult(z11, 0, z9, 0, z2, 0); - - /* 22 */ - square(t0, 0, z11, 0); - - /* 2^5 - 2^0 = 31 */ - mult(z2_5_0, 0, t0, 0, z9, 0); - - /* 2^6 - 2^1 */ - square(t0, 0, z2_5_0, 0); - - /* 2^7 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^8 - 2^3 */ - square(t0, 0, t1, 0); - - /* 2^9 - 2^4 */ - square(t1, 0, t0, 0); - - /* 2^10 - 2^5 */ - square(t0, 0, t1, 0); - - /* 2^10 - 2^0 */ - mult(z2_10_0, 0, t0, 0, z2_5_0, 0); - - /* 2^11 - 2^1 */ - square(t0, 0, z2_10_0, 0); - - /* 2^12 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^20 - 2^10 */ - for (int i = 2; i < 10; i += 2) { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^20 - 2^0 */ - mult(z2_20_0, 0, t1, 0, z2_10_0, 0); - - /* 2^21 - 2^1 */ - square(t0, 0, z2_20_0, 0); - - /* 2^22 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^40 - 2^20 */ - for (int i = 2; i < 20; i += 2) { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^40 - 2^0 */ - mult(t0, 0, t1, 0, z2_20_0, 0); - - /* 2^41 - 2^1 */ - square(t1, 0, t0, 0); - - /* 2^42 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^50 - 2^10 */ - for (int i = 2; i < 10; i += 2) { - square(t1, 0, t0, 0); - square(t0, 0, t1, 0); - } - - /* 2^50 - 2^0 */ - mult(z2_50_0, 0, t0, 0, z2_10_0, 0); - - /* 2^51 - 2^1 */ - square(t0, 0, z2_50_0, 0); - - /* 2^52 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^100 - 2^50 */ - for (int i = 2; i < 50; i += 2) { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^100 - 2^0 */ - mult(z2_100_0, 0, t1, 0, z2_50_0, 0); - - /* 2^101 - 2^1 */ - square(t1, 0, z2_100_0, 0); - - /* 2^102 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^200 - 2^100 */ - for (int i = 2; i < 100; i += 2) { - square(t1, 0, t0, 0); - square(t0, 0, t1, 0); - } - - /* 2^200 - 2^0 */ - mult(t1, 0, t0, 0, z2_100_0, 0); - - /* 2^201 - 2^1 */ - square(t0, 0, t1, 0); - - /* 2^202 - 2^2 */ - square(t1, 0, t0, 0); - - /* 2^250 - 2^50 */ - for (int i = 2; i < 50; i += 2) { - square(t0, 0, t1, 0); - square(t1, 0, t0, 0); - } - - /* 2^250 - 2^0 */ - mult(t0, 0, t1, 0, z2_50_0, 0); - - /* 2^251 - 2^1 */ - square(t1, 0, t0, 0); - - /* 2^252 - 2^2 */ - square(t0, 0, t1, 0); - - /* 2^253 - 2^3 */ - square(t1, 0, t0, 0); - - /* 2^254 - 2^4 */ - square(t0, 0, t1, 0); - - /* 2^255 - 2^5 */ - square(t1, 0, t0, 0); - - /* 2^255 - 21 */ - int[] t1p = t1, z11p = z11; - mult(outv, outvoffset, t1p, 0, z11p, 0); - } - - public static int crypto_scalarmult(byte[] q, byte[] n, byte[] p) { - int[] work = new int[96]; - byte[] e = new byte[32]; - - for (int i = 0; i < 32; ++i) e[i] = n[i]; - - e[0] &= 248; - e[31] &= 127; - e[31] |= 64; - - for (int i = 0; i < 32; ++i) work[i] = p[i] & 0xFF; - - mainloop(work, e); - - recip(work, 32, work, 32); - mult(work, 64, work, 0, work, 32); - freeze(work, 64); - - for (int i = 0; i < 32; ++i) q[i] = (byte) work[64 + i]; - - return 0; - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java deleted file mode 100644 index 07f8b81..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/curve25519xsalsa20poly1305.java +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class curve25519xsalsa20poly1305 { - public static final int crypto_box_PUBLICKEYBYTES = 32; - public static final int crypto_box_SECRETKEYBYTES = 32; - public static final int crypto_box_BEFORENMBYTES = 32; - public static final int crypto_box_NONCEBYTES = 24; - public static final int crypto_box_ZEROBYTES = 32; - public static final int crypto_box_BOXZEROBYTES = 16; - - public static int crypto_box_getpublickey(byte[] pk, byte[] sk) { - return curve25519.crypto_scalarmult_base(pk, sk); - } - - public static int crypto_box_afternm(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) { - return xsalsa20poly1305.crypto_secretbox(c, m, mlen, n, k); - } - - public static int crypto_box_afternm_nopad( - byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) { - return xsalsa20poly1305.crypto_secretbox_nopad(c, coffset, m, moffset, mlen, n, k); - } - - public static int crypto_box_beforenm(byte[] k, byte[] pk, byte[] sk) { - byte[] s = new byte[32]; - byte[] sp = s, sigmap = xsalsa20.sigma; - - curve25519.crypto_scalarmult(sp, sk, pk); - return hsalsa20.crypto_core(k, null, sp, sigmap); - } - - public static int crypto_box(byte[] c, byte[] m, long mlen, byte[] n, byte[] pk, byte[] sk) { - byte[] k = new byte[crypto_box_BEFORENMBYTES]; - byte[] kp = k; - - crypto_box_beforenm(kp, pk, sk); - return crypto_box_afternm(c, m, mlen, n, kp); - } - - public static int crypto_box_open(byte[] m, byte[] c, long clen, byte[] n, byte[] pk, byte[] sk) { - byte[] k = new byte[crypto_box_BEFORENMBYTES]; - byte[] kp = k; - - crypto_box_beforenm(kp, pk, sk); - return crypto_box_open_afternm(m, c, clen, n, kp); - } - - public static int crypto_box_open_afternm(byte[] m, byte[] c, long clen, byte[] n, byte[] k) { - return xsalsa20poly1305.crypto_secretbox_open(m, c, clen, n, k); - } - - public static int crypto_box_open_afternm_nopad( - byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) { - return xsalsa20poly1305.crypto_secretbox_open_nopad(m, moffset, c, coffset, clen, n, k); - } - - public static int crypto_box_afternm(byte[] c, byte[] m, byte[] n, byte[] k) { - return crypto_box_afternm(c, m, 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, c.length, n, k); - } - - public static int crypto_box(byte[] c, byte[] m, byte[] n, byte[] pk, byte[] sk) { - return crypto_box(c, m, m.length, n, pk, sk); - } - - public static int crypto_box_open(byte[] m, byte[] c, byte[] n, byte[] pk, byte[] sk) { - return crypto_box_open(m, c, c.length, n, pk, sk); - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java deleted file mode 100644 index 20b894b..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/hsalsa20.java +++ /dev/null @@ -1,157 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class hsalsa20 { - static final int ROUNDS = 20; - - static int rotate(int u, int c) { - return (u << c) | (u >>> (32 - c)); - } - - static int load_littleendian(byte[] x, int offset) { - return ((x[offset]) & 0xff) - | ((((x[offset + 1]) & 0xff)) << 8) - | ((((x[offset + 2]) & 0xff)) << 16) - | ((((x[offset + 3]) & 0xff)) << 24); - } - - static void store_littleendian(byte[] x, int offset, int u) { - x[offset] = (byte) u; - u >>>= 8; - x[offset + 1] = (byte) u; - u >>>= 8; - x[offset + 2] = (byte) u; - u >>>= 8; - x[offset + 3] = (byte) u; - } - - public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c) { - int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - int i; - - j0 = x0 = load_littleendian(c, 0); - j1 = x1 = load_littleendian(k, 0); - j2 = x2 = load_littleendian(k, 4); - j3 = x3 = load_littleendian(k, 8); - j4 = x4 = load_littleendian(k, 12); - j5 = x5 = load_littleendian(c, 4); - - if (inv != null) { - j6 = x6 = load_littleendian(inv, 0); - j7 = x7 = load_littleendian(inv, 4); - j8 = x8 = load_littleendian(inv, 8); - j9 = x9 = load_littleendian(inv, 12); - } else { - j6 = x6 = j7 = x7 = j8 = x8 = j9 = x9 = 0; - } - - j10 = x10 = load_littleendian(c, 8); - j11 = x11 = load_littleendian(k, 16); - j12 = x12 = load_littleendian(k, 20); - j13 = x13 = load_littleendian(k, 24); - j14 = x14 = load_littleendian(k, 28); - j15 = x15 = load_littleendian(c, 12); - - for (i = ROUNDS; i > 0; i -= 2) { - x4 ^= rotate(x0 + x12, 7); - x8 ^= rotate(x4 + x0, 9); - x12 ^= rotate(x8 + x4, 13); - x0 ^= rotate(x12 + x8, 18); - x9 ^= rotate(x5 + x1, 7); - x13 ^= rotate(x9 + x5, 9); - x1 ^= rotate(x13 + x9, 13); - x5 ^= rotate(x1 + x13, 18); - x14 ^= rotate(x10 + x6, 7); - x2 ^= rotate(x14 + x10, 9); - x6 ^= rotate(x2 + x14, 13); - x10 ^= rotate(x6 + x2, 18); - x3 ^= rotate(x15 + x11, 7); - x7 ^= rotate(x3 + x15, 9); - x11 ^= rotate(x7 + x3, 13); - x15 ^= rotate(x11 + x7, 18); - x1 ^= rotate(x0 + x3, 7); - x2 ^= rotate(x1 + x0, 9); - x3 ^= rotate(x2 + x1, 13); - x0 ^= rotate(x3 + x2, 18); - x6 ^= rotate(x5 + x4, 7); - x7 ^= rotate(x6 + x5, 9); - x4 ^= rotate(x7 + x6, 13); - x5 ^= rotate(x4 + x7, 18); - x11 ^= rotate(x10 + x9, 7); - x8 ^= rotate(x11 + x10, 9); - x9 ^= rotate(x8 + x11, 13); - x10 ^= rotate(x9 + x8, 18); - x12 ^= rotate(x15 + x14, 7); - x13 ^= rotate(x12 + x15, 9); - x14 ^= rotate(x13 + x12, 13); - x15 ^= rotate(x14 + x13, 18); - } - - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; - x4 += j4; - x5 += j5; - x6 += j6; - x7 += j7; - x8 += j8; - x9 += j9; - x10 += j10; - x11 += j11; - x12 += j12; - x13 += j13; - x14 += j14; - x15 += j15; - - x0 -= load_littleendian(c, 0); - x5 -= load_littleendian(c, 4); - x10 -= load_littleendian(c, 8); - x15 -= load_littleendian(c, 12); - - if (inv != null) { - x6 -= load_littleendian(inv, 0); - x7 -= load_littleendian(inv, 4); - x8 -= load_littleendian(inv, 8); - x9 -= load_littleendian(inv, 12); - } - - store_littleendian(outv, 0, x0); - store_littleendian(outv, 4, x5); - store_littleendian(outv, 8, x10); - store_littleendian(outv, 12, x15); - store_littleendian(outv, 16, x6); - store_littleendian(outv, 20, x7); - store_littleendian(outv, 24, x8); - store_littleendian(outv, 28, x9); - - return 0; - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java deleted file mode 100644 index a9cffee..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/poly1305.java +++ /dev/null @@ -1,158 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class poly1305 { - final int CRYPTO_BYTES = 16; - final int CRYPTO_KEYBYTES = 32; - - static final int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252}; - - public static int crypto_onetimeauth_verify( - byte[] h, int hoffset, byte[] inv, int invoffset, long inlen, byte[] k) { - byte[] correct = new byte[16]; - - crypto_onetimeauth(correct, 0, inv, invoffset, inlen, k); - return verify_16.crypto_verify(h, hoffset, correct); - } - - static void add(int[] h, int[] c) { - int j; - int u = 0; - - for (j = 0; j < 17; ++j) { - u += h[j] + c[j]; - h[j] = u & 255; - u >>>= 8; - } - } - - static void squeeze(int[] h) { - int u = 0; - - for (int j = 0; j < 16; ++j) { - u += h[j]; - h[j] = u & 255; - u >>>= 8; - } - - u += h[16]; - h[16] = u & 3; - u = 5 * (u >>> 2); - - for (int j = 0; j < 16; ++j) { - u += h[j]; - h[j] = u & 255; - u >>>= 8; - } - - u += h[16]; - h[16] = u; - } - - static void freeze(int[] h) { - int[] horig = new int[17]; - - for (int j = 0; j < 17; ++j) horig[j] = h[j]; - - add(h, minusp); - - int negative = (-(h[16] >>> 7)); - - for (int j = 0; j < 17; ++j) h[j] ^= negative & (horig[j] ^ h[j]); - } - - static void mulmod(int[] h, int[] r) { - int[] hr = new int[17]; - - for (int i = 0; i < 17; ++i) { - int u = 0; - - for (int j = 0; j <= i; ++j) u += h[j] * r[i - j]; - - for (int j = i + 1; j < 17; ++j) u += 320 * h[j] * r[i + 17 - j]; - - hr[i] = u; - } - - for (int i = 0; i < 17; ++i) h[i] = hr[i]; - - squeeze(h); - } - - public static int crypto_onetimeauth( - byte[] outv, int outvoffset, byte[] inv, int invoffset, long inlen, byte[] k) { - int j; - int[] r = new int[17]; - int[] h = new int[17]; - int[] c = new int[17]; - - r[0] = k[0] & 0xFF; - r[1] = k[1] & 0xFF; - r[2] = k[2] & 0xFF; - r[3] = k[3] & 15; - r[4] = k[4] & 252; - r[5] = k[5] & 0xFF; - r[6] = k[6] & 0xFF; - r[7] = k[7] & 15; - r[8] = k[8] & 252; - r[9] = k[9] & 0xFF; - r[10] = k[10] & 0xFF; - r[11] = k[11] & 15; - r[12] = k[12] & 252; - r[13] = k[13] & 0xFF; - r[14] = k[14] & 0xFF; - r[15] = k[15] & 15; - r[16] = 0; - - for (j = 0; j < 17; ++j) h[j] = 0; - - while (inlen > 0) { - for (j = 0; j < 17; ++j) c[j] = 0; - - for (j = 0; (j < 16) && (j < inlen); ++j) c[j] = inv[invoffset + j] & 0xff; - - c[j] = 1; - invoffset += j; - inlen -= j; - add(h, c); - mulmod(h, r); - } - - freeze(h); - - for (j = 0; j < 16; ++j) c[j] = k[j + 16] & 0xFF; - - c[16] = 0; - add(h, c); - - for (j = 0; j < 16; ++j) outv[j + outvoffset] = (byte) h[j]; - - return 0; - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java deleted file mode 100644 index 118d895..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/salsa20.java +++ /dev/null @@ -1,303 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class salsa20 { - final int crypto_core_salsa20_ref_OUTPUTBYTES = 64; - final int crypto_core_salsa20_ref_INPUTBYTES = 16; - final int crypto_core_salsa20_ref_KEYBYTES = 32; - final int crypto_core_salsa20_ref_CONSTBYTES = 16; - final int crypto_stream_salsa20_ref_KEYBYTES = 32; - final int crypto_stream_salsa20_ref_NONCEBYTES = 8; - - static final int ROUNDS = 20; - - static long rotate(int u, int c) { - return (u << c) | (u >>> (32 - c)); - } - - static int load_littleendian(byte[] x, int offset) { - return ((x[offset]) & 0xff) - | ((((x[offset + 1]) & 0xff)) << 8) - | ((((x[offset + 2]) & 0xff)) << 16) - | ((((x[offset + 3]) & 0xff)) << 24); - } - - static void store_littleendian(byte[] x, int offset, int u) { - x[offset] = (byte) u; - u >>>= 8; - x[offset + 1] = (byte) u; - u >>>= 8; - x[offset + 2] = (byte) u; - u >>>= 8; - x[offset + 3] = (byte) u; - } - - public static int crypto_core(byte[] outv, byte[] inv, byte[] k, byte[] c) { - int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - int i; - - j0 = x0 = load_littleendian(c, 0); - j1 = x1 = load_littleendian(k, 0); - j2 = x2 = load_littleendian(k, 4); - j3 = x3 = load_littleendian(k, 8); - j4 = x4 = load_littleendian(k, 12); - j5 = x5 = load_littleendian(c, 4); - j6 = x6 = load_littleendian(inv, 0); - j7 = x7 = load_littleendian(inv, 4); - j8 = x8 = load_littleendian(inv, 8); - j9 = x9 = load_littleendian(inv, 12); - j10 = x10 = load_littleendian(c, 8); - j11 = x11 = load_littleendian(k, 16); - j12 = x12 = load_littleendian(k, 20); - j13 = x13 = load_littleendian(k, 24); - j14 = x14 = load_littleendian(k, 28); - j15 = x15 = load_littleendian(c, 12); - - for (i = ROUNDS; i > 0; i -= 2) { - x4 ^= rotate(x0 + x12, 7); - x8 ^= rotate(x4 + x0, 9); - x12 ^= rotate(x8 + x4, 13); - x0 ^= rotate(x12 + x8, 18); - x9 ^= rotate(x5 + x1, 7); - x13 ^= rotate(x9 + x5, 9); - x1 ^= rotate(x13 + x9, 13); - x5 ^= rotate(x1 + x13, 18); - x14 ^= rotate(x10 + x6, 7); - x2 ^= rotate(x14 + x10, 9); - x6 ^= rotate(x2 + x14, 13); - x10 ^= rotate(x6 + x2, 18); - x3 ^= rotate(x15 + x11, 7); - x7 ^= rotate(x3 + x15, 9); - x11 ^= rotate(x7 + x3, 13); - x15 ^= rotate(x11 + x7, 18); - x1 ^= rotate(x0 + x3, 7); - x2 ^= rotate(x1 + x0, 9); - x3 ^= rotate(x2 + x1, 13); - x0 ^= rotate(x3 + x2, 18); - x6 ^= rotate(x5 + x4, 7); - x7 ^= rotate(x6 + x5, 9); - x4 ^= rotate(x7 + x6, 13); - x5 ^= rotate(x4 + x7, 18); - x11 ^= rotate(x10 + x9, 7); - x8 ^= rotate(x11 + x10, 9); - x9 ^= rotate(x8 + x11, 13); - x10 ^= rotate(x9 + x8, 18); - x12 ^= rotate(x15 + x14, 7); - x13 ^= rotate(x12 + x15, 9); - x14 ^= rotate(x13 + x12, 13); - x15 ^= rotate(x14 + x13, 18); - } - - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; - x4 += j4; - x5 += j5; - x6 += j6; - x7 += j7; - x8 += j8; - x9 += j9; - x10 += j10; - x11 += j11; - x12 += j12; - x13 += j13; - x14 += j14; - x15 += j15; - - store_littleendian(outv, 0, x0); - store_littleendian(outv, 4, x1); - store_littleendian(outv, 8, x2); - store_littleendian(outv, 12, x3); - store_littleendian(outv, 16, x4); - store_littleendian(outv, 20, x5); - store_littleendian(outv, 24, x6); - store_littleendian(outv, 28, x7); - store_littleendian(outv, 32, x8); - store_littleendian(outv, 36, x9); - store_littleendian(outv, 40, x10); - store_littleendian(outv, 44, x11); - store_littleendian(outv, 48, x12); - store_littleendian(outv, 52, x13); - store_littleendian(outv, 56, x14); - store_littleendian(outv, 60, x15); - - return 0; - } - - public static int crypto_stream(byte[] c, int clen, byte[] n, int noffset, byte[] k) { - byte[] inv = new byte[16]; - byte[] block = new byte[64]; - - int coffset = 0; - - if (clen == 0) return 0; - - for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) inv[i] = 0; - - while (clen >= 64) { - salsa20.crypto_core(c, inv, k, xsalsa20.sigma); - - int u = 1; - - for (int i = 8; i < 16; ++i) { - u += inv[i] & 0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - clen -= 64; - coffset += 64; - } - - if (clen != 0) { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < clen; ++i) c[coffset + i] = block[i]; - } - - return 0; - } - - public static int crypto_stream_xor( - byte[] c, byte[] m, int mlen, byte[] n, int noffset, byte[] k) { - byte[] inv = new byte[16]; - byte[] block = new byte[64]; - - int coffset = 0; - int moffset = 0; - - if (mlen == 0) return 0; - - for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) inv[i] = 0; - - while (mlen >= 64) { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < 64; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ block[i]); - - int u = 1; - - for (int i = 8; i < 16; ++i) { - u += inv[i] & 0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - mlen -= 64; - coffset += 64; - moffset += 64; - } - - if (mlen != 0) { - salsa20.crypto_core(block, inv, k, xsalsa20.sigma); - - for (int i = 0; i < mlen; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ block[i]); - } - - return 0; - } - - public static int crypto_stream_xor_skip32( - byte[] c0, - byte[] c, - int coffset, - byte[] m, - int moffset, - int mlen, - byte[] n, - int noffset, - byte[] k) { - /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */ - - int u; - byte[] inv = new byte[16]; - byte[] prevblock = new byte[64]; - byte[] curblock = new byte[64]; - - if (mlen == 0) return 0; - - for (int i = 0; i < 8; ++i) inv[i] = n[noffset + i]; - - for (int i = 8; i < 16; ++i) inv[i] = 0; - - /* calculate first block */ - salsa20.crypto_core(prevblock, inv, k, xsalsa20.sigma); - - /* extract first 32 bytes of cipherstream into c0 */ - if (c0 != null) System.arraycopy(prevblock, 0, c0, 0, 32); - - while (mlen >= 64) { - u = 1; - for (int i = 8; i < 16; ++i) { - u += inv[i] & 0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); - - for (int i = 0; i < 32; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ prevblock[i + 32]); - - for (int i = 32; i < 64; ++i) c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]); - - mlen -= 64; - coffset += 64; - moffset += 64; - - byte[] tmpblock = prevblock; - prevblock = curblock; - curblock = tmpblock; - } - - if (mlen != 0) { - u = 1; - for (int i = 8; i < 16; ++i) { - u += inv[i] & 0xff; - inv[i] = (byte) u; - u >>>= 8; - } - - salsa20.crypto_core(curblock, inv, k, xsalsa20.sigma); - - for (int i = 0; i < mlen && i < 32; ++i) - c[coffset + i] = (byte) (m[moffset + i] ^ prevblock[i + 32]); - - for (int i = 32; i < mlen && i < 64; ++i) - c[coffset + i] = (byte) (m[moffset + i] ^ curblock[i - 32]); - } - - return 0; - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java b/api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java deleted file mode 100644 index 7599552..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/verify_16.java +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class verify_16 { - final int crypto_verify_16_ref_BYTES = 16; - - public static int crypto_verify(byte[] x, int xoffset, byte[] y) { - int differentbits = 0; - - for (int i = 0; i < 15; i++) differentbits |= (x[xoffset + i] ^ y[i]) & 0xff; - - return (1 & ((differentbits - 1) >>> 8)) - 1; - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java b/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java deleted file mode 100644 index fd7025f..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20.java +++ /dev/null @@ -1,76 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class xsalsa20 { - final int crypto_stream_xsalsa20_ref_KEYBYTES = 32; - final int crypto_stream_xsalsa20_ref_NONCEBYTES = 24; - - public static final byte[] sigma = { - (byte) 'e', - (byte) 'x', - (byte) 'p', - (byte) 'a', - (byte) 'n', - (byte) 'd', - (byte) ' ', - (byte) '3', - (byte) '2', - (byte) '-', - (byte) 'b', - (byte) 'y', - (byte) 't', - (byte) 'e', - (byte) ' ', - (byte) 'k' - }; - - public static int crypto_stream(byte[] c, int clen, byte[] n, byte[] k) { - byte[] subkey = new byte[32]; - - hsalsa20.crypto_core(subkey, n, k, sigma); - return salsa20.crypto_stream(c, clen, n, 16, subkey); - } - - public static int crypto_stream_xor(byte[] c, byte[] m, long mlen, byte[] n, byte[] k) { - byte[] subkey = new byte[32]; - - hsalsa20.crypto_core(subkey, n, k, sigma); - return salsa20.crypto_stream_xor(c, m, (int) mlen, n, 16, subkey); - } - - public static int crypto_stream_xor_skip32( - byte[] c0, byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) { - /* Variant of crypto_stream_xor that outputs the first 32 bytes of the cipherstream to c0 */ - - byte[] subkey = new byte[32]; - - hsalsa20.crypto_core(subkey, n, k, sigma); - return salsa20.crypto_stream_xor_skip32(c0, c, coffset, m, moffset, (int) mlen, n, 16, subkey); - } -} diff --git a/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java b/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java deleted file mode 100644 index bf84a73..0000000 --- a/api/src/main/java/com/neilalexander/jnacl/crypto/xsalsa20poly1305.java +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) 2011, Neil Alexander T. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -package com.neilalexander.jnacl.crypto; - -public class xsalsa20poly1305 { - final int crypto_secretbox_KEYBYTES = 32; - final int crypto_secretbox_NONCEBYTES = 24; - final int crypto_secretbox_ZEROBYTES = 32; - final int crypto_secretbox_BOXZEROBYTES = 16; - - public static 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; - } - - public static int crypto_secretbox_nopad( - byte[] c, int coffset, byte[] m, int moffset, long mlen, byte[] n, byte[] k) { - /* variant of crypto_secretbox that doesn't require 32 zero bytes before m and doesn't output - * 16 zero bytes before c */ - byte[] c0 = new byte[32]; - - xsalsa20.crypto_stream_xor_skip32(c0, c, coffset + 16, m, moffset, mlen, n, k); - poly1305.crypto_onetimeauth(c, coffset, c, coffset + 16, mlen, c0); - - return 0; - } - - public static 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; - } - - public static int crypto_secretbox_open_nopad( - byte[] m, int moffset, byte[] c, int coffset, long clen, byte[] n, byte[] k) { - /* variant of crypto_secretbox_open that doesn't require 16 zero bytes before c and doesn't output - * 32 zero bytes before m */ - - if (clen < 16) return -1; - - byte[] subkeyp = new byte[32]; - - xsalsa20.crypto_stream(subkeyp, 32, n, k); - - if (poly1305.crypto_onetimeauth_verify(c, coffset, c, coffset + 16, clen - 16, subkeyp) != 0) - return -1; - - xsalsa20.crypto_stream_xor_skip32(null, m, moffset, c, coffset + 16, clen - 16, n, k); - - return 0; - } -} diff --git a/pom.xml b/pom.xml index 2713c34..dbe6c06 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 1.1.5-SNAPSHOT Threema Messaging SDK pom - + UTF-8 ${source.encoding} ${source.encoding} @@ -48,6 +48,11 @@ gson 2.8.6 + + eu.neilalexander + jnacl + 1.0.1 + @@ -71,7 +76,6 @@ - api cli diff --git a/source/pom.xml b/source/pom.xml deleted file mode 100644 index 2786124..0000000 --- a/source/pom.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - 4.0.0 - ch.threema.apitool - msgapi-sdk-java - 1.1.4 - Threema MsgApi SDK - - UTF-8 - ${source.encoding} - ${source.encoding} - - - Threema GmbH, Staldenbachstrasse 11, 8808 Pfäffikon SZ, Schweiz - https://www.threema.ch - - https://gateway.threema.ch/ - - - MIT-License - http://opensource.org/licenses/mit-license.php - repo - - - - - org.junit.jupiter - junit-jupiter-api - 5.9.1 - test - - - org.assertj - assertj-core - 3.23.1 - test - - - commons-io - commons-io - 2.10.0 - - - org.apache.commons - commons-text - 1.10.0 - - - com.google.code.gson - gson - 2.8.9 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - ${source.encoding} - 17 - 17 - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - - true - lib/ - ch.threema.apitool.Console - - - - - - - maven-assembly-plugin - 3.3.0 - - - - ch.threema.apitool.ConsoleMain - - - - jar-with-dependencies - - - - - make-assembly - package - - single - - - - - - - - - github - GitHub lordyavin Apache Maven Packages - https://maven.pkg.github.com/lordyavin/threema-msgapi-sdk-java - - - From f99efb29e8c9f0ba792cb65b2ca53b92c7e962b0 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 9 Nov 2022 23:42:06 +0100 Subject: [PATCH 10/11] Moves CLI exceptions to CLI --- .../threema/cli/console/commands/fields/Field.java | 4 ++-- .../cli}/exceptions/InvalidCommandFieldValueException.java | 2 +- .../threema/cli}/exceptions/NotAllowedException.java | 2 +- .../cli}/exceptions/RequiredCommandFieldMissingException.java | 2 +- .../java/net/klesatschke/threema/cli/helpers/E2EHelper.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename {api/src/main/java/net/klesatschke/threema/api => cli/src/main/java/net/klesatschke/threema/cli}/exceptions/InvalidCommandFieldValueException.java (96%) rename {api/src/main/java/net/klesatschke/threema/api => cli/src/main/java/net/klesatschke/threema/cli}/exceptions/NotAllowedException.java (96%) rename {api/src/main/java/net/klesatschke/threema/api => cli/src/main/java/net/klesatschke/threema/cli}/exceptions/RequiredCommandFieldMissingException.java (96%) diff --git a/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java index cb8b8cd..28f7721 100644 --- a/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/console/commands/fields/Field.java @@ -24,8 +24,8 @@ package net.klesatschke.threema.cli.console.commands.fields; -import net.klesatschke.threema.api.exceptions.InvalidCommandFieldValueException; -import net.klesatschke.threema.api.exceptions.RequiredCommandFieldMissingException; +import net.klesatschke.threema.cli.exceptions.InvalidCommandFieldValueException; +import net.klesatschke.threema.cli.exceptions.RequiredCommandFieldMissingException; public abstract class Field { diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/InvalidCommandFieldValueException.java similarity index 96% rename from api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java rename to cli/src/main/java/net/klesatschke/threema/cli/exceptions/InvalidCommandFieldValueException.java index ad7fe86..c782ea1 100644 --- a/api/src/main/java/net/klesatschke/threema/api/exceptions/InvalidCommandFieldValueException.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/InvalidCommandFieldValueException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package net.klesatschke.threema.api.exceptions; +package net.klesatschke.threema.cli.exceptions; /** Exception that gets thrown on a illegal call. */ public class InvalidCommandFieldValueException extends Exception { diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/NotAllowedException.java similarity index 96% rename from api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java rename to cli/src/main/java/net/klesatschke/threema/cli/exceptions/NotAllowedException.java index f5ac688..fae126c 100644 --- a/api/src/main/java/net/klesatschke/threema/api/exceptions/NotAllowedException.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/NotAllowedException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package net.klesatschke.threema.api.exceptions; +package net.klesatschke.threema.cli.exceptions; /** Exception that gets thrown on a illegal call. */ public class NotAllowedException extends Exception { diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/RequiredCommandFieldMissingException.java similarity index 96% rename from api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java rename to cli/src/main/java/net/klesatschke/threema/cli/exceptions/RequiredCommandFieldMissingException.java index 2dcf97d..094e634 100644 --- a/api/src/main/java/net/klesatschke/threema/api/exceptions/RequiredCommandFieldMissingException.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/exceptions/RequiredCommandFieldMissingException.java @@ -22,7 +22,7 @@ * THE SOFTWARE */ -package net.klesatschke.threema.api.exceptions; +package net.klesatschke.threema.cli.exceptions; /** Exception that gets thrown on a illegal call. */ public class RequiredCommandFieldMissingException extends Exception { diff --git a/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java index 7a7e9c2..00d8ed4 100644 --- a/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java +++ b/cli/src/main/java/net/klesatschke/threema/cli/helpers/E2EHelper.java @@ -42,11 +42,11 @@ import net.klesatschke.threema.api.CryptTool; import net.klesatschke.threema.api.exceptions.InvalidKeyException; import net.klesatschke.threema.api.exceptions.MessageParseException; -import net.klesatschke.threema.api.exceptions.NotAllowedException; import net.klesatschke.threema.api.messages.FileMessage; import net.klesatschke.threema.api.messages.ImageMessage; import net.klesatschke.threema.api.messages.ThreemaMessage; import net.klesatschke.threema.api.results.UploadResult; +import net.klesatschke.threema.cli.exceptions.NotAllowedException; /** Helper to handle Threema end-to-end encryption. */ public class E2EHelper { From 936ff14b61790ebb6f99afde67741fc7fbb7d9e2 Mon Sep 17 00:00:00 2001 From: Kai Klesatschke Date: Wed, 9 Nov 2022 23:44:55 +0100 Subject: [PATCH 11/11] Adds integration test for APIConnecector --- api/.gitignore | 1 + api/pom.xml | 24 +++++ .../klesatschke/threema/api/APIConnector.java | 84 ++++++++++------- .../threema/api/exceptions/ClientError.java | 11 +++ .../threema/api/exceptions/ServerError.java | 16 ++++ .../threema/api/exceptions/ThreemaError.java | 18 ++++ .../threema/api/APIConnectorTest.java | 90 +++++++++++++++++++ pom.xml | 29 ++++++ 8 files changed, 243 insertions(+), 30 deletions(-) create mode 100644 api/.gitignore create mode 100644 api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java create mode 100644 api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java create mode 100644 api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java create mode 100644 api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..1c3509c --- /dev/null +++ b/api/.gitignore @@ -0,0 +1 @@ +mockserver_keystore* diff --git a/api/pom.xml b/api/pom.xml index e6bd726..0608443 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,11 +21,31 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-params + test + org.assertj assertj-core test + + org.mock-server + mockserver-junit-jupiter-no-dependencies + test + + + org.mock-server + mockserver-client-java-no-dependencies + test + + + org.mockito + mockito-junit-jupiter + test + commons-io commons-io @@ -47,6 +67,10 @@ eu.neilalexander jnacl + + org.apache.logging.log4j + log4j-core + diff --git a/api/src/main/java/net/klesatschke/threema/api/APIConnector.java b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java index df9b83c..d1c2e42 100644 --- a/api/src/main/java/net/klesatschke/threema/api/APIConnector.java +++ b/api/src/main/java/net/klesatschke/threema/api/APIConnector.java @@ -32,19 +32,31 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.net.URL; import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Map; +import java.util.Optional; import javax.net.ssl.HttpsURLConnection; +import lombok.extern.log4j.Log4j2; +import net.klesatschke.threema.api.exceptions.ClientError; +import net.klesatschke.threema.api.exceptions.ServerError; +import net.klesatschke.threema.api.exceptions.ThreemaError; import net.klesatschke.threema.api.results.CapabilityResult; import net.klesatschke.threema.api.results.EncryptResult; import net.klesatschke.threema.api.results.UploadResult; /** Facilitates HTTPS communication with the Threema Message API. */ +@Log4j2 public class APIConnector { private static final int BUFFER_SIZE = 16384; @@ -72,6 +84,7 @@ public InputStreamLength(InputStream inputStream, int length) { private final PublicKeyStore publicKeyStore; private final String apiIdentity; private final String secret; + private HttpClient httpClient; public APIConnector(String apiIdentity, String secret, PublicKeyStore publicKeyStore) { this(apiIdentity, secret, "https://msgapi.threema.ch/", publicKeyStore); @@ -83,6 +96,7 @@ public APIConnector( this.secret = secret; this.apiUrl = apiUrl; this.publicKeyStore = publicKeyStore; + httpClient = HttpClient.newBuilder().followRedirects(Redirect.NEVER).build(); } /** @@ -129,17 +143,27 @@ public String sendE2EMessage(String to, byte[] nonce, byte[] box) throws IOExcep * @throws IOException if a communication or server error occurs */ public String lookupPhone(String phoneNumber) throws IOException { + var getParams = makeRequestParams(); + var phoneHash = CryptTool.hashPhoneNo(phoneNumber); try { - var getParams = makeRequestParams(); - - var phoneHash = CryptTool.hashPhoneNo(phoneNumber); - return doGet( - new URL(this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)), + URI.create( + this.apiUrl + "lookup/phone_hash/" + DataUtils.byteArrayToHexString(phoneHash)), getParams); - } catch (FileNotFoundException e) { - return null; + } catch (ThreemaError e) { + switch (e.getResponse().statusCode()) { + case 400: + throw new ClientError(e.getResponse(), "the hash length is wrong"); + case 401: + throw new ClientError(e.getResponse(), "API identity or secret are incorrect"); + case 404: + throw new ClientError(e.getResponse(), "no matching ID could be found"); + case 500: + throw new ServerError(e.getResponse(), "a temporary internal server error occurs"); + default: + throw e; + } } } @@ -159,7 +183,8 @@ public String lookupEmail(String email) throws IOException { var emailHash = CryptTool.hashEmail(email); return doGet( - new URL(this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)), + URI.create( + this.apiUrl + "lookup/email_hash/" + DataUtils.byteArrayToHexString(emailHash)), getParams); } catch (FileNotFoundException e) { return null; @@ -178,7 +203,7 @@ public byte[] lookupKey(String id) throws IOException { if (key == null) { try { var getParams = makeRequestParams(); - var pubkeyHex = doGet(new URL(this.apiUrl + "pubkeys/" + id), getParams); + var pubkeyHex = doGet(URI.create(this.apiUrl + "pubkeys/" + id), getParams); key = DataUtils.hexStringToByteArray(pubkeyHex); if (key != null) { @@ -199,7 +224,7 @@ public byte[] lookupKey(String id) throws IOException { * @throws IOException */ public CapabilityResult lookupKeyCapability(String threemaId) throws IOException { - var res = doGet(new URL(this.apiUrl + "capabilities/" + threemaId), makeRequestParams()); + var res = doGet(URI.create(this.apiUrl + "capabilities/" + threemaId), makeRequestParams()); if (res != null) { return new CapabilityResult(threemaId, res.split(",")); } @@ -207,7 +232,7 @@ public CapabilityResult lookupKeyCapability(String threemaId) throws IOException } public Integer lookupCredits() throws IOException { - var res = doGet(new URL(this.apiUrl + "credits"), makeRequestParams()); + var res = doGet(URI.create(this.apiUrl + "credits"), makeRequestParams()); if (res != null) { return Integer.valueOf(res); } @@ -358,29 +383,28 @@ private Map makeRequestParams() { return Map.of("from", apiIdentity, "secret", secret); } - private String doGet(URL url, Map getParams) throws IOException { + private String doGet(URI uri, Map getParams) throws IOException { + var uriWithParameters = + Optional.ofNullable(getParams) + .map(params -> URI.create(uri.toString() + "?" + makeUrlEncoded(params))) + .orElse(uri); - if (getParams != null) { - var queryString = makeUrlEncoded(getParams); - - url = new URL(url.toString() + "?" + queryString); - } - - var connection = (HttpsURLConnection) url.openConnection(); - connection.setDoOutput(false); - connection.setDoInput(true); - connection.setInstanceFollowRedirects(false); - connection.setRequestMethod("GET"); - connection.setUseCaches(false); + var httpRequest = HttpRequest.newBuilder().uri(uriWithParameters).GET().build(); + try { + HttpResponse response = httpClient.send(httpRequest, BodyHandlers.ofString()); - var is = connection.getInputStream(); - var br = new BufferedReader(new InputStreamReader(is)); - var response = br.readLine(); - br.close(); + var statusCode = response.statusCode(); + if (400 <= statusCode) { + throw new ThreemaError(response); + } - connection.disconnect(); + return response.body(); - return response; + } catch (InterruptedException e) { + log.warn("Interrupted"); + Thread.currentThread().interrupt(); + } + return null; } private String doPost(URL url, Map postParams) throws IOException { diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java new file mode 100644 index 0000000..a2c1bd5 --- /dev/null +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ClientError.java @@ -0,0 +1,11 @@ +package net.klesatschke.threema.api.exceptions; + +import java.net.http.HttpResponse; + +public class ClientError extends ThreemaError { + private static final long serialVersionUID = 5586830942850922017L; + + public ClientError(HttpResponse response, String string) { + super(response, string); + } +} diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java new file mode 100644 index 0000000..f78abd8 --- /dev/null +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ServerError.java @@ -0,0 +1,16 @@ +package net.klesatschke.threema.api.exceptions; + +import java.net.http.HttpResponse; + +public class ServerError extends ThreemaError { + + private static final long serialVersionUID = 1306889474607193102L; + + public ServerError(HttpResponse response) { + super(response); + } + + public ServerError(HttpResponse response, String string) { + super(response, string); + } +} diff --git a/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java b/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java new file mode 100644 index 0000000..f621f6d --- /dev/null +++ b/api/src/main/java/net/klesatschke/threema/api/exceptions/ThreemaError.java @@ -0,0 +1,18 @@ +package net.klesatschke.threema.api.exceptions; + +import java.io.IOException; +import java.net.http.HttpResponse; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class ThreemaError extends IOException { + private static final long serialVersionUID = -5089520812601719876L; + @Getter private final HttpResponse response; + + public ThreemaError(HttpResponse response, String string) { + super(string); + this.response = response; + } +} diff --git a/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java b/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java new file mode 100644 index 0000000..a4a2d4b --- /dev/null +++ b/api/src/test/java/net/klesatschke/threema/api/APIConnectorTest.java @@ -0,0 +1,90 @@ +package net.klesatschke.threema.api; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIOException; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.io.IOException; + +import javax.net.ssl.HttpsURLConnection; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.junit.jupiter.MockServerExtension; +import org.mockserver.junit.jupiter.MockServerSettings; +import org.mockserver.logging.MockServerLogger; +import org.mockserver.socket.PortFactory; +import org.mockserver.socket.tls.KeyStoreFactory; + +@ExtendWith(MockServerExtension.class) +@ExtendWith(MockitoExtension.class) +@MockServerSettings(ports = {8787, 8888}) +class APIConnectorTest { + private static ClientAndServer client; + + @Mock PublicKeyStore keystore; + + private APIConnector apiConnector; + + @BeforeAll + static void startMockServer() { + // ensure all connection using HTTPS will use the SSL context defined by + // MockServer to allow dynamically generated certificates to be accepted + HttpsURLConnection.setDefaultSSLSocketFactory( + new KeyStoreFactory(new MockServerLogger()).sslContext().getSocketFactory()); + client = ClientAndServer.startClientAndServer(PortFactory.findFreePort()); + } + + @BeforeEach + void setup() { + client.reset(); + apiConnector = + new APIConnector("ID", "secret", "https://localhost:" + client.getPort() + "/", keystore); + } + + @Test + void testLookupEmail() throws IOException { + // GIVEN + var email = "test@mail.box"; + var hash = DataUtils.byteArrayToHexString(CryptTool.hashEmail(email)); + var path = "/lookup/email_hash/" + hash; + var threemaID = "the ID"; + client.when(request().withMethod("GET").withPath(path)).respond(response().withBody(threemaID)); + + assertThat(apiConnector.lookupEmail(email)).isEqualTo(threemaID); + } + + @Test + void testLookupPhone() throws IOException { + // GIVEN + var number = "+49172989127128"; + var hash = DataUtils.byteArrayToHexString(CryptTool.hashPhoneNo(number)); + var path = "/lookup/phone_hash/" + hash; + var threemaID = "the ID"; + client.when(request().withMethod("GET").withPath(path)).respond(response().withBody(threemaID)); + + assertThat(apiConnector.lookupPhone(number)).isEqualTo(threemaID); + } + + @ParameterizedTest + @ValueSource(ints = {400, 401, 404, 500}) + void testLookupPhoneErrors(int httpStatusCode) throws IOException { + // GIVEN + var number = "+49172989127128"; + var hash = DataUtils.byteArrayToHexString(CryptTool.hashPhoneNo(number)); + var path = "/lookup/phone_hash/" + hash; + client + .when(request().withMethod("GET").withPath(path)) + .respond(response().withStatusCode(httpStatusCode)); + + assertThatIOException().isThrownBy(() -> apiConnector.lookupPhone(number)); + } +} diff --git a/pom.xml b/pom.xml index dbe6c06..8aa1797 100644 --- a/pom.xml +++ b/pom.xml @@ -22,12 +22,36 @@ 5.9.1 test + + org.junit.jupiter + junit-jupiter-params + 5.9.1 + test + org.assertj assertj-core 3.23.1 test + + org.mockito + mockito-junit-jupiter + 4.8.1 + test + + + org.mock-server + mockserver-junit-jupiter-no-dependencies + 5.14.0 + test + + + org.mock-server + mockserver-client-java-no-dependencies + 5.14.0 + test + org.projectlombok lombok @@ -53,6 +77,11 @@ jnacl 1.0.1 + + org.apache.logging.log4j + log4j-core + 2.19.0 +