From ca591b5c7b95f9ff9492c55bf38df0588b1b7cd4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 14 May 2019 11:32:07 +0100 Subject: [PATCH 1/2] Add test for equivalent public keys. --- .../bramble/crypto/KeyAgreementTest.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) 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 92bb9fc38..ffdb8a43a 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 @@ -11,12 +11,14 @@ import org.junit.Test; import org.whispersystems.curve25519.Curve25519; import java.security.GeneralSecurityException; +import java.util.Arrays; import java.util.Random; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; public class KeyAgreementTest extends BrambleTestCase { @@ -66,11 +68,9 @@ public class KeyAgreementTest extends BrambleTestCase { @Test public void testRfc7748TestVector() { - // Private keys need to be clamped because curve25519-java does the - // clamping at key generation time, not multiplication time - byte[] aPriv = AgreementKeyParser.clamp(fromHexString(ALICE_PRIVATE)); + byte[] aPriv = parsePrivateKey(ALICE_PRIVATE); byte[] aPub = fromHexString(ALICE_PUBLIC); - byte[] bPriv = AgreementKeyParser.clamp(fromHexString(BOB_PRIVATE)); + byte[] bPriv = parsePrivateKey(BOB_PRIVATE); byte[] bPub = fromHexString(BOB_PUBLIC); byte[] sharedSecret = fromHexString(SHARED_SECRET); Curve25519 curve25519 = Curve25519.getInstance("java"); @@ -79,4 +79,29 @@ public class KeyAgreementTest extends BrambleTestCase { assertArrayEquals(sharedSecret, curve25519.calculateAgreement(bPub, aPriv)); } + + @Test + public void testDerivesSameSharedSecretFromEquivalentPublicKey() { + byte[] aPub = fromHexString(ALICE_PUBLIC); + byte[] bPriv = parsePrivateKey(BOB_PRIVATE); + byte[] sharedSecret = fromHexString(SHARED_SECRET); + Curve25519 curve25519 = Curve25519.getInstance("java"); + + // Flip the unused most significant bit of the little-endian public key + byte[] aPubEquivalent = aPub.clone(); + aPubEquivalent[31] ^= (byte) 128; + + // The public keys should be different but give the same shared secret + assertFalse(Arrays.equals(aPub, aPubEquivalent)); + assertArrayEquals(sharedSecret, + curve25519.calculateAgreement(aPub, bPriv)); + assertArrayEquals(sharedSecret, + curve25519.calculateAgreement(aPubEquivalent, bPriv)); + } + + private byte[] parsePrivateKey(String hex) { + // Private keys need to be clamped because curve25519-java does the + // clamping at key generation time, not multiplication time + return AgreementKeyParser.clamp(fromHexString(hex)); + } } From 9d64b186ffb4baece210344a0e0b7ee0cdee8996 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 14 May 2019 12:12:58 +0100 Subject: [PATCH 2/2] Add tests for hashing public keys into shared secret. --- .../bramble/crypto/KeyAgreementTest.java | 71 ++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) 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 ffdb8a43a..0836b058e 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 @@ -14,9 +14,9 @@ import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Random; -import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.util.StringUtils.fromHexString; +import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; @@ -38,6 +38,7 @@ public class KeyAgreementTest extends BrambleTestCase { private final CryptoComponent crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null); + private final String label = getRandomString(123); private final byte[][] inputs; public KeyAgreementTest() { @@ -51,9 +52,9 @@ public class KeyAgreementTest extends BrambleTestCase { public void testDerivesSharedSecret() throws Exception { KeyPair aPair = crypto.generateAgreementKeyPair(); KeyPair bPair = crypto.generateAgreementKeyPair(); - SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + SecretKey aShared = crypto.deriveSharedSecret(label, bPair.getPublic(), aPair, inputs); - SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + SecretKey bShared = crypto.deriveSharedSecret(label, aPair.getPublic(), bPair, inputs); assertArrayEquals(aShared.getBytes(), bShared.getBytes()); } @@ -62,8 +63,7 @@ public class KeyAgreementTest extends BrambleTestCase { public void testRejectsInvalidPublicKey() throws Exception { KeyPair keyPair = crypto.generateAgreementKeyPair(); PublicKey invalid = new AgreementPublicKey(new byte[32]); - crypto.deriveSharedSecret(SHARED_SECRET_LABEL, invalid, keyPair, - inputs); + crypto.deriveSharedSecret(label, invalid, keyPair, inputs); } @Test @@ -88,15 +88,68 @@ public class KeyAgreementTest extends BrambleTestCase { Curve25519 curve25519 = Curve25519.getInstance("java"); // Flip the unused most significant bit of the little-endian public key - byte[] aPubEquivalent = aPub.clone(); - aPubEquivalent[31] ^= (byte) 128; + byte[] aPubEquiv = aPub.clone(); + aPubEquiv[31] ^= (byte) 128; // The public keys should be different but give the same shared secret - assertFalse(Arrays.equals(aPub, aPubEquivalent)); + assertFalse(Arrays.equals(aPub, aPubEquiv)); assertArrayEquals(sharedSecret, curve25519.calculateAgreement(aPub, bPriv)); assertArrayEquals(sharedSecret, - curve25519.calculateAgreement(aPubEquivalent, bPriv)); + curve25519.calculateAgreement(aPubEquiv, bPriv)); + } + + @Test + public void testDerivesSameSharedSecretFromEquivalentPublicKeyWithoutPublicKeysHashedIn() + throws Exception { + KeyPair aPair = crypto.generateAgreementKeyPair(); + KeyPair bPair = crypto.generateAgreementKeyPair(); + + // Flip the unused most significant bit of the little-endian public key + byte[] aPub = aPair.getPublic().getEncoded(); + byte[] aPubEquiv = aPub.clone(); + aPubEquiv[31] ^= (byte) 128; + KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv), + aPair.getPrivate()); + + // The public keys should be different but give the same shared secret + assertFalse(Arrays.equals(aPub, aPubEquiv)); + SecretKey shared = crypto.deriveSharedSecret(label, + aPair.getPublic(), bPair); + SecretKey sharedEquiv = crypto.deriveSharedSecret(label, + aPairEquiv.getPublic(), bPair); + assertArrayEquals(shared.getBytes(), sharedEquiv.getBytes()); + } + + @Test + public void testDerivesDifferentSharedSecretFromEquivalentPublicKeyWithPublicKeysHashedIn() + throws Exception { + KeyPair aPair = crypto.generateAgreementKeyPair(); + KeyPair bPair = crypto.generateAgreementKeyPair(); + + // Flip the unused most significant bit of the little-endian public key + byte[] aPub = aPair.getPublic().getEncoded(); + byte[] aPubEquiv = aPub.clone(); + aPubEquiv[31] ^= (byte) 128; + KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv), + aPair.getPrivate()); + + // The public keys should be different and give different shared secrets + assertFalse(Arrays.equals(aPub, aPubEquiv)); + SecretKey shared = deriveSharedSecretWithPublicKeysHashedIn(label, + aPair.getPublic(), bPair); + SecretKey sharedEquiv = deriveSharedSecretWithPublicKeysHashedIn(label, + aPairEquiv.getPublic(), bPair); + assertFalse(Arrays.equals(shared.getBytes(), sharedEquiv.getBytes())); + } + + private SecretKey deriveSharedSecretWithPublicKeysHashedIn(String label, + PublicKey publicKey, KeyPair keyPair) throws Exception { + byte[][] inputs = new byte[][] { + publicKey.getEncoded(), + keyPair.getPublic().getEncoded() + }; + return crypto.deriveSharedSecret(label, publicKey, keyPair, inputs); } private byte[] parsePrivateKey(String hex) {