mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Merge branch '1232-triple-dh' into 'master'
Implement triple Diffie-Hellman key agreement See merge request briar/briar!1108
This commit is contained in:
@@ -27,31 +27,55 @@ public interface CryptoComponent {
|
|||||||
/**
|
/**
|
||||||
* Derives another secret key from the given secret key.
|
* 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
|
* key, to prevent it from being repurposed or colliding with a key derived
|
||||||
* for another purpose
|
* 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);
|
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a common shared secret from two public keys and one of the
|
* Derives a shared secret from two key pairs.
|
||||||
* corresponding private keys.
|
|
||||||
*
|
*
|
||||||
* @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, to prevent it from being repurposed or colliding with a shared
|
||||||
* secret derived for another purpose
|
* secret derived for another purpose
|
||||||
* @param theirPublicKey the public key of the remote party
|
* @param theirPublicKey The public key of the remote party
|
||||||
* @param ourKeyPair the key pair of the local party
|
* @param ourKeyPair The key pair of the local party
|
||||||
* @return the shared secret
|
* @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,
|
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
KeyPair ourKeyPair, byte[]... inputs)
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
throws GeneralSecurityException;
|
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
|
||||||
|
* @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,
|
||||||
|
KeyPair ourEphemeralKeyPair, boolean alice, byte[]... inputs)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given byte[] with the given private key.
|
* 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, to prevent it from being repurposed or colliding with a
|
||||||
* signature created for another purpose
|
* signature created for another purpose
|
||||||
*/
|
*/
|
||||||
@@ -62,10 +86,10 @@ public interface CryptoComponent {
|
|||||||
* Verifies that the given signature is valid for the signed data
|
* Verifies that the given signature is valid for the signed data
|
||||||
* and the given public key.
|
* 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, to prevent it from being repurposed or colliding with a
|
||||||
* signature created for another purpose
|
* 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,
|
boolean verifySignature(byte[] signature, String label, byte[] signed,
|
||||||
PublicKey publicKey) throws GeneralSecurityException;
|
PublicKey publicKey) throws GeneralSecurityException;
|
||||||
@@ -74,7 +98,7 @@ public interface CryptoComponent {
|
|||||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
* combined by prefixing each input with its length.
|
* 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
|
* prevent it from being repurposed or colliding with a hash created for
|
||||||
* another purpose
|
* another purpose
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +109,7 @@ public interface CryptoComponent {
|
|||||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||||
* input with its length.
|
* 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
|
* prevent it from being repurposed or colliding with a MAC created for
|
||||||
* another purpose
|
* another purpose
|
||||||
*/
|
*/
|
||||||
@@ -95,10 +119,10 @@ public interface CryptoComponent {
|
|||||||
* Verifies that the given message authentication code is valid for the
|
* Verifies that the given message authentication code is valid for the
|
||||||
* given secret key and inputs.
|
* 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
|
* prevent it from being repurposed or colliding with a MAC created for
|
||||||
* another purpose
|
* 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,
|
boolean verifyMac(byte[] mac, String label, SecretKey macKey,
|
||||||
byte[]... inputs);
|
byte[]... inputs);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
import static java.util.logging.Level.INFO;
|
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_AGREEMENT;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
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,
|
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
KeyPair ourKeyPair, byte[]... inputs)
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
PrivateKey ourPrivateKey = ourKeyPair.getPrivate();
|
||||||
byte[][] hashInputs = new byte[inputs.length + 1][];
|
byte[][] hashInputs = new byte[inputs.length + 1][];
|
||||||
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
|
hashInputs[0] = performRawKeyAgreement(ourPrivateKey, theirPublicKey);
|
||||||
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
|
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);
|
byte[] hash = hash(label, hashInputs);
|
||||||
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
return new SecretKey(hash);
|
return new SecretKey(hash);
|
||||||
@@ -304,13 +334,13 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
output[outputOff] = PBKDF_FORMAT_SCRYPT;
|
output[outputOff] = PBKDF_FORMAT_SCRYPT;
|
||||||
outputOff++;
|
outputOff++;
|
||||||
// Salt
|
// Salt
|
||||||
System.arraycopy(salt, 0, output, outputOff, salt.length);
|
arraycopy(salt, 0, output, outputOff, salt.length);
|
||||||
outputOff += salt.length;
|
outputOff += salt.length;
|
||||||
// Cost parameter
|
// Cost parameter
|
||||||
ByteUtils.writeUint32(cost, output, outputOff);
|
ByteUtils.writeUint32(cost, output, outputOff);
|
||||||
outputOff += INT_32_BYTES;
|
outputOff += INT_32_BYTES;
|
||||||
// IV
|
// IV
|
||||||
System.arraycopy(iv, 0, output, outputOff, iv.length);
|
arraycopy(iv, 0, output, outputOff, iv.length);
|
||||||
outputOff += iv.length;
|
outputOff += iv.length;
|
||||||
// Initialise the cipher and encrypt the plaintext
|
// Initialise the cipher and encrypt the plaintext
|
||||||
try {
|
try {
|
||||||
@@ -340,7 +370,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return null; // Unknown format
|
return null; // Unknown format
|
||||||
// Salt
|
// Salt
|
||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
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;
|
inputOff += salt.length;
|
||||||
// Cost parameter
|
// Cost parameter
|
||||||
long cost = ByteUtils.readUint32(input, inputOff);
|
long cost = ByteUtils.readUint32(input, inputOff);
|
||||||
@@ -349,7 +379,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return null; // Invalid cost parameter
|
return null; // Invalid cost parameter
|
||||||
// IV
|
// IV
|
||||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
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;
|
inputOff += iv.length;
|
||||||
// Derive the key from the password
|
// Derive the key from the password
|
||||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
||||||
|
|||||||
@@ -59,6 +59,22 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
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)
|
@Test(expected = GeneralSecurityException.class)
|
||||||
public void testRejectsInvalidPublicKey() throws Exception {
|
public void testRejectsInvalidPublicKey() throws Exception {
|
||||||
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
||||||
|
|||||||
Reference in New Issue
Block a user