From d6aec07ca3914c2539c63e75bf0b9a1499beefe7 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 14 Feb 2024 17:09:36 +0500 Subject: [PATCH 01/21] use bouncycastle in cryptosign auth --- .../autobahn/wamp/auth/CryptosignAuth.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java index 9b471cac..b5a46b04 100644 --- a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java +++ b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java @@ -1,10 +1,14 @@ package io.crossbar.autobahn.wamp.auth; -import org.libsodium.jni.crypto.Random; -import org.libsodium.jni.keys.SigningKey; -import org.libsodium.jni.keys.VerifyKey; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; +import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.crypto.signers.Ed25519Signer; import java.io.File; +import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -16,7 +20,6 @@ import io.crossbar.autobahn.wamp.types.Challenge; import io.crossbar.autobahn.wamp.types.ChallengeResponse; -import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES; public class CryptosignAuth implements IAuthenticator { public static final String authmethod = "cryptosign"; @@ -28,13 +31,18 @@ public class CryptosignAuth implements IAuthenticator { private final byte[] privateKeyRaw; public static Pair generateSigningKeyPair() { - VerifyKey key = new VerifyKey(new Random().randomBytes(SECRETKEY_BYTES)); - SigningKey signingKey = new SigningKey(key.toBytes()); + Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator(); + keyPairGenerator.init(new Ed25519KeyGenerationParameters(new SecureRandom())); - String privateKey = AuthUtil.toHexString(key.toBytes()); - String publicKey = AuthUtil.toHexString(signingKey.getVerifyKey().toBytes()); + AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); - return new Pair<>(publicKey, privateKey); + Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) keyPair.getPrivate(); + Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) keyPair.getPublic(); + + String privateKeyHex = AuthUtil.toHexString(privateKey.getEncoded()); + String publicKeyHex = AuthUtil.toHexString(publicKey.getEncoded()); + + return new Pair<>(publicKeyHex, privateKeyHex); } public CryptosignAuth(String authid, String privateKey) { @@ -42,8 +50,10 @@ public CryptosignAuth(String authid, String privateKey) { } public static String getPublicKey(byte[] privateKeyRaw) { - SigningKey signingKey = new SigningKey(privateKeyRaw); - return AuthUtil.toHexString(signingKey.getVerifyKey().toBytes()); + Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(privateKeyRaw, 0); + + byte[] publicKeyBytes = privateKey.generatePublicKey().getEncoded(); + return AuthUtil.toHexString(publicKeyBytes); } public CryptosignAuth(String authid, String privkey, Map authextra) { @@ -85,8 +95,11 @@ public CompletableFuture onChallenge(Session session, Challen String hexChallenge = (String) challenge.extra.get("challenge"); byte[] rawChallenge = AuthUtil.toBinary(hexChallenge); - SigningKey key = new SigningKey(privateKeyRaw); - byte[] signed = key.sign(rawChallenge); + Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(privateKeyRaw, 0); + Ed25519Signer signer = new Ed25519Signer(); + signer.init(true, privateKey); + signer.update(rawChallenge, 0, rawChallenge.length); + byte[] signed =signer.generateSignature(); String signatureHex = AuthUtil.toHexString(signed); String res = signatureHex + hexChallenge; From 926204208e88eba00152d4239dc4194402c7e4fc Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 14 Feb 2024 17:37:13 +0500 Subject: [PATCH 02/21] add missing space --- .../java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java index b5a46b04..d9ea680c 100644 --- a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java +++ b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java @@ -99,7 +99,7 @@ public CompletableFuture onChallenge(Session session, Challen Ed25519Signer signer = new Ed25519Signer(); signer.init(true, privateKey); signer.update(rawChallenge, 0, rawChallenge.length); - byte[] signed =signer.generateSignature(); + byte[] signed = signer.generateSignature(); String signatureHex = AuthUtil.toHexString(signed); String res = signatureHex + hexChallenge; From 5b1b7133323eb7888a697c7b1ba1ea6e509e571b Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 19 Feb 2024 15:41:59 +0500 Subject: [PATCH 03/21] add unit tests for CryptosignAuth --- autobahn/build.gradle | 1 + .../wamp/auth/CryptosignAuthTests.java | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java diff --git a/autobahn/build.gradle b/autobahn/build.gradle index f84ace65..014e2fd8 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -38,6 +38,7 @@ dependencies { implementation 'io.netty:netty-codec-http:4.1.106.Final' implementation 'io.netty:netty-handler:4.1.106.Final' } + testImplementation 'junit:junit:4.13.2' } // Create the pom configuration: diff --git a/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java b/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java new file mode 100644 index 00000000..bf47c142 --- /dev/null +++ b/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java @@ -0,0 +1,112 @@ +package io.crossbar.autobahn.wamp.auth; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import io.crossbar.autobahn.utils.AuthUtil; +import io.crossbar.autobahn.utils.Pair; +import io.crossbar.autobahn.wamp.types.Challenge; +import io.crossbar.autobahn.wamp.types.ChallengeResponse; + +public class CryptosignAuthTests { + + private static final String TestAuthID = "testAuthId"; + private static final String TestPrivateKey = "61b297d1573d0a2a6ac58d7fd39369adbd365c5b3276bd69edf661c92b7ad9ff"; + private static final String TestPublicKey = "ea971c008ee99021eaf48342791442dd742259a4bf14004fa3500d1fa6995211"; + + @Test + public void testGenerateSigningKeyPair() { + Pair keyPair = CryptosignAuth.generateSigningKeyPair(); + assertNotNull(keyPair.first); + assertNotNull(keyPair.second); + assertEquals(64, keyPair.first.length()); + assertEquals(64, keyPair.second.length()); + } + + @Test + public void testGetPublicKey() { + byte[] privateKeyRaw = AuthUtil.toBinary(TestPrivateKey); + String publicKeyHex = CryptosignAuth.getPublicKey(privateKeyRaw); + assertNotNull(publicKeyHex); + assertEquals(TestPublicKey, publicKeyHex); + } + + @Test + public void testConstructorWithPrivateKey() { + CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey); + assertNotNull(auth); + assertEquals(TestAuthID, auth.authid); + } + + @Test + public void testConstructorWithPrivateKeyAndPubKey() { + CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey, TestPublicKey); + assertNotNull(auth); + assertEquals(TestAuthID, auth.authid); + } + + @Test + public void testConstructorWithPrivateKeyFile() throws IOException { + File privateKeyFile = createPrivateKeyFile(); + CryptosignAuth auth = new CryptosignAuth(TestAuthID, privateKeyFile); + assertNotNull(auth); + assertEquals(TestAuthID, auth.authid); + } + + private File createPrivateKeyFile() throws IOException { + String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" + + "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAgwAAAAtzc2gtZW\n" + + "QyNTUxOQAAACCxFVfOJU6fDhJmG1Hh8qW2nEdqeDbXLkCOoeepu5VxcAAACBAMP3yQT9Mk\n" + + "/YAAAAAtzc2gtZWQyNTUxOQAAACCxFVfOJU6fDhJmG1Hh8qW2nEdqeDbXLkCOoeepu5Vxc\n" + + "AAAEA6Ay3awSTmEnMS7QZi4ofEiChSju2bN8wFFS4tmFrG3sVV84lTp8OEmYbUeHypbacR\n" + + "2p4NtcuQI6h56m7lXFwAAAECf4qghufu/GJScYXWHjVPfX+znr0NQ1p2YVNTZi7mC0rLQ6\n" + + "qSsWfNYu4mSvA1Q6Jv6Xu8HAB/TIllo5bqV9acxSbA5GEE0S8T6rMq0BtZk+jHxUlFMjrr\n" + + "QwEW18H/mFjtsitG8K5XWfBgMb3dF8XsoGYJyKlY4e4p0D7BBjqSKnB7wlUGwkCdTeeUDw==\n" + + "-----END OPENSSH PRIVATE KEY-----\n"; + byte[] privateKeyBytes = privateKey.getBytes(StandardCharsets.UTF_8); + Path tempFilePath = Files.createTempFile("private_key", ".txt"); + Files.write(tempFilePath, privateKeyBytes); + return tempFilePath.toFile(); + } + + @Test + public void testConstructorWithAuthIdPrivateKeyAuthExtra() { + Map authextra = new HashMap<>(); + authextra.put("pubkey", TestPublicKey); + CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey, authextra); + assertNotNull(auth); + assertEquals(TestAuthID, auth.authid); + assertEquals(authextra, auth.authextra); + } + + @Test + public void testOnChallenge() { + String challengeString = "f9d17535fb925e9f674d648cbfc41399"; + String signedString = "539707667d93bb9eb01e72be9ca5e00006bb6b1b786d697b3f189ebf5a0f60c70" + + "b8054f3735e19b77df31dc990864fb21259cfe3021f9a7e8ec0427c2077840a"; + Challenge challenge = new Challenge("cryptosign", new HashMap() {{ + put("challenge", challengeString); + }}); + + CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey); + ChallengeResponse response = auth.onChallenge(null, challenge).join(); + assertNotNull(response.signature); + assertEquals(signedString + challengeString, response.signature); + } + + @Test + public void testGetAuthMethod() { + CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey); + assertEquals("cryptosign", auth.getAuthMethod()); + } +} \ No newline at end of file From 0f1c292ffbd53932a13ea70974eff0cd23fe887f Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Sat, 24 Feb 2024 16:09:37 +0500 Subject: [PATCH 04/21] replace libsodium with bouncy castle in SecretBox --- .../src/main/java/xbr/network/KeySeries.java | 9 +- .../main/java/xbr/network/SimpleBuyer.java | 10 +- .../main/java/xbr/network/SimpleSeller.java | 3 +- autobahn/src/main/java/xbr/network/Util.java | 32 ++++++ .../java/xbr/network/crypto/SecretBox.java | 107 +++++++++++------- .../wamp/auth/CryptosignAuthTests.java | 2 +- .../src/test/java/xbr/network/UtilTest.java | 32 ++++++ .../xbr/network/crypto/SecretBoxTest.java | 71 ++++++++++++ 8 files changed, 213 insertions(+), 53 deletions(-) create mode 100644 autobahn/src/test/java/xbr/network/UtilTest.java create mode 100644 autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java diff --git a/autobahn/src/main/java/xbr/network/KeySeries.java b/autobahn/src/main/java/xbr/network/KeySeries.java index d48d0cc1..118088e8 100644 --- a/autobahn/src/main/java/xbr/network/KeySeries.java +++ b/autobahn/src/main/java/xbr/network/KeySeries.java @@ -15,8 +15,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; -import org.libsodium.jni.SodiumConstants; -import org.libsodium.jni.crypto.Random; import org.web3j.utils.Numeric; import java.math.BigInteger; @@ -94,8 +92,7 @@ byte[] getPrice() { } Map encrypt(Object payload) throws JsonProcessingException { - byte[] nonce = new Random().randomBytes( - SodiumConstants.XSALSA20_POLY1305_SECRETBOX_NONCEBYTES); + byte[] nonce = Util.generateRandomBytesArray(Util.NONCE_SIZE); Map data = new HashMap<>(); data.put("id", mID); @@ -112,8 +109,8 @@ byte[] encryptKey(byte[] keyID, byte[] buyerPubKey) { } private void onRotate() { - mID = new Random().randomBytes(16); - mKey = new Random().randomBytes(SodiumConstants.XSALSA20_POLY1305_SECRETBOX_KEYBYTES); + mID = Util.generateRandomBytesArray(16); + mKey = Util.generateRandomBytesArray(Util.SECRET_KEY_LEN); mBox = new SecretBox(mKey); Map data = new HashMap<>(); diff --git a/autobahn/src/main/java/xbr/network/SimpleBuyer.java b/autobahn/src/main/java/xbr/network/SimpleBuyer.java index 4b22695e..3d45bbd8 100644 --- a/autobahn/src/main/java/xbr/network/SimpleBuyer.java +++ b/autobahn/src/main/java/xbr/network/SimpleBuyer.java @@ -15,7 +15,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; -import org.libsodium.jni.SodiumConstants; import org.web3j.crypto.Credentials; import org.web3j.crypto.ECKeyPair; import org.web3j.utils.Numeric; @@ -29,6 +28,7 @@ import io.crossbar.autobahn.utils.ABLogger; import io.crossbar.autobahn.utils.IABLogger; +import io.crossbar.autobahn.utils.Pair; import io.crossbar.autobahn.wamp.Session; import io.crossbar.autobahn.wamp.exceptions.ApplicationError; import xbr.network.crypto.SealedBox; @@ -36,8 +36,6 @@ import xbr.network.pojo.Quote; import xbr.network.pojo.Receipt; -import static org.libsodium.jni.NaCl.sodium; - public class SimpleBuyer { private static final IABLogger LOGGER = ABLogger.getLogger(SimpleBuyer.class.getName()); @@ -69,9 +67,9 @@ public SimpleBuyer(String marketMakerAddr, String buyerKey, BigInteger maxPrice) mEthPublicKey = mECKey.getPublicKey().toByteArray(); mEthAddr = Numeric.hexStringToByteArray(Credentials.create(mECKey).getAddress()); - mPrivateKey = new byte[SodiumConstants.SECRETKEY_BYTES]; - mPublicKey = new byte[SodiumConstants.PUBLICKEY_BYTES]; - sodium().crypto_box_keypair(mPublicKey, mPrivateKey); + Pair pubPriKeyPair = Util.generateX25519KeyPair(); + mPublicKey = pubPriKeyPair.first; + mPrivateKey = pubPriKeyPair.second; mMaxPrice = maxPrice; mKeys = new HashMap<>(); diff --git a/autobahn/src/main/java/xbr/network/SimpleSeller.java b/autobahn/src/main/java/xbr/network/SimpleSeller.java index 2bac117a..3e5c9ae8 100644 --- a/autobahn/src/main/java/xbr/network/SimpleSeller.java +++ b/autobahn/src/main/java/xbr/network/SimpleSeller.java @@ -14,7 +14,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import org.libsodium.jni.crypto.Random; import org.web3j.crypto.ECKeyPair; import org.web3j.crypto.Keys; import org.web3j.utils.Numeric; @@ -93,7 +92,7 @@ public byte[] getPublicKey() { private void onRotate(KeySeries series) { mKeysMap.put(Numeric.toHexString(series.getID()), series); long validFrom = Math.round(System.nanoTime() - 10 * Math.pow(10, 9)); - byte[] signature = new Random().randomBytes(65); + byte[] signature = Util.generateRandomBytesArray(65); List args = new ArrayList<>(); args.add(series.getID()); diff --git a/autobahn/src/main/java/xbr/network/Util.java b/autobahn/src/main/java/xbr/network/Util.java index b99f98fb..03757ae0 100644 --- a/autobahn/src/main/java/xbr/network/Util.java +++ b/autobahn/src/main/java/xbr/network/Util.java @@ -11,6 +11,11 @@ package xbr.network; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; +import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -22,11 +27,17 @@ import java.io.IOException; import java.math.BigInteger; +import java.security.SecureRandom; import java.util.Arrays; import java.util.concurrent.CompletableFuture; +import io.crossbar.autobahn.utils.Pair; + public class Util { + public static final int NONCE_SIZE = 24; + public static final int SECRET_KEY_LEN = 32; + public static BigInteger toXBR(int xbr) { return BigInteger.valueOf(xbr).multiply(BigInteger.valueOf(10).pow(18)); } @@ -169,4 +180,25 @@ static CompletableFuture recoverEIP712Signer(int chainId, String verifyi return future; } + + public static byte[] generateRandomBytesArray(int size) { + byte[] randomBytes = new byte[size]; + SecureRandom random = new SecureRandom(); + random.nextBytes(randomBytes); + return randomBytes; + } + + public static Pair generateX25519KeyPair() { + SecureRandom random = new SecureRandom(); + X25519KeyGenerationParameters params = new X25519KeyGenerationParameters(random); + X25519KeyPairGenerator generator = new X25519KeyPairGenerator(); + generator.init(params); + + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + + X25519PrivateKeyParameters privateKeyParams = (X25519PrivateKeyParameters) keyPair.getPrivate(); + X25519PublicKeyParameters publicKeyParams = (X25519PublicKeyParameters) keyPair.getPublic(); + + return new Pair<>(publicKeyParams.getEncoded(), privateKeyParams.getEncoded()); + } } diff --git a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java index acdb3045..c2c548b0 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java @@ -1,46 +1,49 @@ package xbr.network.crypto; -import org.libsodium.jni.crypto.Random; -import org.libsodium.jni.crypto.Util; -import org.libsodium.jni.encoders.Encoder; +import static xbr.network.Util.NONCE_SIZE; +import static xbr.network.Util.SECRET_KEY_LEN; +import static xbr.network.Util.generateRandomBytesArray; -import java.util.Arrays; - -import static org.libsodium.jni.NaCl.sodium; -import static org.libsodium.jni.SodiumConstants.BOXZERO_BYTES; -import static org.libsodium.jni.SodiumConstants.XSALSA20_POLY1305_SECRETBOX_KEYBYTES; -import static org.libsodium.jni.SodiumConstants.XSALSA20_POLY1305_SECRETBOX_NONCEBYTES; -import static org.libsodium.jni.SodiumConstants.ZERO_BYTES; -import static org.libsodium.jni.crypto.Util.checkLength; -import static org.libsodium.jni.crypto.Util.isValid; -import static org.libsodium.jni.crypto.Util.removeZeros; +import org.bouncycastle.crypto.engines.XSalsa20Engine; +import org.bouncycastle.crypto.macs.Poly1305; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import java.security.MessageDigest; +import java.util.Arrays; public class SecretBox { - - private byte[] mKey; - private Encoder mEncoder; + private final byte[] mKey; public SecretBox(byte[] key) { - checkLength(key, XSALSA20_POLY1305_SECRETBOX_KEYBYTES); - mEncoder = Encoder.RAW; - mKey = key; + checkLength(key, SECRET_KEY_LEN); + mKey = Arrays.copyOf(key, key.length); } public byte[] encrypt(byte[] message) { - byte[] nonce = new Random().randomBytes(XSALSA20_POLY1305_SECRETBOX_NONCEBYTES); + byte[] nonce = generateRandomBytesArray(NONCE_SIZE); return encrypt(nonce, message); } - public byte[] encrypt(byte[] nonce, byte[] message) { - checkLength(nonce, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES); - byte[] msg = org.libsodium.jni.crypto.Util.prependZeros(ZERO_BYTES, message); - byte[] ct = org.libsodium.jni.crypto.Util.zeros(msg.length); - isValid(sodium().crypto_secretbox_xsalsa20poly1305(ct, msg, msg.length, - nonce, mKey), "Encryption failed"); - byte[] cipherWithoutNonce = removeZeros(BOXZERO_BYTES, ct); + public byte[] encrypt(byte[] nonce, byte[] plaintext) { + checkLength(nonce, NONCE_SIZE); + + XSalsa20Engine xsalsa20 = new XSalsa20Engine(); + Poly1305 poly1305 = new Poly1305(); + + xsalsa20.init(true, new ParametersWithIV(new KeyParameter(mKey), nonce)); + byte[] subKey = new byte[SECRET_KEY_LEN]; + xsalsa20.processBytes(subKey, 0, SECRET_KEY_LEN, subKey, 0); + byte[] cipherWithoutNonce = new byte[plaintext.length + poly1305.getMacSize()]; + xsalsa20.processBytes(plaintext, 0, plaintext.length, cipherWithoutNonce, poly1305.getMacSize()); + + // hash ciphertext and prepend mac to ciphertext + poly1305.init(new KeyParameter(subKey)); + poly1305.update(cipherWithoutNonce, poly1305.getMacSize(), plaintext.length); + poly1305.doFinal(cipherWithoutNonce, 0); + byte[] ciphertext = new byte[cipherWithoutNonce.length + - XSALSA20_POLY1305_SECRETBOX_NONCEBYTES]; + NONCE_SIZE]; System.arraycopy(nonce, 0, ciphertext, 0, nonce.length); System.arraycopy(cipherWithoutNonce, 0, ciphertext, nonce.length, cipherWithoutNonce.length); @@ -48,18 +51,46 @@ public byte[] encrypt(byte[] nonce, byte[] message) { } public byte[] decrypt(byte[] ciphertext) { - byte[] nonce = Arrays.copyOfRange(ciphertext, 0, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES); - byte[] message = Arrays.copyOfRange(ciphertext, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES, + byte[] nonce = Arrays.copyOfRange(ciphertext, 0, NONCE_SIZE); + byte[] message = Arrays.copyOfRange(ciphertext, NONCE_SIZE, ciphertext.length); return decrypt(nonce, message); } - public byte[] decrypt(byte[] nonce, byte[] ciphertext) { - checkLength(nonce, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES); - byte[] ct = org.libsodium.jni.crypto.Util.prependZeros(BOXZERO_BYTES, ciphertext); - byte[] message = Util.zeros(ct.length); - isValid(sodium().crypto_secretbox_xsalsa20poly1305_open(message, ct, - ct.length, nonce, mKey), "Decryption failed. Ciphertext failed verification"); - return removeZeros(ZERO_BYTES, message); + + private byte[] decrypt(byte[] nonce, byte[] ciphertext) { + checkLength(nonce, NONCE_SIZE); + + XSalsa20Engine xsalsa20 = new XSalsa20Engine(); + Poly1305 poly1305 = new Poly1305(); + + xsalsa20.init(false, new ParametersWithIV(new KeyParameter(mKey), nonce)); + byte[] sk = new byte[SECRET_KEY_LEN]; + xsalsa20.processBytes(sk, 0, sk.length, sk, 0); + + // hash ciphertext + poly1305.init(new KeyParameter(sk)); + int len = Math.max(ciphertext.length - poly1305.getMacSize(), 0); + poly1305.update(ciphertext, poly1305.getMacSize(), len); + byte[] calculatedMAC = new byte[poly1305.getMacSize()]; + poly1305.doFinal(calculatedMAC, 0); + + // extract mac + final byte[] presentedMAC = new byte[poly1305.getMacSize()]; + System.arraycopy( + ciphertext, 0, presentedMAC, 0, Math.min(ciphertext.length, poly1305.getMacSize())); + + if (!MessageDigest.isEqual(calculatedMAC, presentedMAC)) { + throw new IllegalArgumentException("Invalid MAC"); + } + + byte[] plaintext = new byte[len]; + xsalsa20.processBytes(ciphertext, poly1305.getMacSize(), plaintext.length, plaintext, 0); + return plaintext; + } + + private void checkLength(byte[] data, int size) { + if (data == null || data.length != size) + throw new IllegalArgumentException("Invalid size: " + data.length); } -} +} \ No newline at end of file diff --git a/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java b/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java index bf47c142..e047d397 100644 --- a/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java +++ b/autobahn/src/test/java/io/crossbar/autobahn/wamp/auth/CryptosignAuthTests.java @@ -109,4 +109,4 @@ public void testGetAuthMethod() { CryptosignAuth auth = new CryptosignAuth(TestAuthID, TestPrivateKey); assertEquals("cryptosign", auth.getAuthMethod()); } -} \ No newline at end of file +} diff --git a/autobahn/src/test/java/xbr/network/UtilTest.java b/autobahn/src/test/java/xbr/network/UtilTest.java new file mode 100644 index 00000000..9ed109be --- /dev/null +++ b/autobahn/src/test/java/xbr/network/UtilTest.java @@ -0,0 +1,32 @@ +package xbr.network; + +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.crossbar.autobahn.utils.Pair; + +public class UtilTest { + + @Test + public void testGenerateRandomBytesArray() { + int size = 32; + byte[] randomBytes = Util.generateRandomBytesArray(size); + + assertNotNull(randomBytes); + assertEquals(size, randomBytes.length); + } + + @Test + public void testGenerateKeyPair() { + Pair keyPair = Util.generateX25519KeyPair(); + + assertNotNull(keyPair); + assertNotNull(keyPair.first); + assertNotNull(keyPair.second); + + assertEquals(32, keyPair.first.length); + assertEquals(32, keyPair.second.length); + } +} \ No newline at end of file diff --git a/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java b/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java new file mode 100644 index 00000000..4b562955 --- /dev/null +++ b/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java @@ -0,0 +1,71 @@ +package xbr.network.crypto; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; +import static xbr.network.Util.NONCE_SIZE; +import static xbr.network.Util.generateRandomBytesArray; + +import org.junit.Test; + +public class SecretBoxTest { + + @Test + public void testConstructor() { + // test with valid key + new SecretBox(new byte[32]); + + // test with invalid key + assertThrows(IllegalArgumentException.class, () -> new SecretBox(new byte[16])); + + // test with null key + assertThrows(NullPointerException.class, () -> new SecretBox(null)); + } + + @Test + public void testEncryptAndDecrypt() { + SecretBox secretBox = new SecretBox(new byte[32]); + byte[] message = "Hello, World!".getBytes(); + byte[] encrypted = secretBox.encrypt(message); + byte[] decrypted = secretBox.decrypt(encrypted); + assertArrayEquals(message, decrypted); + } + + @Test + public void testEncryptAndDecryptWithNonce() { + SecretBox secretBox = new SecretBox(new byte[32]); + byte[] nonce = generateRandomBytesArray(NONCE_SIZE); + byte[] message = "Hello, World!".getBytes(); + byte[] encrypted = secretBox.encrypt(nonce, message); + byte[] decrypted = secretBox.decrypt(encrypted); + assertArrayEquals(message, decrypted); + } + + @Test + public void testEncryptAndDecryptWithInvalidMAC() { + SecretBox secretBox = new SecretBox(new byte[32]); + byte[] message = "Hello, World!".getBytes(); + byte[] encrypted = secretBox.encrypt(message); + encrypted[encrypted.length - 1] ^= 0xFF; // Modify last byte + assertThrows(IllegalArgumentException.class, () -> secretBox.decrypt(encrypted)); + } + + @Test + public void testEncryptAndDecryptWithInvalidNonce() { + SecretBox secretBox = new SecretBox(new byte[32]); + byte[] message = "Hello, World!".getBytes(); + byte[] encrypted = secretBox.encrypt(message); + encrypted[0] ^= 0xFF; // Modify first byte + assertThrows(IllegalArgumentException.class, () -> secretBox.decrypt(encrypted)); + } + + @Test + public void testEncryptAndDecryptWithModifiedCiphertext() { + byte[] key = new byte[32]; + SecretBox secretBox = new SecretBox(key); + byte[] message = "Hello, World!".getBytes(); + byte[] encrypted = secretBox.encrypt(message); + encrypted[NONCE_SIZE + 1] ^= 0xFF; // Modify the byte next to nonce + assertThrows(IllegalArgumentException.class, () -> secretBox.decrypt(encrypted)); + } + +} From 967e1278ea4daabd088c0cbb4acc43eaefc0b743 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Sat, 24 Feb 2024 16:30:34 +0500 Subject: [PATCH 05/21] add missing EOL --- autobahn/src/main/java/xbr/network/crypto/SecretBox.java | 2 +- autobahn/src/test/java/xbr/network/UtilTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java index c2c548b0..2375a019 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java @@ -93,4 +93,4 @@ private void checkLength(byte[] data, int size) { if (data == null || data.length != size) throw new IllegalArgumentException("Invalid size: " + data.length); } -} \ No newline at end of file +} diff --git a/autobahn/src/test/java/xbr/network/UtilTest.java b/autobahn/src/test/java/xbr/network/UtilTest.java index 9ed109be..13182ea5 100644 --- a/autobahn/src/test/java/xbr/network/UtilTest.java +++ b/autobahn/src/test/java/xbr/network/UtilTest.java @@ -29,4 +29,4 @@ public void testGenerateKeyPair() { assertEquals(32, keyPair.first.length); assertEquals(32, keyPair.second.length); } -} \ No newline at end of file +} From 838b6b8900a143aa427eed7f60e6cc4d06fa1649 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 13:28:40 +0500 Subject: [PATCH 06/21] add function to create sealedbox nonce --- .../java/xbr/network/crypto/SealedBox.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index 18728663..63690b35 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -1,8 +1,10 @@ package xbr.network.crypto; +import org.bouncycastle.crypto.digests.Blake2bDigest; import org.libsodium.jni.encoders.Encoder; import static org.libsodium.jni.NaCl.sodium; +import static org.libsodium.jni.SodiumConstants.NONCE_BYTES; import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES; import static org.libsodium.jni.crypto.Util.isValid; @@ -44,15 +46,27 @@ public SealedBox(String publicKey, String privateKey, Encoder encoder) { public byte[] encrypt(byte[] message) { byte[] ct = new byte[message.length + SEAL_BYTES]; isValid(sodium().crypto_box_seal( - ct, message, message.length, publicKey), + ct, message, message.length, publicKey), "Encryption failed"); return ct; } + private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) { + Blake2bDigest blake2b = new Blake2bDigest(NONCE_BYTES * 8); + byte[] nonce = new byte[blake2b.getDigestSize()]; + + blake2b.update(ephemeralPublicKey, 0, ephemeralPublicKey.length); + blake2b.update(recipientPublicKey, 0, recipientPublicKey.length); + + blake2b.doFinal(nonce, 0); + + return nonce; + } + public byte[] decrypt(byte[] ciphertext) { byte[] message = new byte[ciphertext.length - SEAL_BYTES]; isValid(sodium().crypto_box_seal_open( - message, ciphertext, ciphertext.length, publicKey, privateKey), + message, ciphertext, ciphertext.length, publicKey, privateKey), "Decryption failed. Ciphertext failed verification"); return message; } From b1e88c6a08001dfee3afd83a25298c976ead8336 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 13:31:13 +0500 Subject: [PATCH 07/21] add function to compute shared secret for sealedbox --- .../main/java/xbr/network/crypto/Salsa.java | 146 ++++++++++++++++++ .../java/xbr/network/crypto/SealedBox.java | 10 ++ 2 files changed, 156 insertions(+) create mode 100644 autobahn/src/main/java/xbr/network/crypto/Salsa.java diff --git a/autobahn/src/main/java/xbr/network/crypto/Salsa.java b/autobahn/src/main/java/xbr/network/crypto/Salsa.java new file mode 100644 index 00000000..3617ab17 --- /dev/null +++ b/autobahn/src/main/java/xbr/network/crypto/Salsa.java @@ -0,0 +1,146 @@ +package xbr.network.crypto; + +/** + */ +public class Salsa { + public static byte[] SIGMA = {'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}; + + // HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte + // key k, and 16-byte constant c, and returns the result as the 32-byte array + // out. + public static byte[] HSalsa20(byte[] in, byte[] k, byte[] c) { + long x0 = (0xFFl & c[0]) | (0xFFl & c[1]) << 8 | (0xFFl & c[2]) << 16 | (0xFFl & c[3]) << 24; + long x1 = (0xFFl & k[0]) | (0xFFl & k[1]) << 8 | (0xFFl & k[2]) << 16 | (0xFFl & k[3]) << 24; + long x2 = (0xFFl & k[4]) | (0xFFl & k[5]) << 8 | (0xFFl & k[6]) << 16 | (0xFFl & k[7]) << 24; + long x3 = (0xFFl & k[8]) | (0xFFl & k[9]) << 8 | (0xFFl & k[10]) << 16 | (0xFFl & k[11]) << 24; + long x4 = (0xFFl & k[12]) | (0xFFl & k[13]) << 8 | (0xFFl & k[14]) << 16 | (0xFFl & k[15]) << 24; + long x5 = (0xFFl & c[4]) | (0xFFl & c[5]) << 8 | (0xFFl & c[6]) << 16 | (0xFFl & c[7]) << 24; + long x6 = (0xFFl & in[0]) | (0xFFl & in[1]) << 8 | (0xFFl & in[2]) << 16 | (0xFFl & in[3]) << 24; + long x7 = (0xFFl & in[4]) | (0xFFl & in[5]) << 8 | (0xFFl & in[6]) << 16 | (0xFFl & in[7]) << 24; + long x8 = (0xFFl & in[8]) | (0xFFl & in[9]) << 8 | (0xFFl & in[10]) << 16 | (0xFFl & in[11]) << 24; + long x9 = (0xFFl & in[12]) | (0xFFl & in[13]) << 8 | (0xFFl & in[14]) << 16 | (0xFFl & in[15]) << 24; + long x10 = (0xFFl & c[8]) | (0xFFl & c[9]) << 8 | (0xFFl & c[10]) << 16 | (0xFFl & c[11]) << 24; + long x11 = (0xFFl & k[16]) | (0xFFl & k[17]) << 8 | (0xFFl & k[18]) << 16 | (0xFFl & k[19]) << 24; + long x12 = (0xFFl & k[20]) | (0xFFl & k[21]) << 8 | (0xFFl & k[22]) << 16 | (0xFFl & k[23]) << 24; + long x13 = (0xFFl & k[24]) | (0xFFl & k[25]) << 8 | (0xFFl & k[26]) << 16 | (0xFFl & k[27]) << 24; + long x14 = (0xFFl & k[28]) | (0xFFl & k[29]) << 8 | (0xFFl & k[30]) << 16 | (0xFFl & k[31]) << 24; + long x15 = (0xFFl & c[12]) | (0xFFl & c[13]) << 8 | (0xFFl & c[14]) << 16 | (0xFFl & c[15]) << 24; + + long mask = 0xFFFFFFFFl; + for (int i = 0; i < 20; i += 2) { + long u = mask & (x0 + x12); + x4 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x4 + x0); + x8 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x8 + x4); + x12 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x12 + x8); + x0 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x5 + x1); + x9 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x9 + x5); + x13 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x13 + x9); + x1 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x1 + x13); + x5 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x10 + x6); + x14 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x14 + x10); + x2 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x2 + x14); + x6 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x6 + x2); + x10 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x15 + x11); + x3 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x3 + x15); + x7 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x7 + x3); + x11 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x11 + x7); + x15 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x0 + x3); + x1 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x1 + x0); + x2 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x2 + x1); + x3 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x3 + x2); + x0 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x5 + x4); + x6 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x6 + x5); + x7 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x7 + x6); + x4 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x4 + x7); + x5 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x10 + x9); + x11 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x11 + x10); + x8 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x8 + x11); + x9 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x9 + x8); + x10 ^= mask & (u << 18 | u >>> (32 - 18)); + + u = mask & (x15 + x14); + x12 ^= mask & (u << 7 | u >>> (32 - 7)); + u = mask & (x12 + x15); + x13 ^= mask & (u << 9 | u >>> (32 - 9)); + u = mask & (x13 + x12); + x14 ^= mask & (u << 13 | u >>> (32 - 13)); + u = mask & (x14 + x13); + x15 ^= mask & (u << 18 | u >>> (32 - 18)); + } + + byte out[] = new byte[32]; + out[0] = (byte) x0; + out[1] = (byte) (x0 >> 8); + out[2] = (byte) (x0 >> 16); + out[3] = (byte) (x0 >> 24); + + out[4] = (byte) (x5); + out[5] = (byte) (x5 >> 8); + out[6] = (byte) (x5 >> 16); + out[7] = (byte) (x5 >> 24); + + out[8] = (byte) (x10); + out[9] = (byte) (x10 >> 8); + out[10] = (byte) (x10 >> 16); + out[11] = (byte) (x10 >> 24); + + out[12] = (byte) (x15); + out[13] = (byte) (x15 >> 8); + out[14] = (byte) (x15 >> 16); + out[15] = (byte) (x15 >> 24); + + out[16] = (byte) (x6); + out[17] = (byte) (x6 >> 8); + out[18] = (byte) (x6 >> 16); + out[19] = (byte) (x6 >> 24); + + out[20] = (byte) (x7); + out[21] = (byte) (x7 >> 8); + out[22] = (byte) (x7 >> 16); + out[23] = (byte) (x7 >> 24); + + out[24] = (byte) (x8); + out[25] = (byte) (x8 >> 8); + out[26] = (byte) (x8 >> 16); + out[27] = (byte) (x8 >> 24); + + out[28] = (byte) (x9); + out[29] = (byte) (x9 >> 8); + out[30] = (byte) (x9 >> 16); + out[31] = (byte) (x9 >> 24); + return out; + } +} diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index 63690b35..f9a66a1f 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -1,6 +1,7 @@ package xbr.network.crypto; import org.bouncycastle.crypto.digests.Blake2bDigest; +import org.bouncycastle.math.ec.rfc7748.X25519; import org.libsodium.jni.encoders.Encoder; import static org.libsodium.jni.NaCl.sodium; @@ -63,6 +64,15 @@ private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) return nonce; } + public byte[] computeSharedSecret(byte[] publicKey, byte[] privateKey) { + byte[] sharedSecret = new byte[32]; + // compute the raw shared secret + X25519.scalarMult(publicKey, 0, privateKey, 0, sharedSecret, 0); + // encrypt the shared secret + byte[] nonce = new byte[32]; + return Salsa.HSalsa20(nonce, sharedSecret, Salsa.SIGMA); + } + public byte[] decrypt(byte[] ciphertext) { byte[] message = new byte[ciphertext.length - SEAL_BYTES]; isValid(sodium().crypto_box_seal_open( From cdcc45e4bd46c01636aadc2881ca3bb520c5f0b2 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 13:38:07 +0500 Subject: [PATCH 08/21] implement the libsodium compatible seal function --- .../java/xbr/network/crypto/SealedBox.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index f9a66a1f..c71be264 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -1,14 +1,23 @@ package xbr.network.crypto; import org.bouncycastle.crypto.digests.Blake2bDigest; +import org.bouncycastle.crypto.engines.XSalsa20Engine; +import org.bouncycastle.crypto.macs.Poly1305; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.math.ec.rfc7748.X25519; +import org.bouncycastle.util.Arrays; import org.libsodium.jni.encoders.Encoder; import static org.libsodium.jni.NaCl.sodium; import static org.libsodium.jni.SodiumConstants.NONCE_BYTES; import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES; +import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES; import static org.libsodium.jni.crypto.Util.isValid; +import io.crossbar.autobahn.utils.Pair; +import xbr.network.Util; + public class SealedBox { private static final int MAC_BYTES = 16; @@ -52,6 +61,32 @@ public byte[] encrypt(byte[] message) { return ct; } + public byte[] encrypt(byte[] message, byte[] recipientPublicKey) { + Pair keyPair = Util.generateX25519KeyPair(); + byte[] nonce = createNonce(keyPair.first, recipientPublicKey); + byte[] sharedSecret = computeSharedSecret(recipientPublicKey, keyPair.second); + + XSalsa20Engine cipher = new XSalsa20Engine(); + ParametersWithIV params = new ParametersWithIV(new KeyParameter(sharedSecret), nonce); + cipher.init(true, params); + + byte[] sk = new byte[SECRETKEY_BYTES]; + cipher.processBytes(sk, 0, sk.length, sk, 0); + + // encrypt the message + byte[] ciphertext = new byte[message.length]; + cipher.processBytes(message, 0, message.length, ciphertext, 0); + + // create the MAC + Poly1305 mac = new Poly1305(); + byte[] macBuf = new byte[mac.getMacSize()]; + mac.init(new KeyParameter(sk)); + mac.update(ciphertext, 0, ciphertext.length); + mac.doFinal(macBuf, 0); + + return Arrays.concatenate(keyPair.first, macBuf, ciphertext); + } + private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) { Blake2bDigest blake2b = new Blake2bDigest(NONCE_BYTES * 8); byte[] nonce = new byte[blake2b.getDigestSize()]; From 8ce13a5e5b8412526b6295166becf7a7540aab33 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 13:59:37 +0500 Subject: [PATCH 09/21] implement the libsodium compatible unseal function --- .../main/java/xbr/network/crypto/SealedBox.java | 16 +++++++++------- .../main/java/xbr/network/crypto/SecretBox.java | 3 +-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index c71be264..4d164d47 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -102,17 +102,19 @@ private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) public byte[] computeSharedSecret(byte[] publicKey, byte[] privateKey) { byte[] sharedSecret = new byte[32]; // compute the raw shared secret - X25519.scalarMult(publicKey, 0, privateKey, 0, sharedSecret, 0); + X25519.scalarMult(privateKey, 0, publicKey, 0, sharedSecret, 0); // encrypt the shared secret byte[] nonce = new byte[32]; return Salsa.HSalsa20(nonce, sharedSecret, Salsa.SIGMA); } - public byte[] decrypt(byte[] ciphertext) { - byte[] message = new byte[ciphertext.length - SEAL_BYTES]; - isValid(sodium().crypto_box_seal_open( - message, ciphertext, ciphertext.length, publicKey, privateKey), - "Decryption failed. Ciphertext failed verification"); - return message; + public byte[] decrypt(byte[] message) { + byte[] ephemeralPublicKey = Arrays.copyOf(message, PUBLICKEY_BYTES); + byte[] ciphertext = Arrays.copyOfRange(message, PUBLICKEY_BYTES, message.length); + byte[] nonce = createNonce(ephemeralPublicKey, publicKey); + byte[] sharedSecret = computeSharedSecret(ephemeralPublicKey, privateKey); + + SecretBox box = new SecretBox(sharedSecret); + return box.decrypt(nonce, ciphertext); } } diff --git a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java index 2375a019..b998d80e 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java @@ -57,8 +57,7 @@ public byte[] decrypt(byte[] ciphertext) { return decrypt(nonce, message); } - - private byte[] decrypt(byte[] nonce, byte[] ciphertext) { + public byte[] decrypt(byte[] nonce, byte[] ciphertext) { checkLength(nonce, NONCE_SIZE); XSalsa20Engine xsalsa20 = new XSalsa20Engine(); From af76c2c94d115f91cfa389529e8ab8d3fe6812a9 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 14:26:47 +0500 Subject: [PATCH 10/21] make encrypt to only use the new code --- .../main/java/xbr/network/crypto/SealedBox.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index 4d164d47..0fcbba4e 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -9,11 +9,9 @@ import org.bouncycastle.util.Arrays; import org.libsodium.jni.encoders.Encoder; -import static org.libsodium.jni.NaCl.sodium; import static org.libsodium.jni.SodiumConstants.NONCE_BYTES; import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES; import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES; -import static org.libsodium.jni.crypto.Util.isValid; import io.crossbar.autobahn.utils.Pair; import xbr.network.Util; @@ -54,17 +52,9 @@ public SealedBox(String publicKey, String privateKey, Encoder encoder) { } public byte[] encrypt(byte[] message) { - byte[] ct = new byte[message.length + SEAL_BYTES]; - isValid(sodium().crypto_box_seal( - ct, message, message.length, publicKey), - "Encryption failed"); - return ct; - } - - public byte[] encrypt(byte[] message, byte[] recipientPublicKey) { Pair keyPair = Util.generateX25519KeyPair(); - byte[] nonce = createNonce(keyPair.first, recipientPublicKey); - byte[] sharedSecret = computeSharedSecret(recipientPublicKey, keyPair.second); + byte[] nonce = createNonce(keyPair.first, publicKey); + byte[] sharedSecret = computeSharedSecret(publicKey, keyPair.second); XSalsa20Engine cipher = new XSalsa20Engine(); ParametersWithIV params = new ParametersWithIV(new KeyParameter(sharedSecret), nonce); From c6f45a7c0e956a238a2c8f83833b8698915f9d55 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 15:16:23 +0500 Subject: [PATCH 11/21] remove dependency of libsodium --- autobahn/build.gradle | 7 ++----- .../main/java/xbr/network/crypto/SealedBox.java | 17 +++-------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index 014e2fd8..ba425784 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -27,10 +27,7 @@ dependencies { implementation 'org.web3j:core:5.0.0' implementation 'org.web3j:abi:5.0.0' implementation 'org.web3j:utils:5.0.0' - if (IS_ANDROID) { - implementation 'com.github.joshjdevl.libsodiumjni:libsodium-jni-aar:2.0.2' - } else { - implementation 'com.github.joshjdevl.libsodiumjni:libsodium-jni:2.0.2' + if (!IS_ANDROID) { implementation 'org.json:json:20240205' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.5' } @@ -188,7 +185,7 @@ afterEvaluate { artifactId ARTIFACT_ANDROID } else { from components.java - artifactId IS_NEXT ? ARTIFACT_NEXT: ARTIFACT_JAVA + artifactId IS_NEXT ? ARTIFACT_NEXT : ARTIFACT_JAVA } artifact sourcesJar diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index 0fcbba4e..c0c34665 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -7,11 +7,6 @@ import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.math.ec.rfc7748.X25519; import org.bouncycastle.util.Arrays; -import org.libsodium.jni.encoders.Encoder; - -import static org.libsodium.jni.SodiumConstants.NONCE_BYTES; -import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES; -import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES; import io.crossbar.autobahn.utils.Pair; import xbr.network.Util; @@ -19,6 +14,7 @@ public class SealedBox { private static final int MAC_BYTES = 16; + private static int PUBLICKEY_BYTES = 32; private static final int SEAL_BYTES = PUBLICKEY_BYTES + MAC_BYTES; private byte[] publicKey; @@ -32,9 +28,6 @@ public SealedBox(byte[] publicKey) { this.privateKey = null; } - public SealedBox(String publicKey, Encoder encoder) { - this(encoder.decode(publicKey)); - } public SealedBox(byte[] publicKey, byte[] privateKey) { if (publicKey == null) { @@ -47,10 +40,6 @@ public SealedBox(byte[] publicKey, byte[] privateKey) { this.privateKey = privateKey; } - public SealedBox(String publicKey, String privateKey, Encoder encoder) { - this(encoder.decode(publicKey), encoder.decode(privateKey)); - } - public byte[] encrypt(byte[] message) { Pair keyPair = Util.generateX25519KeyPair(); byte[] nonce = createNonce(keyPair.first, publicKey); @@ -60,7 +49,7 @@ public byte[] encrypt(byte[] message) { ParametersWithIV params = new ParametersWithIV(new KeyParameter(sharedSecret), nonce); cipher.init(true, params); - byte[] sk = new byte[SECRETKEY_BYTES]; + byte[] sk = new byte[Util.SECRET_KEY_LEN]; cipher.processBytes(sk, 0, sk.length, sk, 0); // encrypt the message @@ -78,7 +67,7 @@ public byte[] encrypt(byte[] message) { } private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) { - Blake2bDigest blake2b = new Blake2bDigest(NONCE_BYTES * 8); + Blake2bDigest blake2b = new Blake2bDigest(Util.NONCE_SIZE * 8); byte[] nonce = new byte[blake2b.getDigestSize()]; blake2b.update(ephemeralPublicKey, 0, ephemeralPublicKey.length); From 41b71b92d65052e28cadb6d19f03eb1c3cd4d8df Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 15:21:09 +0500 Subject: [PATCH 12/21] use correct version of web3j --- autobahn/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index ba425784..3802e6cf 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -24,9 +24,9 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' - implementation 'org.web3j:core:5.0.0' - implementation 'org.web3j:abi:5.0.0' - implementation 'org.web3j:utils:5.0.0' + implementation 'org.web3j:core:4.11.0' + implementation 'org.web3j:abi:4.11.0' + implementation 'org.web3j:utils:4.11.0' if (!IS_ANDROID) { implementation 'org.json:json:20240205' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.5' From fbdd505d58d784f65cd2e702f1c3e3a66f4f3b3f Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 15:44:17 +0500 Subject: [PATCH 13/21] use correct dependencies --- autobahn/build.gradle | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index 3802e6cf..d8325f20 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -24,10 +24,14 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' - implementation 'org.web3j:core:4.11.0' - implementation 'org.web3j:abi:4.11.0' - implementation 'org.web3j:utils:4.11.0' - if (!IS_ANDROID) { + if (IS_ANDROID) { + implementation 'org.web3j:core:4.8.7-android' + implementation 'org.web3j:abi:4.8.7-android' + implementation 'org.web3j:utils:4.8.7-android' + } else{ + implementation 'org.web3j:core:4.11.0' + implementation 'org.web3j:abi:4.11.0' + implementation 'org.web3j:utils:4.11.0' implementation 'org.json:json:20240205' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.5' } From 492a7f119e304ab222d2047d1362d3f0385c5df9 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 17:36:19 +0500 Subject: [PATCH 14/21] add HSalsa20 implementation based on bouncy castle --- .../java/xbr/network/crypto/HSalsa20.java | 61 ++++++++ .../main/java/xbr/network/crypto/Salsa.java | 146 ------------------ .../java/xbr/network/crypto/SealedBox.java | 6 +- 3 files changed, 65 insertions(+), 148 deletions(-) create mode 100644 autobahn/src/main/java/xbr/network/crypto/HSalsa20.java delete mode 100644 autobahn/src/main/java/xbr/network/crypto/Salsa.java diff --git a/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java b/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java new file mode 100644 index 00000000..810327a9 --- /dev/null +++ b/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java @@ -0,0 +1,61 @@ +package xbr.network.crypto; + +import java.nio.charset.StandardCharsets; +import org.bouncycastle.crypto.engines.Salsa20Engine; +import org.bouncycastle.util.Pack; + +/** An implementation of the HSalsa20 hash based on the Bouncy Castle Salsa20 core. */ +class HSalsa20 { + + private static final byte[] SIGMA = "expand 32-byte k".getBytes(StandardCharsets.US_ASCII); + private static final int SIGMA_0 = Pack.littleEndianToInt(SIGMA, 0); + private static final int SIGMA_4 = Pack.littleEndianToInt(SIGMA, 4); + private static final int SIGMA_8 = Pack.littleEndianToInt(SIGMA, 8); + private static final int SIGMA_12 = Pack.littleEndianToInt(SIGMA, 12); + + static void hsalsa20(byte[] out, byte[] in, byte[] k) { + final int[] x = new int[16]; + + final int in0 = Pack.littleEndianToInt(in, 0); + final int in4 = Pack.littleEndianToInt(in, 4); + final int in8 = Pack.littleEndianToInt(in, 8); + final int in12 = Pack.littleEndianToInt(in, 12); + + x[0] = SIGMA_0; + x[1] = Pack.littleEndianToInt(k, 0); + x[2] = Pack.littleEndianToInt(k, 4); + x[3] = Pack.littleEndianToInt(k, 8); + x[4] = Pack.littleEndianToInt(k, 12); + x[5] = SIGMA_4; + x[6] = in0; + x[7] = in4; + x[8] = in8; + x[9] = in12; + x[10] = SIGMA_8; + x[11] = Pack.littleEndianToInt(k, 16); + x[12] = Pack.littleEndianToInt(k, 20); + x[13] = Pack.littleEndianToInt(k, 24); + x[14] = Pack.littleEndianToInt(k, 28); + x[15] = SIGMA_12; + + Salsa20Engine.salsaCore(20, x, x); + + x[0] -= SIGMA_0; + x[5] -= SIGMA_4; + x[10] -= SIGMA_8; + x[15] -= SIGMA_12; + x[6] -= in0; + x[7] -= in4; + x[8] -= in8; + x[9] -= in12; + + Pack.intToLittleEndian(x[0], out, 0); + Pack.intToLittleEndian(x[5], out, 4); + Pack.intToLittleEndian(x[10], out, 8); + Pack.intToLittleEndian(x[15], out, 12); + Pack.intToLittleEndian(x[6], out, 16); + Pack.intToLittleEndian(x[7], out, 20); + Pack.intToLittleEndian(x[8], out, 24); + Pack.intToLittleEndian(x[9], out, 28); + } +} diff --git a/autobahn/src/main/java/xbr/network/crypto/Salsa.java b/autobahn/src/main/java/xbr/network/crypto/Salsa.java deleted file mode 100644 index 3617ab17..00000000 --- a/autobahn/src/main/java/xbr/network/crypto/Salsa.java +++ /dev/null @@ -1,146 +0,0 @@ -package xbr.network.crypto; - -/** - */ -public class Salsa { - public static byte[] SIGMA = {'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}; - - // HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte - // key k, and 16-byte constant c, and returns the result as the 32-byte array - // out. - public static byte[] HSalsa20(byte[] in, byte[] k, byte[] c) { - long x0 = (0xFFl & c[0]) | (0xFFl & c[1]) << 8 | (0xFFl & c[2]) << 16 | (0xFFl & c[3]) << 24; - long x1 = (0xFFl & k[0]) | (0xFFl & k[1]) << 8 | (0xFFl & k[2]) << 16 | (0xFFl & k[3]) << 24; - long x2 = (0xFFl & k[4]) | (0xFFl & k[5]) << 8 | (0xFFl & k[6]) << 16 | (0xFFl & k[7]) << 24; - long x3 = (0xFFl & k[8]) | (0xFFl & k[9]) << 8 | (0xFFl & k[10]) << 16 | (0xFFl & k[11]) << 24; - long x4 = (0xFFl & k[12]) | (0xFFl & k[13]) << 8 | (0xFFl & k[14]) << 16 | (0xFFl & k[15]) << 24; - long x5 = (0xFFl & c[4]) | (0xFFl & c[5]) << 8 | (0xFFl & c[6]) << 16 | (0xFFl & c[7]) << 24; - long x6 = (0xFFl & in[0]) | (0xFFl & in[1]) << 8 | (0xFFl & in[2]) << 16 | (0xFFl & in[3]) << 24; - long x7 = (0xFFl & in[4]) | (0xFFl & in[5]) << 8 | (0xFFl & in[6]) << 16 | (0xFFl & in[7]) << 24; - long x8 = (0xFFl & in[8]) | (0xFFl & in[9]) << 8 | (0xFFl & in[10]) << 16 | (0xFFl & in[11]) << 24; - long x9 = (0xFFl & in[12]) | (0xFFl & in[13]) << 8 | (0xFFl & in[14]) << 16 | (0xFFl & in[15]) << 24; - long x10 = (0xFFl & c[8]) | (0xFFl & c[9]) << 8 | (0xFFl & c[10]) << 16 | (0xFFl & c[11]) << 24; - long x11 = (0xFFl & k[16]) | (0xFFl & k[17]) << 8 | (0xFFl & k[18]) << 16 | (0xFFl & k[19]) << 24; - long x12 = (0xFFl & k[20]) | (0xFFl & k[21]) << 8 | (0xFFl & k[22]) << 16 | (0xFFl & k[23]) << 24; - long x13 = (0xFFl & k[24]) | (0xFFl & k[25]) << 8 | (0xFFl & k[26]) << 16 | (0xFFl & k[27]) << 24; - long x14 = (0xFFl & k[28]) | (0xFFl & k[29]) << 8 | (0xFFl & k[30]) << 16 | (0xFFl & k[31]) << 24; - long x15 = (0xFFl & c[12]) | (0xFFl & c[13]) << 8 | (0xFFl & c[14]) << 16 | (0xFFl & c[15]) << 24; - - long mask = 0xFFFFFFFFl; - for (int i = 0; i < 20; i += 2) { - long u = mask & (x0 + x12); - x4 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x4 + x0); - x8 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x8 + x4); - x12 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x12 + x8); - x0 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x5 + x1); - x9 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x9 + x5); - x13 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x13 + x9); - x1 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x1 + x13); - x5 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x10 + x6); - x14 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x14 + x10); - x2 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x2 + x14); - x6 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x6 + x2); - x10 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x15 + x11); - x3 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x3 + x15); - x7 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x7 + x3); - x11 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x11 + x7); - x15 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x0 + x3); - x1 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x1 + x0); - x2 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x2 + x1); - x3 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x3 + x2); - x0 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x5 + x4); - x6 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x6 + x5); - x7 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x7 + x6); - x4 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x4 + x7); - x5 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x10 + x9); - x11 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x11 + x10); - x8 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x8 + x11); - x9 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x9 + x8); - x10 ^= mask & (u << 18 | u >>> (32 - 18)); - - u = mask & (x15 + x14); - x12 ^= mask & (u << 7 | u >>> (32 - 7)); - u = mask & (x12 + x15); - x13 ^= mask & (u << 9 | u >>> (32 - 9)); - u = mask & (x13 + x12); - x14 ^= mask & (u << 13 | u >>> (32 - 13)); - u = mask & (x14 + x13); - x15 ^= mask & (u << 18 | u >>> (32 - 18)); - } - - byte out[] = new byte[32]; - out[0] = (byte) x0; - out[1] = (byte) (x0 >> 8); - out[2] = (byte) (x0 >> 16); - out[3] = (byte) (x0 >> 24); - - out[4] = (byte) (x5); - out[5] = (byte) (x5 >> 8); - out[6] = (byte) (x5 >> 16); - out[7] = (byte) (x5 >> 24); - - out[8] = (byte) (x10); - out[9] = (byte) (x10 >> 8); - out[10] = (byte) (x10 >> 16); - out[11] = (byte) (x10 >> 24); - - out[12] = (byte) (x15); - out[13] = (byte) (x15 >> 8); - out[14] = (byte) (x15 >> 16); - out[15] = (byte) (x15 >> 24); - - out[16] = (byte) (x6); - out[17] = (byte) (x6 >> 8); - out[18] = (byte) (x6 >> 16); - out[19] = (byte) (x6 >> 24); - - out[20] = (byte) (x7); - out[21] = (byte) (x7 >> 8); - out[22] = (byte) (x7 >> 16); - out[23] = (byte) (x7 >> 24); - - out[24] = (byte) (x8); - out[25] = (byte) (x8 >> 8); - out[26] = (byte) (x8 >> 16); - out[27] = (byte) (x8 >> 24); - - out[28] = (byte) (x9); - out[29] = (byte) (x9 >> 8); - out[30] = (byte) (x9 >> 16); - out[31] = (byte) (x9 >> 24); - return out; - } -} diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index c0c34665..c5cec016 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -83,8 +83,10 @@ public byte[] computeSharedSecret(byte[] publicKey, byte[] privateKey) { // compute the raw shared secret X25519.scalarMult(privateKey, 0, publicKey, 0, sharedSecret, 0); // encrypt the shared secret - byte[] nonce = new byte[32]; - return Salsa.HSalsa20(nonce, sharedSecret, Salsa.SIGMA); + byte[] key = new byte[32]; + byte[] HSALSA20_SEED = new byte[16]; + HSalsa20.hsalsa20(key, HSALSA20_SEED, sharedSecret); + return key; } public byte[] decrypt(byte[] message) { From 5ec6bdaa438f4d08512d6611d5cde92b5404260b Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 17:42:00 +0500 Subject: [PATCH 15/21] make HSALSA20_SEED global,static variable --- autobahn/src/main/java/xbr/network/crypto/SealedBox.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index c5cec016..940994ef 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -17,6 +17,7 @@ public class SealedBox { private static int PUBLICKEY_BYTES = 32; private static final int SEAL_BYTES = PUBLICKEY_BYTES + MAC_BYTES; + private static final byte[] HSALSA20_SEED = new byte[16]; private byte[] publicKey; private byte[] privateKey; @@ -84,7 +85,6 @@ public byte[] computeSharedSecret(byte[] publicKey, byte[] privateKey) { X25519.scalarMult(privateKey, 0, publicKey, 0, sharedSecret, 0); // encrypt the shared secret byte[] key = new byte[32]; - byte[] HSALSA20_SEED = new byte[16]; HSalsa20.hsalsa20(key, HSALSA20_SEED, sharedSecret); return key; } From b0300d7280ec0d1d5fb67891d8a24ad330dc4ad4 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Mon, 26 Feb 2024 20:27:58 +0500 Subject: [PATCH 16/21] add lisence for HSalsa20 --- .../main/java/xbr/network/crypto/HSalsa20.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java b/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java index 810327a9..f378ffab 100644 --- a/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java +++ b/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java @@ -1,3 +1,18 @@ +/* + * Copyright © 2017 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package xbr.network.crypto; import java.nio.charset.StandardCharsets; From cffc968a1b70bfe113b1cba2b46b295c20603775 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Tue, 27 Feb 2024 18:18:30 +0500 Subject: [PATCH 17/21] update web3j and java --- autobahn/build.gradle | 10 +++++----- build.gradle | 1 + demo-gallery/build.gradle | 5 ++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index d8325f20..bb135c8b 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -25,9 +25,9 @@ dependencies { implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' if (IS_ANDROID) { - implementation 'org.web3j:core:4.8.7-android' - implementation 'org.web3j:abi:4.8.7-android' - implementation 'org.web3j:utils:4.8.7-android' + implementation 'org.web3j:core:4.8.8-android' + implementation 'org.web3j:abi:4.8.8-android' + implementation 'org.web3j:utils:4.8.8-android' } else{ implementation 'org.web3j:core:4.11.0' implementation 'org.web3j:abi:4.11.0' @@ -176,8 +176,8 @@ if (IS_ANDROID) { jar { version = relVersion } - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } afterEvaluate { diff --git a/build.gradle b/build.gradle index cd36d419..f64b0493 100644 --- a/build.gradle +++ b/build.gradle @@ -38,5 +38,6 @@ allprojects { google() mavenCentral() maven { url "https://dl.bintray.com/ethereum/maven/" } + maven { url "https://artifacts.consensys.net/public/maven/maven/" } } } diff --git a/demo-gallery/build.gradle b/demo-gallery/build.gradle index c4ea851b..ac4d47f5 100644 --- a/demo-gallery/build.gradle +++ b/demo-gallery/build.gradle @@ -47,7 +47,7 @@ if (plugins.hasPlugin(project.PLUGIN_ANDROID_APP)) { implementation "com.google.android.material:material:1.11.0" implementation 'com.basgeekball:awesome-validation:4.2' - implementation 'org.web3j:crypto:5.0.0' + implementation 'org.web3j:crypto:4.8.8-android' implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' } } else if (project.IS_NEXT) { @@ -79,4 +79,7 @@ if (plugins.hasPlugin(project.PLUGIN_ANDROID_APP)) { jar { version = project.properties.get("buildVersion", "") } + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } From 091097363048866f2ec41d54a15aebd0c15012a6 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 6 Mar 2024 17:54:59 +0500 Subject: [PATCH 18/21] use cryptology --- autobahn/build.gradle | 1 + .../autobahn/wamp/auth/CryptosignAuth.java | 38 +++------- .../src/main/java/xbr/network/KeySeries.java | 20 ++--- .../main/java/xbr/network/SimpleBuyer.java | 9 ++- .../main/java/xbr/network/SimpleSeller.java | 4 +- autobahn/src/main/java/xbr/network/Util.java | 29 ------- .../java/xbr/network/crypto/HSalsa20.java | 76 ------------------- .../java/xbr/network/crypto/SealedBox.java | 66 +--------------- .../java/xbr/network/crypto/SecretBox.java | 74 +++--------------- .../src/test/java/xbr/network/UtilTest.java | 32 -------- .../xbr/network/crypto/SealedBoxTest.java | 43 +++++++++++ .../xbr/network/crypto/SecretBoxTest.java | 3 +- demo-gallery/build.gradle | 4 +- 13 files changed, 94 insertions(+), 305 deletions(-) delete mode 100644 autobahn/src/main/java/xbr/network/crypto/HSalsa20.java delete mode 100644 autobahn/src/test/java/xbr/network/UtilTest.java create mode 100644 autobahn/src/test/java/xbr/network/crypto/SealedBoxTest.java diff --git a/autobahn/build.gradle b/autobahn/build.gradle index bb135c8b..7561f474 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' + implementation 'io.xconn:cryptology:0.1.0' if (IS_ANDROID) { implementation 'org.web3j:core:4.8.8-android' implementation 'org.web3j:abi:4.8.8-android' diff --git a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java index d9ea680c..48f87001 100644 --- a/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java +++ b/autobahn/src/main/java/io/crossbar/autobahn/wamp/auth/CryptosignAuth.java @@ -1,18 +1,15 @@ package io.crossbar.autobahn.wamp.auth; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; -import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; -import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; -import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; -import org.bouncycastle.crypto.signers.Ed25519Signer; - import java.io.File; -import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; +import org.bouncycastle.util.encoders.Hex; + +import io.xconn.cryptology.CryptoSign; +import io.xconn.cryptology.KeyPair; + import io.crossbar.autobahn.utils.AuthUtil; import io.crossbar.autobahn.utils.Pair; import io.crossbar.autobahn.wamp.Session; @@ -31,16 +28,10 @@ public class CryptosignAuth implements IAuthenticator { private final byte[] privateKeyRaw; public static Pair generateSigningKeyPair() { - Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator(); - keyPairGenerator.init(new Ed25519KeyGenerationParameters(new SecureRandom())); + KeyPair keyPair = CryptoSign.generateKeyPair(); - AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); - - Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) keyPair.getPrivate(); - Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) keyPair.getPublic(); - - String privateKeyHex = AuthUtil.toHexString(privateKey.getEncoded()); - String publicKeyHex = AuthUtil.toHexString(publicKey.getEncoded()); + String publicKeyHex = Hex.toHexString(keyPair.getPublicKey()); + String privateKeyHex = Hex.toHexString(keyPair.getPrivateKey()); return new Pair<>(publicKeyHex, privateKeyHex); } @@ -50,9 +41,7 @@ public CryptosignAuth(String authid, String privateKey) { } public static String getPublicKey(byte[] privateKeyRaw) { - Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(privateKeyRaw, 0); - - byte[] publicKeyBytes = privateKey.generatePublicKey().getEncoded(); + byte[] publicKeyBytes = CryptoSign.getPublicKey(privateKeyRaw); return AuthUtil.toHexString(publicKeyBytes); } @@ -78,8 +67,7 @@ public CryptosignAuth(String authid, File privateKeyFile) { } } - public CryptosignAuth(String authid, String authrole, String privkey, - Map authextra) { + public CryptosignAuth(String authid, String authrole, String privkey, Map authextra) { this.authid = authid; this.authrole = authrole; this.authextra = authextra; @@ -95,11 +83,7 @@ public CompletableFuture onChallenge(Session session, Challen String hexChallenge = (String) challenge.extra.get("challenge"); byte[] rawChallenge = AuthUtil.toBinary(hexChallenge); - Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(privateKeyRaw, 0); - Ed25519Signer signer = new Ed25519Signer(); - signer.init(true, privateKey); - signer.update(rawChallenge, 0, rawChallenge.length); - byte[] signed = signer.generateSignature(); + byte[] signed = CryptoSign.sign(privateKeyRaw, rawChallenge); String signatureHex = AuthUtil.toHexString(signed); String res = signatureHex + hexChallenge; diff --git a/autobahn/src/main/java/xbr/network/KeySeries.java b/autobahn/src/main/java/xbr/network/KeySeries.java index 118088e8..1df16ef3 100644 --- a/autobahn/src/main/java/xbr/network/KeySeries.java +++ b/autobahn/src/main/java/xbr/network/KeySeries.java @@ -11,12 +11,6 @@ package xbr.network; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.cbor.CBORFactory; - -import org.web3j.utils.Numeric; - import java.math.BigInteger; import java.util.HashMap; import java.util.Map; @@ -24,6 +18,14 @@ import java.util.TimerTask; import java.util.function.Consumer; +import static io.xconn.cryptology.Util.generateRandomBytesArray; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.cbor.CBORFactory; + +import org.web3j.utils.Numeric; + import xbr.network.crypto.SealedBox; import xbr.network.crypto.SecretBox; @@ -92,7 +94,7 @@ byte[] getPrice() { } Map encrypt(Object payload) throws JsonProcessingException { - byte[] nonce = Util.generateRandomBytesArray(Util.NONCE_SIZE); + byte[] nonce = generateRandomBytesArray(Util.NONCE_SIZE); Map data = new HashMap<>(); data.put("id", mID); @@ -109,8 +111,8 @@ byte[] encryptKey(byte[] keyID, byte[] buyerPubKey) { } private void onRotate() { - mID = Util.generateRandomBytesArray(16); - mKey = Util.generateRandomBytesArray(Util.SECRET_KEY_LEN); + mID = generateRandomBytesArray(16); + mKey = generateRandomBytesArray(Util.SECRET_KEY_LEN); mBox = new SecretBox(mKey); Map data = new HashMap<>(); diff --git a/autobahn/src/main/java/xbr/network/SimpleBuyer.java b/autobahn/src/main/java/xbr/network/SimpleBuyer.java index 3d45bbd8..8ed30353 100644 --- a/autobahn/src/main/java/xbr/network/SimpleBuyer.java +++ b/autobahn/src/main/java/xbr/network/SimpleBuyer.java @@ -26,9 +26,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; +import io.xconn.cryptology.KeyPair; + import io.crossbar.autobahn.utils.ABLogger; import io.crossbar.autobahn.utils.IABLogger; -import io.crossbar.autobahn.utils.Pair; import io.crossbar.autobahn.wamp.Session; import io.crossbar.autobahn.wamp.exceptions.ApplicationError; import xbr.network.crypto.SealedBox; @@ -67,9 +68,9 @@ public SimpleBuyer(String marketMakerAddr, String buyerKey, BigInteger maxPrice) mEthPublicKey = mECKey.getPublicKey().toByteArray(); mEthAddr = Numeric.hexStringToByteArray(Credentials.create(mECKey).getAddress()); - Pair pubPriKeyPair = Util.generateX25519KeyPair(); - mPublicKey = pubPriKeyPair.first; - mPrivateKey = pubPriKeyPair.second; + KeyPair pubPriKeyPair = io.xconn.cryptology.SealedBox.generateKeyPair(); + mPublicKey = pubPriKeyPair.getPublicKey(); + mPrivateKey = pubPriKeyPair.getPrivateKey(); mMaxPrice = maxPrice; mKeys = new HashMap<>(); diff --git a/autobahn/src/main/java/xbr/network/SimpleSeller.java b/autobahn/src/main/java/xbr/network/SimpleSeller.java index 3e5c9ae8..3f3b5471 100644 --- a/autobahn/src/main/java/xbr/network/SimpleSeller.java +++ b/autobahn/src/main/java/xbr/network/SimpleSeller.java @@ -11,6 +11,8 @@ package xbr.network; +import static io.xconn.cryptology.Util.generateRandomBytesArray; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -92,7 +94,7 @@ public byte[] getPublicKey() { private void onRotate(KeySeries series) { mKeysMap.put(Numeric.toHexString(series.getID()), series); long validFrom = Math.round(System.nanoTime() - 10 * Math.pow(10, 9)); - byte[] signature = Util.generateRandomBytesArray(65); + byte[] signature = generateRandomBytesArray(65); List args = new ArrayList<>(); args.add(series.getID()); diff --git a/autobahn/src/main/java/xbr/network/Util.java b/autobahn/src/main/java/xbr/network/Util.java index 03757ae0..956becd2 100644 --- a/autobahn/src/main/java/xbr/network/Util.java +++ b/autobahn/src/main/java/xbr/network/Util.java @@ -11,11 +11,6 @@ package xbr.network; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; -import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; -import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; -import org.bouncycastle.crypto.params.X25519PublicKeyParameters; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -27,12 +22,9 @@ import java.io.IOException; import java.math.BigInteger; -import java.security.SecureRandom; import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import io.crossbar.autobahn.utils.Pair; - public class Util { public static final int NONCE_SIZE = 24; @@ -180,25 +172,4 @@ static CompletableFuture recoverEIP712Signer(int chainId, String verifyi return future; } - - public static byte[] generateRandomBytesArray(int size) { - byte[] randomBytes = new byte[size]; - SecureRandom random = new SecureRandom(); - random.nextBytes(randomBytes); - return randomBytes; - } - - public static Pair generateX25519KeyPair() { - SecureRandom random = new SecureRandom(); - X25519KeyGenerationParameters params = new X25519KeyGenerationParameters(random); - X25519KeyPairGenerator generator = new X25519KeyPairGenerator(); - generator.init(params); - - AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); - - X25519PrivateKeyParameters privateKeyParams = (X25519PrivateKeyParameters) keyPair.getPrivate(); - X25519PublicKeyParameters publicKeyParams = (X25519PublicKeyParameters) keyPair.getPublic(); - - return new Pair<>(publicKeyParams.getEncoded(), privateKeyParams.getEncoded()); - } } diff --git a/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java b/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java deleted file mode 100644 index f378ffab..00000000 --- a/autobahn/src/main/java/xbr/network/crypto/HSalsa20.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © 2017 Coda Hale (coda.hale@gmail.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package xbr.network.crypto; - -import java.nio.charset.StandardCharsets; -import org.bouncycastle.crypto.engines.Salsa20Engine; -import org.bouncycastle.util.Pack; - -/** An implementation of the HSalsa20 hash based on the Bouncy Castle Salsa20 core. */ -class HSalsa20 { - - private static final byte[] SIGMA = "expand 32-byte k".getBytes(StandardCharsets.US_ASCII); - private static final int SIGMA_0 = Pack.littleEndianToInt(SIGMA, 0); - private static final int SIGMA_4 = Pack.littleEndianToInt(SIGMA, 4); - private static final int SIGMA_8 = Pack.littleEndianToInt(SIGMA, 8); - private static final int SIGMA_12 = Pack.littleEndianToInt(SIGMA, 12); - - static void hsalsa20(byte[] out, byte[] in, byte[] k) { - final int[] x = new int[16]; - - final int in0 = Pack.littleEndianToInt(in, 0); - final int in4 = Pack.littleEndianToInt(in, 4); - final int in8 = Pack.littleEndianToInt(in, 8); - final int in12 = Pack.littleEndianToInt(in, 12); - - x[0] = SIGMA_0; - x[1] = Pack.littleEndianToInt(k, 0); - x[2] = Pack.littleEndianToInt(k, 4); - x[3] = Pack.littleEndianToInt(k, 8); - x[4] = Pack.littleEndianToInt(k, 12); - x[5] = SIGMA_4; - x[6] = in0; - x[7] = in4; - x[8] = in8; - x[9] = in12; - x[10] = SIGMA_8; - x[11] = Pack.littleEndianToInt(k, 16); - x[12] = Pack.littleEndianToInt(k, 20); - x[13] = Pack.littleEndianToInt(k, 24); - x[14] = Pack.littleEndianToInt(k, 28); - x[15] = SIGMA_12; - - Salsa20Engine.salsaCore(20, x, x); - - x[0] -= SIGMA_0; - x[5] -= SIGMA_4; - x[10] -= SIGMA_8; - x[15] -= SIGMA_12; - x[6] -= in0; - x[7] -= in4; - x[8] -= in8; - x[9] -= in12; - - Pack.intToLittleEndian(x[0], out, 0); - Pack.intToLittleEndian(x[5], out, 4); - Pack.intToLittleEndian(x[10], out, 8); - Pack.intToLittleEndian(x[15], out, 12); - Pack.intToLittleEndian(x[6], out, 16); - Pack.intToLittleEndian(x[7], out, 20); - Pack.intToLittleEndian(x[8], out, 24); - Pack.intToLittleEndian(x[9], out, 28); - } -} diff --git a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java index 940994ef..1969dcfd 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SealedBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SealedBox.java @@ -1,15 +1,7 @@ package xbr.network.crypto; -import org.bouncycastle.crypto.digests.Blake2bDigest; -import org.bouncycastle.crypto.engines.XSalsa20Engine; -import org.bouncycastle.crypto.macs.Poly1305; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.math.ec.rfc7748.X25519; -import org.bouncycastle.util.Arrays; - -import io.crossbar.autobahn.utils.Pair; -import xbr.network.Util; +import static io.xconn.cryptology.SealedBox.seal; +import static io.xconn.cryptology.SealedBox.sealOpen; public class SealedBox { @@ -42,60 +34,10 @@ public SealedBox(byte[] publicKey, byte[] privateKey) { } public byte[] encrypt(byte[] message) { - Pair keyPair = Util.generateX25519KeyPair(); - byte[] nonce = createNonce(keyPair.first, publicKey); - byte[] sharedSecret = computeSharedSecret(publicKey, keyPair.second); - - XSalsa20Engine cipher = new XSalsa20Engine(); - ParametersWithIV params = new ParametersWithIV(new KeyParameter(sharedSecret), nonce); - cipher.init(true, params); - - byte[] sk = new byte[Util.SECRET_KEY_LEN]; - cipher.processBytes(sk, 0, sk.length, sk, 0); - - // encrypt the message - byte[] ciphertext = new byte[message.length]; - cipher.processBytes(message, 0, message.length, ciphertext, 0); - - // create the MAC - Poly1305 mac = new Poly1305(); - byte[] macBuf = new byte[mac.getMacSize()]; - mac.init(new KeyParameter(sk)); - mac.update(ciphertext, 0, ciphertext.length); - mac.doFinal(macBuf, 0); - - return Arrays.concatenate(keyPair.first, macBuf, ciphertext); - } - - private byte[] createNonce(byte[] ephemeralPublicKey, byte[] recipientPublicKey) { - Blake2bDigest blake2b = new Blake2bDigest(Util.NONCE_SIZE * 8); - byte[] nonce = new byte[blake2b.getDigestSize()]; - - blake2b.update(ephemeralPublicKey, 0, ephemeralPublicKey.length); - blake2b.update(recipientPublicKey, 0, recipientPublicKey.length); - - blake2b.doFinal(nonce, 0); - - return nonce; - } - - public byte[] computeSharedSecret(byte[] publicKey, byte[] privateKey) { - byte[] sharedSecret = new byte[32]; - // compute the raw shared secret - X25519.scalarMult(privateKey, 0, publicKey, 0, sharedSecret, 0); - // encrypt the shared secret - byte[] key = new byte[32]; - HSalsa20.hsalsa20(key, HSALSA20_SEED, sharedSecret); - return key; + return seal(message, publicKey); } public byte[] decrypt(byte[] message) { - byte[] ephemeralPublicKey = Arrays.copyOf(message, PUBLICKEY_BYTES); - byte[] ciphertext = Arrays.copyOfRange(message, PUBLICKEY_BYTES, message.length); - byte[] nonce = createNonce(ephemeralPublicKey, publicKey); - byte[] sharedSecret = computeSharedSecret(ephemeralPublicKey, privateKey); - - SecretBox box = new SecretBox(sharedSecret); - return box.decrypt(nonce, ciphertext); + return sealOpen(message, privateKey); } } diff --git a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java index b998d80e..9fa5e9dd 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java @@ -1,16 +1,13 @@ package xbr.network.crypto; -import static xbr.network.Util.NONCE_SIZE; -import static xbr.network.Util.SECRET_KEY_LEN; -import static xbr.network.Util.generateRandomBytesArray; +import java.util.Arrays; -import org.bouncycastle.crypto.engines.XSalsa20Engine; -import org.bouncycastle.crypto.macs.Poly1305; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; +import static io.xconn.cryptology.SecretBox.box; +import static io.xconn.cryptology.SecretBox.boxOpen; +import static io.xconn.cryptology.Util.generateRandomBytesArray; -import java.security.MessageDigest; -import java.util.Arrays; +import static xbr.network.Util.NONCE_SIZE; +import static xbr.network.Util.SECRET_KEY_LEN; public class SecretBox { private final byte[] mKey; @@ -26,66 +23,17 @@ public byte[] encrypt(byte[] message) { } public byte[] encrypt(byte[] nonce, byte[] plaintext) { - checkLength(nonce, NONCE_SIZE); - - XSalsa20Engine xsalsa20 = new XSalsa20Engine(); - Poly1305 poly1305 = new Poly1305(); - - xsalsa20.init(true, new ParametersWithIV(new KeyParameter(mKey), nonce)); - byte[] subKey = new byte[SECRET_KEY_LEN]; - xsalsa20.processBytes(subKey, 0, SECRET_KEY_LEN, subKey, 0); - byte[] cipherWithoutNonce = new byte[plaintext.length + poly1305.getMacSize()]; - xsalsa20.processBytes(plaintext, 0, plaintext.length, cipherWithoutNonce, poly1305.getMacSize()); - - // hash ciphertext and prepend mac to ciphertext - poly1305.init(new KeyParameter(subKey)); - poly1305.update(cipherWithoutNonce, poly1305.getMacSize(), plaintext.length); - poly1305.doFinal(cipherWithoutNonce, 0); - - byte[] ciphertext = new byte[cipherWithoutNonce.length + - NONCE_SIZE]; + byte[] cipherWithoutNonce = box(nonce, plaintext, mKey); + byte[] ciphertext = new byte[cipherWithoutNonce.length + NONCE_SIZE]; System.arraycopy(nonce, 0, ciphertext, 0, nonce.length); - System.arraycopy(cipherWithoutNonce, 0, ciphertext, nonce.length, - cipherWithoutNonce.length); + System.arraycopy(cipherWithoutNonce, 0, ciphertext, nonce.length, cipherWithoutNonce.length); return ciphertext; } public byte[] decrypt(byte[] ciphertext) { byte[] nonce = Arrays.copyOfRange(ciphertext, 0, NONCE_SIZE); - byte[] message = Arrays.copyOfRange(ciphertext, NONCE_SIZE, - ciphertext.length); - return decrypt(nonce, message); - } - - public byte[] decrypt(byte[] nonce, byte[] ciphertext) { - checkLength(nonce, NONCE_SIZE); - - XSalsa20Engine xsalsa20 = new XSalsa20Engine(); - Poly1305 poly1305 = new Poly1305(); - - xsalsa20.init(false, new ParametersWithIV(new KeyParameter(mKey), nonce)); - byte[] sk = new byte[SECRET_KEY_LEN]; - xsalsa20.processBytes(sk, 0, sk.length, sk, 0); - - // hash ciphertext - poly1305.init(new KeyParameter(sk)); - int len = Math.max(ciphertext.length - poly1305.getMacSize(), 0); - poly1305.update(ciphertext, poly1305.getMacSize(), len); - byte[] calculatedMAC = new byte[poly1305.getMacSize()]; - poly1305.doFinal(calculatedMAC, 0); - - // extract mac - final byte[] presentedMAC = new byte[poly1305.getMacSize()]; - System.arraycopy( - ciphertext, 0, presentedMAC, 0, Math.min(ciphertext.length, poly1305.getMacSize())); - - if (!MessageDigest.isEqual(calculatedMAC, presentedMAC)) { - throw new IllegalArgumentException("Invalid MAC"); - } - - byte[] plaintext = new byte[len]; - xsalsa20.processBytes(ciphertext, poly1305.getMacSize(), plaintext.length, plaintext, 0); - return plaintext; + byte[] message = Arrays.copyOfRange(ciphertext, NONCE_SIZE, ciphertext.length); + return boxOpen(nonce, message, mKey); } private void checkLength(byte[] data, int size) { diff --git a/autobahn/src/test/java/xbr/network/UtilTest.java b/autobahn/src/test/java/xbr/network/UtilTest.java deleted file mode 100644 index 13182ea5..00000000 --- a/autobahn/src/test/java/xbr/network/UtilTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package xbr.network; - -import static junit.framework.TestCase.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -import io.crossbar.autobahn.utils.Pair; - -public class UtilTest { - - @Test - public void testGenerateRandomBytesArray() { - int size = 32; - byte[] randomBytes = Util.generateRandomBytesArray(size); - - assertNotNull(randomBytes); - assertEquals(size, randomBytes.length); - } - - @Test - public void testGenerateKeyPair() { - Pair keyPair = Util.generateX25519KeyPair(); - - assertNotNull(keyPair); - assertNotNull(keyPair.first); - assertNotNull(keyPair.second); - - assertEquals(32, keyPair.first.length); - assertEquals(32, keyPair.second.length); - } -} diff --git a/autobahn/src/test/java/xbr/network/crypto/SealedBoxTest.java b/autobahn/src/test/java/xbr/network/crypto/SealedBoxTest.java new file mode 100644 index 00000000..97d5c632 --- /dev/null +++ b/autobahn/src/test/java/xbr/network/crypto/SealedBoxTest.java @@ -0,0 +1,43 @@ +package xbr.network.crypto; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; + +import org.bouncycastle.util.encoders.Hex; +import org.junit.Before; +import org.junit.Test; + +public class SealedBoxTest { + + private static byte[] publicKey; + private static byte[] privateKey; + + @Before + public void setUp() { + publicKey = Hex.decode("e146721761cf7378cb2e007adc1a51b70fa40abfb87652c645d8e86be19c2b1e"); + privateKey = Hex.decode("3817e2630237d569188a02a06354d9e9f61ee9cdd0cc8b5388c56013b7b5654a"); + } + @Test + public void testEncryptDecrypt() { + SealedBox sealedBox = new SealedBox(publicKey, privateKey); + + String message = "Hello, world!"; + byte[] encrypted = sealedBox.encrypt(message.getBytes()); + byte[] decrypted = sealedBox.decrypt(encrypted); + + assertArrayEquals(message.getBytes(), decrypted); + } + + @Test + public void testNullPublicKey() { + assertThrows(IllegalArgumentException.class, () -> new SealedBox(null)); + } + + @Test + public void testNullPrivateKey() { + byte[] publicKey = Hex.decode("1eb32ea638c250f7b781b7a0d29d0c1b3456d7a3428ff9c7a4a64d75db709709"); + + assertThrows(IllegalArgumentException.class, () -> new SealedBox(publicKey, null)); + } + +} diff --git a/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java b/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java index 4b562955..3927dac5 100644 --- a/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java +++ b/autobahn/src/test/java/xbr/network/crypto/SecretBoxTest.java @@ -2,8 +2,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertThrows; +import static io.xconn.cryptology.Util.generateRandomBytesArray; + import static xbr.network.Util.NONCE_SIZE; -import static xbr.network.Util.generateRandomBytesArray; import org.junit.Test; diff --git a/demo-gallery/build.gradle b/demo-gallery/build.gradle index ac4d47f5..d30934fd 100644 --- a/demo-gallery/build.gradle +++ b/demo-gallery/build.gradle @@ -40,7 +40,9 @@ if (plugins.hasPlugin(project.PLUGIN_ANDROID_APP)) { } dependencies { - implementation project(path: ':autobahn') + implementation(project(path: ':autobahn')){ + exclude group: "org.bouncycastle", module: "bcprov-jdk18on" + } implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.android.support:multidex:1.0.3' implementation "androidx.constraintlayout:constraintlayout:2.1.4" From e0a1b0d848f265a6ef56ade9cf91fabbf85e4c6b Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 6 Mar 2024 18:48:32 +0500 Subject: [PATCH 19/21] exclude bouncy-castle from cryptology --- autobahn/build.gradle | 4 +++- demo-gallery/build.gradle | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index 7561f474..edff1f09 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -24,7 +24,9 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' - implementation 'io.xconn:cryptology:0.1.0' + implementation('io.xconn:cryptology:0.1.0') { + exclude group: "org.bouncycastle", module: "bcprov-jdk18on" + } if (IS_ANDROID) { implementation 'org.web3j:core:4.8.8-android' implementation 'org.web3j:abi:4.8.8-android' diff --git a/demo-gallery/build.gradle b/demo-gallery/build.gradle index d30934fd..ac4d47f5 100644 --- a/demo-gallery/build.gradle +++ b/demo-gallery/build.gradle @@ -40,9 +40,7 @@ if (plugins.hasPlugin(project.PLUGIN_ANDROID_APP)) { } dependencies { - implementation(project(path: ':autobahn')){ - exclude group: "org.bouncycastle", module: "bcprov-jdk18on" - } + implementation project(path: ':autobahn') implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.android.support:multidex:1.0.3' implementation "androidx.constraintlayout:constraintlayout:2.1.4" From 9bf9cf5b50b95cc8888311c8f1bba4e0b054089f Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 6 Mar 2024 19:46:47 +0500 Subject: [PATCH 20/21] remove unnecessary checkLength() function --- autobahn/src/main/java/xbr/network/crypto/SecretBox.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java index 9fa5e9dd..74f4688b 100644 --- a/autobahn/src/main/java/xbr/network/crypto/SecretBox.java +++ b/autobahn/src/main/java/xbr/network/crypto/SecretBox.java @@ -4,6 +4,7 @@ import static io.xconn.cryptology.SecretBox.box; import static io.xconn.cryptology.SecretBox.boxOpen; +import static io.xconn.cryptology.Util.checkLength; import static io.xconn.cryptology.Util.generateRandomBytesArray; import static xbr.network.Util.NONCE_SIZE; @@ -35,9 +36,4 @@ public byte[] decrypt(byte[] ciphertext) { byte[] message = Arrays.copyOfRange(ciphertext, NONCE_SIZE, ciphertext.length); return boxOpen(nonce, message, mKey); } - - private void checkLength(byte[] data, int size) { - if (data == null || data.length != size) - throw new IllegalArgumentException("Invalid size: " + data.length); - } } From 3ebb097435055930fafb6060961096610d92d148 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 20 Mar 2024 15:45:52 +0500 Subject: [PATCH 21/21] update cryptology --- autobahn/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autobahn/build.gradle b/autobahn/build.gradle index edff1f09..729bd8c8 100644 --- a/autobahn/build.gradle +++ b/autobahn/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.1' implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.8' - implementation('io.xconn:cryptology:0.1.0') { + implementation('io.xconn:cryptology:1.0.1') { exclude group: "org.bouncycastle", module: "bcprov-jdk18on" } if (IS_ANDROID) {