From 838b6b8900a143aa427eed7f60e6cc4d06fa1649 Mon Sep 17 00:00:00 2001 From: Omer Akram Date: Mon, 26 Feb 2024 13:28:40 +0500 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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);