From 44d2526997210836a21f04a29b294b719d70ec95 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 15 Mar 2019 14:04:16 +0000 Subject: [PATCH 1/2] Add triple Diffie-Hellman key agreement. --- .../bramble/api/crypto/CryptoComponent.java | 22 +++++++++- .../bramble/crypto/CryptoComponentImpl.java | 44 ++++++++++++++++--- .../bramble/crypto/KeyAgreementTest.java | 16 +++++++ 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java index 4ad811524..52166eaf9 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java @@ -34,8 +34,7 @@ public interface CryptoComponent { SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); /** - * Derives a common shared secret from two public keys and one of the - * corresponding private keys. + * Derives a shared secret from two key pairs. * * @param label a namespaced label indicating the purpose of this shared * secret, to prevent it from being repurposed or colliding with a shared @@ -48,6 +47,25 @@ public interface CryptoComponent { KeyPair ourKeyPair, byte[]... inputs) throws GeneralSecurityException; + /** + * Derives a shared secret from two static and two ephemeral key pairs. + * + * @param label a namespaced label indicating the purpose of this shared + * secret, to prevent it from being repurposed or colliding with a shared + * secret derived for another purpose + * @param theirStaticPublicKey the static public key of the remote party + * @param theirEphemeralPublicKey the ephemeral public key of the remote + * party + * @param ourStaticKeyPair the static key pair of the local party + * @param ourEphemeralKeyPair the ephemeral key pair of the local party + * @param alice true if the local party is Alice + * @return the shared secret + */ + SecretKey deriveSharedSecret(String label, PublicKey theirStaticPublicKey, + PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair, + KeyPair ourEphemeralKeyPair, boolean alice, byte[]... inputs) + throws GeneralSecurityException; + /** * Signs the given byte[] with the given private key. * diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java index d1cad3869..37aa5de3d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java @@ -34,6 +34,7 @@ import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; +import static java.lang.System.arraycopy; import static java.util.logging.Level.INFO; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE; @@ -191,10 +192,39 @@ class CryptoComponentImpl implements CryptoComponent { public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, KeyPair ourKeyPair, byte[]... inputs) throws GeneralSecurityException { - PrivateKey ourPriv = ourKeyPair.getPrivate(); + PrivateKey ourPrivateKey = ourKeyPair.getPrivate(); byte[][] hashInputs = new byte[inputs.length + 1][]; - hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey); - System.arraycopy(inputs, 0, hashInputs, 1, inputs.length); + hashInputs[0] = performRawKeyAgreement(ourPrivateKey, theirPublicKey); + arraycopy(inputs, 0, hashInputs, 1, inputs.length); + byte[] hash = hash(label, hashInputs); + if (hash.length != SecretKey.LENGTH) throw new IllegalStateException(); + return new SecretKey(hash); + } + + @Override + public SecretKey deriveSharedSecret(String label, + PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey, + KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair, + boolean alice, byte[]... inputs) throws GeneralSecurityException { + PrivateKey ourStaticPrivateKey = ourStaticKeyPair.getPrivate(); + PrivateKey ourEphemeralPrivateKey = ourEphemeralKeyPair.getPrivate(); + byte[][] hashInputs = new byte[inputs.length + 3][]; + // Alice static/Bob static + hashInputs[0] = performRawKeyAgreement(ourStaticPrivateKey, + theirStaticPublicKey); + // Alice static/Bob ephemeral, Bob static/Alice ephemeral + if (alice) { + hashInputs[1] = performRawKeyAgreement(ourStaticPrivateKey, + theirEphemeralPublicKey); + hashInputs[2] = performRawKeyAgreement(ourEphemeralPrivateKey, + theirStaticPublicKey); + } else { + hashInputs[1] = performRawKeyAgreement(ourEphemeralPrivateKey, + theirStaticPublicKey); + hashInputs[2] = performRawKeyAgreement(ourStaticPrivateKey, + theirEphemeralPublicKey); + } + arraycopy(inputs, 0, hashInputs, 3, inputs.length); byte[] hash = hash(label, hashInputs); if (hash.length != SecretKey.LENGTH) throw new IllegalStateException(); return new SecretKey(hash); @@ -304,13 +334,13 @@ class CryptoComponentImpl implements CryptoComponent { output[outputOff] = PBKDF_FORMAT_SCRYPT; outputOff++; // Salt - System.arraycopy(salt, 0, output, outputOff, salt.length); + arraycopy(salt, 0, output, outputOff, salt.length); outputOff += salt.length; // Cost parameter ByteUtils.writeUint32(cost, output, outputOff); outputOff += INT_32_BYTES; // IV - System.arraycopy(iv, 0, output, outputOff, iv.length); + arraycopy(iv, 0, output, outputOff, iv.length); outputOff += iv.length; // Initialise the cipher and encrypt the plaintext try { @@ -340,7 +370,7 @@ class CryptoComponentImpl implements CryptoComponent { return null; // Unknown format // Salt byte[] salt = new byte[PBKDF_SALT_BYTES]; - System.arraycopy(input, inputOff, salt, 0, salt.length); + arraycopy(input, inputOff, salt, 0, salt.length); inputOff += salt.length; // Cost parameter long cost = ByteUtils.readUint32(input, inputOff); @@ -349,7 +379,7 @@ class CryptoComponentImpl implements CryptoComponent { return null; // Invalid cost parameter // IV byte[] iv = new byte[STORAGE_IV_BYTES]; - System.arraycopy(input, inputOff, iv, 0, iv.length); + arraycopy(input, inputOff, iv, 0, iv.length); inputOff += iv.length; // Derive the key from the password SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyAgreementTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyAgreementTest.java index 0836b058e..aab957a8a 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyAgreementTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyAgreementTest.java @@ -59,6 +59,22 @@ public class KeyAgreementTest extends BrambleTestCase { assertArrayEquals(aShared.getBytes(), bShared.getBytes()); } + @Test + public void testDerivesStaticEphemeralSharedSecret() throws Exception { + String label = getRandomString(123); + KeyPair aStatic = crypto.generateAgreementKeyPair(); + KeyPair aEphemeral = crypto.generateAgreementKeyPair(); + KeyPair bStatic = crypto.generateAgreementKeyPair(); + KeyPair bEphemeral = crypto.generateAgreementKeyPair(); + SecretKey aShared = crypto.deriveSharedSecret(label, + bStatic.getPublic(), bEphemeral.getPublic(), aStatic, + aEphemeral, true, inputs); + SecretKey bShared = crypto.deriveSharedSecret(label, + aStatic.getPublic(), aEphemeral.getPublic(), bStatic, + bEphemeral, false, inputs); + assertArrayEquals(aShared.getBytes(), bShared.getBytes()); + } + @Test(expected = GeneralSecurityException.class) public void testRejectsInvalidPublicKey() throws Exception { KeyPair keyPair = crypto.generateAgreementKeyPair(); From 819deca93cfa9e0cec4cd8c3575f8ea0b0a74e0e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 27 May 2019 17:54:23 +0100 Subject: [PATCH 2/2] Update javadoc. --- .../bramble/api/crypto/CryptoComponent.java | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java index 52166eaf9..9d498ec9d 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java @@ -27,21 +27,25 @@ public interface CryptoComponent { /** * Derives another secret key from the given secret key. * - * @param label a namespaced label indicating the purpose of the derived + * @param label A namespaced label indicating the purpose of the derived * key, to prevent it from being repurposed or colliding with a key derived * for another purpose + * @param inputs Additional inputs that will be included in the derivation + * of the key */ SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); /** * Derives a shared secret from two key pairs. * - * @param label a namespaced label indicating the purpose of this shared + * @param label A namespaced label indicating the purpose of this shared * secret, to prevent it from being repurposed or colliding with a shared * secret derived for another purpose - * @param theirPublicKey the public key of the remote party - * @param ourKeyPair the key pair of the local party - * @return the shared secret + * @param theirPublicKey The public key of the remote party + * @param ourKeyPair The key pair of the local party + * @param inputs Additional inputs that will be included in the derivation + * of the shared secret + * @return The shared secret */ SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, KeyPair ourKeyPair, byte[]... inputs) @@ -50,16 +54,18 @@ public interface CryptoComponent { /** * Derives a shared secret from two static and two ephemeral key pairs. * - * @param label a namespaced label indicating the purpose of this shared + * @param label A namespaced label indicating the purpose of this shared * secret, to prevent it from being repurposed or colliding with a shared * secret derived for another purpose - * @param theirStaticPublicKey the static public key of the remote party - * @param theirEphemeralPublicKey the ephemeral public key of the remote + * @param theirStaticPublicKey The static public key of the remote party + * @param theirEphemeralPublicKey The ephemeral public key of the remote * party - * @param ourStaticKeyPair the static key pair of the local party - * @param ourEphemeralKeyPair the ephemeral key pair of the local party - * @param alice true if the local party is Alice - * @return the shared secret + * @param ourStaticKeyPair The static key pair of the local party + * @param ourEphemeralKeyPair The ephemeral key pair of the local party + * @param alice True if the local party is Alice + * @param inputs Additional inputs that will be included in the + * derivation of the shared secret + * @return The shared secret */ SecretKey deriveSharedSecret(String label, PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair, @@ -69,7 +75,7 @@ public interface CryptoComponent { /** * Signs the given byte[] with the given private key. * - * @param label a namespaced label indicating the purpose of this + * @param label A namespaced label indicating the purpose of this * signature, to prevent it from being repurposed or colliding with a * signature created for another purpose */ @@ -80,10 +86,10 @@ public interface CryptoComponent { * Verifies that the given signature is valid for the signed data * and the given public key. * - * @param label a namespaced label indicating the purpose of this + * @param label A namespaced label indicating the purpose of this * signature, to prevent it from being repurposed or colliding with a * signature created for another purpose - * @return true if the signature was valid, false otherwise. + * @return True if the signature was valid, false otherwise. */ boolean verifySignature(byte[] signature, String label, byte[] signed, PublicKey publicKey) throws GeneralSecurityException; @@ -92,7 +98,7 @@ public interface CryptoComponent { * Returns the hash of the given inputs. The inputs are unambiguously * combined by prefixing each input with its length. * - * @param label a namespaced label indicating the purpose of this hash, to + * @param label A namespaced label indicating the purpose of this hash, to * prevent it from being repurposed or colliding with a hash created for * another purpose */ @@ -103,7 +109,7 @@ public interface CryptoComponent { * given inputs. The inputs are unambiguously combined by prefixing each * input with its length. * - * @param label a namespaced label indicating the purpose of this MAC, to + * @param label A namespaced label indicating the purpose of this MAC, to * prevent it from being repurposed or colliding with a MAC created for * another purpose */ @@ -113,10 +119,10 @@ public interface CryptoComponent { * Verifies that the given message authentication code is valid for the * given secret key and inputs. * - * @param label a namespaced label indicating the purpose of this MAC, to + * @param label A namespaced label indicating the purpose of this MAC, to * prevent it from being repurposed or colliding with a MAC created for * another purpose - * @return true if the MAC was valid, false otherwise. + * @return True if the MAC was valid, false otherwise. */ boolean verifyMac(byte[] mac, String label, SecretKey macKey, byte[]... inputs);