diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeTask.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeTask.java index 3ee8d70af467ff5e18bc73c7cddd6c941af5bd4b..5750c0e3b4d203a5bf6d829619383f8e89fdd0a4 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeTask.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeTask.java @@ -12,6 +12,27 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; @NotNullByDefault public interface ContactExchangeTask { + /** + * Label for deriving Alice's header key from the master secret. + */ + String ALICE_KEY_LABEL = + "org.briarproject.bramble.contact/ALICE_HEADER_KEY"; + + /** + * Label for deriving Bob's header key from the master secret. + */ + String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY"; + + /** + * Label for deriving Alice's key binding nonce from the master secret. + */ + String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE"; + + /** + * Label for deriving Bob's key binding nonce from the master secret. + */ + String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE"; + /** * Exchanges contact information with a remote peer. */ 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 5da0e421887896682ab8c8bc7ab7b76941c2eb42..fd2c0ffc9781a063588767f43d7fe74d526a1361 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,95 +27,74 @@ public interface CryptoComponent { KeyParser getMessageKeyParser(); /** - * Derives a stream header key from the given master secret. - * @param alice whether the key is for use by Alice or Bob. - */ - SecretKey deriveHeaderKey(SecretKey master, boolean alice); - - /** - * Derives a message authentication code key from the given master secret. - * @param alice whether the key is for use by Alice or Bob. + * Derives another secret key from the given secret key. + * + * @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 */ - SecretKey deriveMacKey(SecretKey master, boolean alice); + SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); /** - * Derives a nonce from the given master secret for one of the parties to - * sign. - * @param alice whether the nonce is for use by Alice or Bob. + * Derives a nonce from the given secret key that can be used for key + * binding. + * + * @param label a namespaced label indicating the purpose of this nonce, + * to prevent it from being repurposed or colliding with a nonce derived + * for another purpose */ - byte[] deriveSignatureNonce(SecretKey master, boolean alice); + byte[] deriveKeyBindingNonce(String label, SecretKey k); /** * Derives a commitment to the provided public key. * <p/> - * Part of BQP. + * Used by the key exchange protocol. * * @param publicKey the public key * @return the commitment to the provided public key. */ - byte[] deriveKeyCommitment(byte[] publicKey); + byte[] deriveKeyCommitment(PublicKey publicKey); /** * Derives a common shared secret from two public keys and one of the * corresponding private keys. - * <p/> - * Part of BQP. * - * @param theirPublicKey the ephemeral public key of the remote party - * @param ourKeyPair our ephemeral keypair - * @param alice true if ourKeyPair belongs to Alice + * @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 + * @param alice true if the local party is Alice * @return the shared secret */ - SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair, - boolean alice) throws GeneralSecurityException; + SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, + KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException; /** * Derives the content of a confirmation record. * <p/> - * Part of BQP. + * Used by the key exchange protocol. * * @param sharedSecret the common shared secret - * @param theirPayload the commit payload from the remote party - * @param ourPayload the commit payload we sent + * @param theirPayload the key exchange payload of the remote party + * @param ourPayload the key exchange payload of the local party * @param theirPublicKey the ephemeral public key of the remote party - * @param ourKeyPair our ephemeral keypair - * @param alice true if ourKeyPair belongs to Alice + * @param ourKeyPair our ephemeral key pair of the local party + * @param alice true if the local party is Alice * @param aliceRecord true if the confirmation record is for use by Alice * @return the confirmation record */ byte[] deriveConfirmationRecord(SecretKey sharedSecret, byte[] theirPayload, byte[] ourPayload, - byte[] theirPublicKey, KeyPair ourKeyPair, + PublicKey theirPublicKey, KeyPair ourKeyPair, boolean alice, boolean aliceRecord); - /** - * Derives a master secret from the given shared secret. - * <p/> - * Part of BQP. - * - * @param sharedSecret the common shared secret - * @return the master secret - */ - SecretKey deriveMasterSecret(SecretKey sharedSecret); - - /** - * Derives a master secret from two public keys and one of the corresponding - * private keys. - * <p/> - * This is a helper method that calls - * deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice)) - * - * @param theirPublicKey the ephemeral public key of the remote party - * @param ourKeyPair our ephemeral keypair - * @param alice true if ourKeyPair belongs to Alice - * @return the shared secret - */ - SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair, - boolean alice) throws GeneralSecurityException; - /** * Derives initial transport keys for the given transport in the given * rotation period from the given master secret. + * <p/> + * Used by the transport security protocol. + * * @param alice whether the keys are for use by Alice or Bob. */ TransportKeys deriveTransportKeys(TransportId t, SecretKey master, @@ -124,18 +103,25 @@ public interface CryptoComponent { /** * Rotates the given transport keys to the given rotation period. If the * keys are for a future rotation period they are not rotated. + * <p/> + * Used by the transport security protocol. */ TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); - /** Encodes the pseudo-random tag that is used to recognise a stream. */ + /** + * Encodes the pseudo-random tag that is used to recognise a stream. + * <p/> + * Used by the transport security protocol. + */ void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, long streamNumber); /** * Signs the given byte[] with the given ECDSA private key. * - * @param label A label specific to this signature - * to ensure that the signature cannot be repurposed + * @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 */ byte[] sign(String label, byte[] toSign, byte[] privateKey) throws GeneralSecurityException; @@ -153,8 +139,9 @@ public interface CryptoComponent { * Verifies that the given signature is valid for the signed data * and the given ECDSA public key. * - * @param label A label that was specific to this signature - * to ensure that the signature cannot be repurposed + * @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. */ boolean verify(String label, byte[] signedData, byte[] publicKey, @@ -175,23 +162,22 @@ 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 label specific to this hash to ensure that hashes - * calculated for distinct purposes don't collide. + * @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 */ byte[] hash(String label, byte[]... inputs); - /** - * Returns the length of hashes produced by - * the {@link CryptoComponent#hash(String, byte[]...)} method. - */ - int getHashLength(); - /** * Returns a message authentication code with the given key over 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 MAC, to + * prevent it from being repurposed or colliding with a MAC created for + * another purpose */ - byte[] mac(SecretKey macKey, byte[]... inputs); + byte[] mac(String label, SecretKey macKey, byte[]... inputs); /** * Encrypts and authenticates the given plaintext so it can be written to diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java index 0963e0049e20ca3fb3ac8dc14950e86c0dae3898..b9793b4775d694c59e58774f804fad1298a57013 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java @@ -16,7 +16,7 @@ public class AuthorId extends UniqueId { /** * Label for hashing authors to calculate their identities. */ - public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID"; + public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID"; public AuthorId(byte[] id) { super(id); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementConstants.java index 76204bdfbeb2b48f7bad458bd82801eb7f39df42..807055d6e048118cc4482fd878eb0e33808e2680 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementConstants.java @@ -5,7 +5,7 @@ public interface KeyAgreementConstants { /** * The current version of the BQP protocol. */ - byte PROTOCOL_VERSION = 2; + byte PROTOCOL_VERSION = 3; /** * The length of the record header in bytes. @@ -22,7 +22,10 @@ public interface KeyAgreementConstants { */ int COMMIT_LENGTH = 16; - long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds + /** + * The connection timeout in milliseconds. + */ + long CONNECTION_TIMEOUT = 20 * 1000; /** * The transport identifier for Bluetooth. @@ -33,4 +36,16 @@ public interface KeyAgreementConstants { * The transport identifier for LAN. */ int TRANSPORT_ID_LAN = 1; + + /** + * Label for deriving the shared secret. + */ + String SHARED_SECRET_LABEL = + "org.briarproject.bramble.keyagreement/SHARED_SECRET"; + + /** + * Label for deriving the master secret. + */ + String MASTER_SECRET_LABEL = + "org.briarproject.bramble.keyagreement/MASTER_SECRET"; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java index cdd6b2d3fba5dd4ad95968e6e3360b1be120a695..d118ed9c0eb65dea8c678afa5c02b9385c93890b 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java @@ -15,7 +15,7 @@ public class GroupId extends UniqueId { /** * Label for hashing groups to calculate their identifiers. */ - public static final String LABEL = "org.briarproject.bramble.GROUP_ID"; + public static final String LABEL = "org.briarproject.bramble/GROUP_ID"; public GroupId(byte[] id) { super(id); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java index 84389bbcc83371980d7e71a62ca5137079e011d3..07487eb8c949e4c18561f6988af4130571bff836 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java @@ -16,7 +16,7 @@ public class MessageId extends UniqueId { /** * Label for hashing messages to calculate their identifiers. */ - public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID"; + public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID"; public MessageId(byte[] id) { super(id); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java index c49360e5bca20a9b99de30a3e710053c7ce8aea1..5a86874ec39b5cd11bbd40bcb09ed561eeba363e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java @@ -141,8 +141,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask { } // Derive the header keys for the transport streams - SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true); - SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false); + SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, + masterSecret); + SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret); // Create the readers InputStream streamReader = @@ -156,8 +157,10 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask { BdfWriter w = bdfWriterFactory.createWriter(streamWriter); // Derive the nonces to be signed - byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true); - byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false); + byte[] aliceNonce = crypto.deriveKeyBindingNonce(ALICE_NONCE_LABEL, + masterSecret); + byte[] bobNonce = crypto.deriveKeyBindingNonce(BOB_NONCE_LABEL, + masterSecret); // Exchange pseudonyms, signed nonces, and timestamps long localTimestamp = clock.currentTimeMillis(); @@ -312,8 +315,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask { return contactId; } - private void tryToClose(DuplexTransportConnection conn, - boolean exception) { + private void tryToClose(DuplexTransportConnection conn, boolean exception) { try { LOG.info("Closing connection"); conn.getReader().dispose(exception, true); 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 e92babdc038c6218508c5bdfb9e068cd5d46fda5..0b1a7f23516c40806765e0a4fc3b574ff19f4f02 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 @@ -30,7 +30,6 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.KeyParameter; -import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.Provider; @@ -65,39 +64,25 @@ class CryptoComponentImpl implements CryptoComponent { private static final int PBKDF_SALT_BYTES = 32; // 256 bits private static final int PBKDF_TARGET_MILLIS = 500; private static final int PBKDF_SAMPLES = 30; - private static final int HASH_SIZE = 256 / 8; - private static byte[] ascii(String s) { - return s.getBytes(Charset.forName("US-ASCII")); - } - - // KDF labels for contact exchange stream header key derivation - private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY"); - private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY"); - // KDF labels for contact exchange signature nonce derivation - private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE"); - private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE"); // Hash label for BQP public key commitment derivation - private static final String COMMIT = - "org.briarproject.bramble.COMMIT"; - // Hash label for shared secret derivation - private static final String SHARED_SECRET = - "org.briarproject.bramble.SHARED_SECRET"; + private static final String COMMIT_LABEL = + "org.briarproject.bramble.keyagreement/COMMIT"; // KDF label for BQP confirmation key derivation - private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY"); - // KDF label for master key derivation - private static final byte[] MASTER_KEY = ascii("MASTER_KEY"); + private static final String CONFIRMATION_KEY_LABEL = + "org.briarproject.bramble.keyagreement/CONFIRMATION_KEY"; + // MAC label for BQP confirmation record + private static final String CONFIRMATION_MAC_LABEL = + "org.briarproject.bramble.keyagreement/CONFIRMATION_MAC"; + // KDF labels for tag key derivation - private static final byte[] A_TAG = ascii("ALICE_TAG_KEY"); - private static final byte[] B_TAG = ascii("BOB_TAG_KEY"); + private static final String A_TAG = "ALICE_TAG_KEY"; + private static final String B_TAG = "BOB_TAG_KEY"; // KDF labels for header key derivation - private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY"); - private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY"); - // KDF labels for MAC key derivation - private static final byte[] A_MAC = ascii("ALICE_MAC_KEY"); - private static final byte[] B_MAC = ascii("BOB_MAC_KEY"); + private static final String A_HEADER = "ALICE_HEADER_KEY"; + private static final String B_HEADER = "BOB_HEADER_KEY"; // KDF label for key rotation - private static final byte[] ROTATE = ascii("ROTATE"); + private static final String ROTATE = "ROTATE"; private final SecureRandom secureRandom; private final ECKeyPairGenerator agreementKeyPairGenerator; @@ -263,25 +248,19 @@ class CryptoComponentImpl implements CryptoComponent { } @Override - public SecretKey deriveHeaderKey(SecretKey master, - boolean alice) { - return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE)); - } - - @Override - public SecretKey deriveMacKey(SecretKey master, boolean alice) { - return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC)); + public SecretKey deriveKey(String label, SecretKey k, + byte[]... inputs) { + return new SecretKey(macKdf(label, k, inputs)); } @Override - public byte[] deriveSignatureNonce(SecretKey master, - boolean alice) { - return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE); + public byte[] deriveKeyBindingNonce(String label, SecretKey k) { + return macKdf(label, k); } @Override - public byte[] deriveKeyCommitment(byte[] publicKey) { - byte[] hash = hash(COMMIT, publicKey); + public byte[] deriveKeyCommitment(PublicKey publicKey) { + byte[] hash = hash(COMMIT_LABEL, publicKey.getEncoded()); // The output is the first COMMIT_LENGTH bytes of the hash byte[] commitment = new byte[COMMIT_LENGTH]; System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH); @@ -289,55 +268,45 @@ class CryptoComponentImpl implements CryptoComponent { } @Override - public SecretKey deriveSharedSecret(byte[] theirPublicKey, + public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException { PrivateKey ourPriv = ourKeyPair.getPrivate(); - PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey); - byte[] raw = performRawKeyAgreement(ourPriv, theirPub); + byte[] raw = performRawKeyAgreement(ourPriv, theirPublicKey); byte[] alicePub, bobPub; if (alice) { alicePub = ourKeyPair.getPublic().getEncoded(); - bobPub = theirPublicKey; + bobPub = theirPublicKey.getEncoded(); } else { - alicePub = theirPublicKey; + alicePub = theirPublicKey.getEncoded(); bobPub = ourKeyPair.getPublic().getEncoded(); } - return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub)); + return new SecretKey(hash(label, raw, alicePub, bobPub)); } @Override public byte[] deriveConfirmationRecord(SecretKey sharedSecret, - byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey, + byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey, KeyPair ourKeyPair, boolean alice, boolean aliceRecord) { - SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY)); + SecretKey ck = deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret); byte[] alicePayload, alicePub, bobPayload, bobPub; if (alice) { alicePayload = ourPayload; alicePub = ourKeyPair.getPublic().getEncoded(); bobPayload = theirPayload; - bobPub = theirPublicKey; + bobPub = theirPublicKey.getEncoded(); } else { alicePayload = theirPayload; - alicePub = theirPublicKey; + alicePub = theirPublicKey.getEncoded(); bobPayload = ourPayload; bobPub = ourKeyPair.getPublic().getEncoded(); } - if (aliceRecord) - return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub); - else - return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub); - } - - @Override - public SecretKey deriveMasterSecret(SecretKey sharedSecret) { - return new SecretKey(macKdf(sharedSecret, MASTER_KEY)); - } - - @Override - public SecretKey deriveMasterSecret(byte[] theirPublicKey, - KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException { - return deriveMasterSecret(deriveSharedSecret( - theirPublicKey, ourKeyPair, alice)); + if (aliceRecord) { + return macKdf(CONFIRMATION_MAC_LABEL, ck, alicePayload, alicePub, + bobPayload, bobPub); + } else { + return macKdf(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub, + alicePayload, alicePub); + } } @Override @@ -396,19 +365,19 @@ class CryptoComponentImpl implements CryptoComponent { private SecretKey rotateKey(SecretKey k, long rotationPeriod) { byte[] period = new byte[INT_64_BYTES]; ByteUtils.writeUint64(rotationPeriod, period, 0); - return new SecretKey(macKdf(k, ROTATE, period)); + return deriveKey(ROTATE, k, period); } private SecretKey deriveTagKey(SecretKey master, TransportId t, boolean alice) { byte[] id = StringUtils.toUtf8(t.getString()); - return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id)); + return deriveKey(alice ? A_TAG : B_TAG, master, id); } private SecretKey deriveHeaderKey(SecretKey master, TransportId t, boolean alice) { byte[] id = StringUtils.toUtf8(t.getString()); - return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id)); + return deriveKey(alice ? A_HEADER : B_HEADER, master, id); } @Override @@ -513,14 +482,13 @@ class CryptoComponentImpl implements CryptoComponent { } @Override - public int getHashLength() { - return HASH_SIZE; - } - - @Override - public byte[] mac(SecretKey macKey, byte[]... inputs) { + public byte[] mac(String label, SecretKey macKey, byte[]... inputs) { + byte[] labelBytes = StringUtils.toUtf8(label); Digest mac = new Blake2sDigest(macKey.getBytes()); byte[] length = new byte[INT_32_BYTES]; + ByteUtils.writeUint32(labelBytes.length, length, 0); + mac.update(length, 0, length.length); + mac.update(labelBytes, 0, labelBytes.length); for (byte[] input : inputs) { ByteUtils.writeUint32(input.length, length, 0); mac.update(length, 0, length.length); @@ -614,26 +582,11 @@ class CryptoComponentImpl implements CryptoComponent { // Key derivation function based on a pseudo-random function - see // NIST SP 800-108, section 5.1 - private byte[] macKdf(SecretKey key, byte[]... inputs) { - // Initialise the PRF - Digest prf = new Blake2sDigest(key.getBytes()); - // The output of the PRF must be long enough to use as a key - int macLength = prf.getDigestSize(); - if (macLength < SecretKey.LENGTH) throw new IllegalStateException(); - // Calculate the PRF over the concatenated length-prefixed inputs - byte[] length = new byte[INT_32_BYTES]; - for (byte[] input : inputs) { - ByteUtils.writeUint32(input.length, length, 0); - prf.update(length, 0, length.length); - prf.update(input, 0, input.length); - } - byte[] mac = new byte[macLength]; - prf.doFinal(mac, 0); - // The output is the first SecretKey.LENGTH bytes of the MAC - if (mac.length == SecretKey.LENGTH) return mac; - byte[] truncated = new byte[SecretKey.LENGTH]; - System.arraycopy(mac, 0, truncated, 0, truncated.length); - return truncated; + private byte[] macKdf(String label, SecretKey k, byte[]... inputs) { + byte[] mac = mac(label, k, inputs); + // The output of the PRF must be usable as a key + if (mac.length != SecretKey.LENGTH) throw new IllegalStateException(); + return mac; } // Password-based key derivation function - see PKCS#5 v2.1, section 5.2 diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java index 992cb006b1daa923e021326066208b87fab7b966..2ed0b95211f0d5153db2717063600b287c642e6c 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java @@ -70,8 +70,8 @@ class KeyAgreementConnector { public Payload listen(KeyPair localKeyPair) { LOG.info("Starting BQP listeners"); // Derive commitment - byte[] commitment = crypto.deriveKeyCommitment( - localKeyPair.getPublic().getEncoded()); + byte[] commitment = + crypto.deriveKeyCommitment(localKeyPair.getPublic()); // Start all listeners and collect their descriptors List<TransportDescriptor> descriptors = new ArrayList<>(); for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocol.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocol.java index 3fb1c8b16e7cbad7e401d5c7ca703e58634dfcc4..be4895ff06152b6faf591da9cd7a342cc710d2bd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocol.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocol.java @@ -2,6 +2,8 @@ package org.briarproject.bramble.keyagreement; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.KeyParser; +import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.PayloadEncoder; @@ -11,6 +13,9 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Arrays; +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; + /** * Implementation of the BQP protocol. * <p/> @@ -86,7 +91,7 @@ class KeyAgreementProtocol { */ SecretKey perform() throws AbortException, IOException { try { - byte[] theirPublicKey; + PublicKey theirPublicKey; if (alice) { sendKey(); // Alice waits here until Bob obtains her payload. @@ -104,7 +109,7 @@ class KeyAgreementProtocol { receiveConfirm(s, theirPublicKey); sendConfirm(s, theirPublicKey); } - return crypto.deriveMasterSecret(s); + return crypto.deriveKey(MASTER_SECRET_LABEL, s); } catch (AbortException e) { sendAbort(e.getCause() != null); throw e; @@ -115,25 +120,32 @@ class KeyAgreementProtocol { transport.sendKey(ourKeyPair.getPublic().getEncoded()); } - private byte[] receiveKey() throws AbortException { - byte[] publicKey = transport.receiveKey(); + private PublicKey receiveKey() throws AbortException { + byte[] publicKeyBytes = transport.receiveKey(); callbacks.initialRecordReceived(); - byte[] expected = crypto.deriveKeyCommitment(publicKey); - if (!Arrays.equals(expected, theirPayload.getCommitment())) + KeyParser keyParser = crypto.getAgreementKeyParser(); + try { + PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes); + byte[] expected = crypto.deriveKeyCommitment(publicKey); + if (!Arrays.equals(expected, theirPayload.getCommitment())) + throw new AbortException(); + return publicKey; + } catch (GeneralSecurityException e) { throw new AbortException(); - return publicKey; + } } - private SecretKey deriveSharedSecret(byte[] theirPublicKey) + private SecretKey deriveSharedSecret(PublicKey theirPublicKey) throws AbortException { try { - return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice); + return crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + theirPublicKey, ourKeyPair, alice); } catch (GeneralSecurityException e) { throw new AbortException(e); } } - private void sendConfirm(SecretKey s, byte[] theirPublicKey) + private void sendConfirm(SecretKey s, PublicKey theirPublicKey) throws IOException { byte[] confirm = crypto.deriveConfirmationRecord(s, payloadEncoder.encode(theirPayload), @@ -143,7 +155,7 @@ class KeyAgreementProtocol { transport.sendConfirm(confirm); } - private void receiveConfirm(SecretKey s, byte[] theirPublicKey) + private void receiveConfirm(SecretKey s, PublicKey theirPublicKey) throws AbortException { byte[] confirm = transport.receiveConfirm(); byte[] expected = crypto.deriveConfirmationRecord(s, 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 ad3b0dad6bb1b3c2d8a5d5cbe0662c4960be26d3..1aec3b78fe14e7f5a5c9923a4698de8edf59f50f 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 @@ -3,40 +3,25 @@ package org.briarproject.bramble.crypto; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.SecretKey; -import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.TestSecureRandomProvider; import org.junit.Test; +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.junit.Assert.assertArrayEquals; public class KeyAgreementTest extends BrambleTestCase { - @Test - public void testDeriveMasterSecret() throws Exception { - SecureRandomProvider - secureRandomProvider = new TestSecureRandomProvider(); - CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider); - KeyPair aPair = crypto.generateAgreementKeyPair(); - byte[] aPub = aPair.getPublic().getEncoded(); - KeyPair bPair = crypto.generateAgreementKeyPair(); - byte[] bPub = bPair.getPublic().getEncoded(); - SecretKey aMaster = crypto.deriveMasterSecret(aPub, bPair, true); - SecretKey bMaster = crypto.deriveMasterSecret(bPub, aPair, false); - assertArrayEquals(aMaster.getBytes(), bMaster.getBytes()); - } - @Test public void testDeriveSharedSecret() throws Exception { - SecureRandomProvider - secureRandomProvider = new TestSecureRandomProvider(); - CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider); + CryptoComponent crypto = + new CryptoComponentImpl(new TestSecureRandomProvider()); KeyPair aPair = crypto.generateAgreementKeyPair(); - byte[] aPub = aPair.getPublic().getEncoded(); KeyPair bPair = crypto.generateAgreementKeyPair(); - byte[] bPub = bPair.getPublic().getEncoded(); - SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true); - SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false); + SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + bPair.getPublic(), aPair, true); + SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + aPair.getPublic(), bPair, false); assertArrayEquals(aShared.getBytes(), bShared.getBytes()); } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/MacTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/MacTest.java index f6760bf3e66ace6db806c241d129c11b95ddc2a2..8541c4a1d3c5ed4193700698c41d5358bc08fd67 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/MacTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/MacTest.java @@ -4,42 +4,49 @@ import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.TestSecureRandomProvider; -import org.briarproject.bramble.test.TestUtils; import org.junit.Test; import java.util.Arrays; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; +import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; public class MacTest extends BrambleTestCase { - private final CryptoComponent crypto; + private final CryptoComponent crypto = + new CryptoComponentImpl(new TestSecureRandomProvider()); - private final SecretKey k = TestUtils.getSecretKey(); - private final byte[] inputBytes = TestUtils.getRandomBytes(123); - private final byte[] inputBytes1 = TestUtils.getRandomBytes(234); - private final byte[] inputBytes2 = new byte[0]; - - public MacTest() { - crypto = new CryptoComponentImpl(new TestSecureRandomProvider()); - } + private final SecretKey key1 = getSecretKey(), key2 = getSecretKey(); + private final String label1 = getRandomString(123); + private final String label2 = getRandomString(123); + private final byte[] input1 = getRandomBytes(123); + private final byte[] input2 = getRandomBytes(234); + private final byte[] input3 = new byte[0]; @Test public void testIdenticalKeysAndInputsProduceIdenticalMacs() { // Calculate the MAC twice - the results should be identical - byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2); - byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2); + byte[] mac = crypto.mac(label1, key1, input1, input2, input3); + byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3); assertArrayEquals(mac, mac1); } + @Test + public void testDifferentLabelsProduceDifferentMacs() { + // Calculate the MAC with each label - the results should be different + byte[] mac = crypto.mac(label1, key1, input1, input2, input3); + byte[] mac1 = crypto.mac(label2, key1, input1, input2, input3); + assertFalse(Arrays.equals(mac, mac1)); + } + @Test public void testDifferentKeysProduceDifferentMacs() { - // Generate second random key - SecretKey k1 = TestUtils.getSecretKey(); // Calculate the MAC with each key - the results should be different - byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2); - byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2); + byte[] mac = crypto.mac(label1, key1, input1, input2, input3); + byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3); assertFalse(Arrays.equals(mac, mac1)); } @@ -47,8 +54,8 @@ public class MacTest extends BrambleTestCase { public void testDifferentInputsProduceDifferentMacs() { // Calculate the MAC with the inputs in different orders - the results // should be different - byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2); - byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes); + byte[] mac = crypto.mac(label1, key1, input1, input2, input3); + byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1); assertFalse(Arrays.equals(mac, mac1)); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocolTest.java b/bramble-core/src/test/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocolTest.java index a737328780331e290cdd73a0346541098a141d23..25d882d78224e387cd41efde0bb7c43e45d4b585 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocolTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/keyagreement/KeyAgreementProtocolTest.java @@ -2,12 +2,12 @@ package org.briarproject.bramble.keyagreement; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.test.BrambleTestCase; -import org.briarproject.bramble.test.TestUtils; import org.jmock.Expectations; import org.jmock.auto.Mock; import org.jmock.integration.junit4.JUnitRuleMockery; @@ -16,6 +16,10 @@ import org.junit.Rule; import org.junit.Test; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -28,34 +32,31 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { setImposteriser(ClassImposteriser.INSTANCE); }}; - private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32); - private static final byte[] ALICE_COMMIT = - TestUtils.getRandomBytes(COMMIT_LENGTH); - private static final byte[] ALICE_PAYLOAD = - TestUtils.getRandomBytes(COMMIT_LENGTH + 8); - - private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32); - private static final byte[] BOB_COMMIT = - TestUtils.getRandomBytes(COMMIT_LENGTH); - private static final byte[] BOB_PAYLOAD = - TestUtils.getRandomBytes(COMMIT_LENGTH + 19); - - private static final byte[] ALICE_CONFIRM = - TestUtils.getRandomBytes(SecretKey.LENGTH); - private static final byte[] BOB_CONFIRM = - TestUtils.getRandomBytes(SecretKey.LENGTH); - - private static final byte[] BAD_PUBKEY = TestUtils.getRandomBytes(32); - private static final byte[] BAD_COMMIT = - TestUtils.getRandomBytes(COMMIT_LENGTH); - private static final byte[] BAD_CONFIRM = - TestUtils.getRandomBytes(SecretKey.LENGTH); + private final PublicKey alicePubKey = + context.mock(PublicKey.class, "alice"); + private final byte[] alicePubKeyBytes = getRandomBytes(32); + private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH); + private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8); + private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH); + + private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob"); + private final byte[] bobPubKeyBytes = getRandomBytes(32); + private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH); + private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19); + private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH); + + private final PublicKey badPubKey = context.mock(PublicKey.class, "bad"); + private final byte[] badPubKeyBytes = getRandomBytes(32); + private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH); + private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH); @Mock KeyAgreementProtocol.Callbacks callbacks; @Mock CryptoComponent crypto; @Mock + KeyParser keyParser; + @Mock PayloadEncoder payloadEncoder; @Mock KeyAgreementTransport transport; @@ -65,11 +66,11 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test public void testAliceProtocol() throws Exception { // set up - Payload theirPayload = new Payload(BOB_COMMIT, null); - Payload ourPayload = new Payload(ALICE_COMMIT, null); + Payload theirPayload = new Payload(bobCommit, null); + Payload ourPayload = new Payload(aliceCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); - SecretKey sharedSecret = TestUtils.getSecretKey(); - SecretKey masterSecret = TestUtils.getSecretKey(); + SecretKey sharedSecret = getSecretKey(); + SecretKey masterSecret = getSecretKey(); KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, @@ -79,46 +80,51 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(payloadEncoder).encode(ourPayload); - will(returnValue(ALICE_PAYLOAD)); + will(returnValue(alicePayload)); allowing(payloadEncoder).encode(theirPayload); - will(returnValue(BOB_PAYLOAD)); + will(returnValue(bobPayload)); allowing(ourPubKey).getEncoded(); - will(returnValue(ALICE_PUBKEY)); + will(returnValue(alicePubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Alice sends her public key - oneOf(transport).sendKey(ALICE_PUBKEY); + oneOf(transport).sendKey(alicePubKeyBytes); // Alice receives Bob's public key oneOf(callbacks).connectionWaiting(); oneOf(transport).receiveKey(); - will(returnValue(BOB_PUBKEY)); + will(returnValue(bobPubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(bobPubKeyBytes); + will(returnValue(bobPubKey)); // Alice verifies Bob's public key - oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY); - will(returnValue(BOB_COMMIT)); + oneOf(crypto).deriveKeyCommitment(bobPubKey); + will(returnValue(bobCommit)); // Alice computes shared secret - oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true); + oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey, + ourKeyPair, true); will(returnValue(sharedSecret)); // Alice sends her confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD, - ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true); - will(returnValue(ALICE_CONFIRM)); - oneOf(transport).sendConfirm(ALICE_CONFIRM); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, bobPayload, + alicePayload, bobPubKey, ourKeyPair, true, true); + will(returnValue(aliceConfirm)); + oneOf(transport).sendConfirm(aliceConfirm); // Alice receives Bob's confirmation record oneOf(transport).receiveConfirm(); - will(returnValue(BOB_CONFIRM)); + will(returnValue(bobConfirm)); // Alice verifies Bob's confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD, - ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false); - will(returnValue(BOB_CONFIRM)); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, bobPayload, + alicePayload, bobPubKey, ourKeyPair, true, false); + will(returnValue(bobConfirm)); // Alice computes master secret - oneOf(crypto).deriveMasterSecret(sharedSecret); + oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); will(returnValue(masterSecret)); }}); @@ -129,11 +135,11 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test public void testBobProtocol() throws Exception { // set up - Payload theirPayload = new Payload(ALICE_COMMIT, null); - Payload ourPayload = new Payload(BOB_COMMIT, null); + Payload theirPayload = new Payload(aliceCommit, null); + Payload ourPayload = new Payload(bobCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); - SecretKey sharedSecret = TestUtils.getSecretKey(); - SecretKey masterSecret = TestUtils.getSecretKey(); + SecretKey sharedSecret = getSecretKey(); + SecretKey masterSecret = getSecretKey(); KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, @@ -143,45 +149,50 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(payloadEncoder).encode(ourPayload); - will(returnValue(BOB_PAYLOAD)); + will(returnValue(bobPayload)); allowing(payloadEncoder).encode(theirPayload); - will(returnValue(ALICE_PAYLOAD)); + will(returnValue(alicePayload)); allowing(ourPubKey).getEncoded(); - will(returnValue(BOB_PUBKEY)); + will(returnValue(bobPubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Bob receives Alice's public key oneOf(transport).receiveKey(); - will(returnValue(ALICE_PUBKEY)); + will(returnValue(alicePubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(alicePubKeyBytes); + will(returnValue(alicePubKey)); // Bob verifies Alice's public key - oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY); - will(returnValue(ALICE_COMMIT)); + oneOf(crypto).deriveKeyCommitment(alicePubKey); + will(returnValue(aliceCommit)); // Bob sends his public key - oneOf(transport).sendKey(BOB_PUBKEY); + oneOf(transport).sendKey(bobPubKeyBytes); // Bob computes shared secret - oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false); + oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey, + ourKeyPair, false); will(returnValue(sharedSecret)); // Bob receives Alices's confirmation record oneOf(transport).receiveConfirm(); - will(returnValue(ALICE_CONFIRM)); + will(returnValue(aliceConfirm)); // Bob verifies Alice's confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD, - BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true); - will(returnValue(ALICE_CONFIRM)); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, alicePayload, + bobPayload, alicePubKey, ourKeyPair, false, true); + will(returnValue(aliceConfirm)); // Bob sends his confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD, - BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false); - will(returnValue(BOB_CONFIRM)); - oneOf(transport).sendConfirm(BOB_CONFIRM); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, alicePayload, + bobPayload, alicePubKey, ourKeyPair, false, false); + will(returnValue(bobConfirm)); + oneOf(transport).sendConfirm(bobConfirm); // Bob computes master secret - oneOf(crypto).deriveMasterSecret(sharedSecret); + oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); will(returnValue(masterSecret)); }}); @@ -192,8 +203,8 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test(expected = AbortException.class) public void testAliceProtocolAbortOnBadKey() throws Exception { // set up - Payload theirPayload = new Payload(BOB_COMMIT, null); - Payload ourPayload = new Payload(ALICE_COMMIT, null); + Payload theirPayload = new Payload(bobCommit, null); + Payload ourPayload = new Payload(aliceCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyAgreementProtocol protocol = @@ -204,26 +215,31 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(ourPubKey).getEncoded(); - will(returnValue(ALICE_PUBKEY)); + will(returnValue(alicePubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Alice sends her public key - oneOf(transport).sendKey(ALICE_PUBKEY); + oneOf(transport).sendKey(alicePubKeyBytes); // Alice receives a bad public key oneOf(callbacks).connectionWaiting(); oneOf(transport).receiveKey(); - will(returnValue(BAD_PUBKEY)); + will(returnValue(badPubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(badPubKeyBytes); + will(returnValue(badPubKey)); // Alice verifies Bob's public key - oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY); - will(returnValue(BAD_COMMIT)); + oneOf(crypto).deriveKeyCommitment(badPubKey); + will(returnValue(badCommit)); // Alice aborts oneOf(transport).sendAbort(false); // Alice never computes shared secret - never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true); + never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey, + ourKeyPair, true); }}); // execute @@ -233,8 +249,8 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test(expected = AbortException.class) public void testBobProtocolAbortOnBadKey() throws Exception { // set up - Payload theirPayload = new Payload(ALICE_COMMIT, null); - Payload ourPayload = new Payload(BOB_COMMIT, null); + Payload theirPayload = new Payload(aliceCommit, null); + Payload ourPayload = new Payload(bobCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyAgreementProtocol protocol = @@ -245,22 +261,26 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(ourPubKey).getEncoded(); - will(returnValue(BOB_PUBKEY)); + will(returnValue(bobPubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Bob receives a bad public key oneOf(transport).receiveKey(); - will(returnValue(BAD_PUBKEY)); + will(returnValue(badPubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(badPubKeyBytes); + will(returnValue(badPubKey)); // Bob verifies Alice's public key - oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY); - will(returnValue(BAD_COMMIT)); + oneOf(crypto).deriveKeyCommitment(badPubKey); + will(returnValue(badCommit)); // Bob aborts oneOf(transport).sendAbort(false); // Bob never sends his public key - never(transport).sendKey(BOB_PUBKEY); + never(transport).sendKey(bobPubKeyBytes); }}); // execute @@ -270,10 +290,10 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test(expected = AbortException.class) public void testAliceProtocolAbortOnBadConfirm() throws Exception { // set up - Payload theirPayload = new Payload(BOB_COMMIT, null); - Payload ourPayload = new Payload(ALICE_COMMIT, null); + Payload theirPayload = new Payload(bobCommit, null); + Payload ourPayload = new Payload(aliceCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); - SecretKey sharedSecret = TestUtils.getSecretKey(); + SecretKey sharedSecret = getSecretKey(); KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, @@ -283,49 +303,54 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(payloadEncoder).encode(ourPayload); - will(returnValue(ALICE_PAYLOAD)); + will(returnValue(alicePayload)); allowing(payloadEncoder).encode(theirPayload); - will(returnValue(BOB_PAYLOAD)); + will(returnValue(bobPayload)); allowing(ourPubKey).getEncoded(); - will(returnValue(ALICE_PUBKEY)); + will(returnValue(alicePubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Alice sends her public key - oneOf(transport).sendKey(ALICE_PUBKEY); + oneOf(transport).sendKey(alicePubKeyBytes); // Alice receives Bob's public key oneOf(callbacks).connectionWaiting(); oneOf(transport).receiveKey(); - will(returnValue(BOB_PUBKEY)); + will(returnValue(bobPubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(bobPubKeyBytes); + will(returnValue(bobPubKey)); // Alice verifies Bob's public key - oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY); - will(returnValue(BOB_COMMIT)); + oneOf(crypto).deriveKeyCommitment(bobPubKey); + will(returnValue(bobCommit)); // Alice computes shared secret - oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true); + oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey, + ourKeyPair, true); will(returnValue(sharedSecret)); // Alice sends her confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD, - ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true); - will(returnValue(ALICE_CONFIRM)); - oneOf(transport).sendConfirm(ALICE_CONFIRM); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, bobPayload, + alicePayload, bobPubKey, ourKeyPair, true, true); + will(returnValue(aliceConfirm)); + oneOf(transport).sendConfirm(aliceConfirm); // Alice receives a bad confirmation record oneOf(transport).receiveConfirm(); - will(returnValue(BAD_CONFIRM)); + will(returnValue(badConfirm)); // Alice verifies Bob's confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD, - ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false); - will(returnValue(BOB_CONFIRM)); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, bobPayload, + alicePayload, bobPubKey, ourKeyPair, true, false); + will(returnValue(bobConfirm)); // Alice aborts oneOf(transport).sendAbort(false); // Alice never computes master secret - never(crypto).deriveMasterSecret(sharedSecret); + never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); }}); // execute @@ -335,10 +360,10 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { @Test(expected = AbortException.class) public void testBobProtocolAbortOnBadConfirm() throws Exception { // set up - Payload theirPayload = new Payload(ALICE_COMMIT, null); - Payload ourPayload = new Payload(BOB_COMMIT, null); + Payload theirPayload = new Payload(aliceCommit, null); + Payload ourPayload = new Payload(bobCommit, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null); - SecretKey sharedSecret = TestUtils.getSecretKey(); + SecretKey sharedSecret = getSecretKey(); KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, @@ -348,43 +373,48 @@ public class KeyAgreementProtocolTest extends BrambleTestCase { context.checking(new Expectations() {{ // Helpers allowing(payloadEncoder).encode(ourPayload); - will(returnValue(BOB_PAYLOAD)); + will(returnValue(bobPayload)); allowing(payloadEncoder).encode(theirPayload); - will(returnValue(ALICE_PAYLOAD)); + will(returnValue(alicePayload)); allowing(ourPubKey).getEncoded(); - will(returnValue(BOB_PUBKEY)); + will(returnValue(bobPubKeyBytes)); + allowing(crypto).getAgreementKeyParser(); + will(returnValue(keyParser)); // Bob receives Alice's public key oneOf(transport).receiveKey(); - will(returnValue(ALICE_PUBKEY)); + will(returnValue(alicePubKeyBytes)); oneOf(callbacks).initialRecordReceived(); + oneOf(keyParser).parsePublicKey(alicePubKeyBytes); + will(returnValue(alicePubKey)); // Bob verifies Alice's public key - oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY); - will(returnValue(ALICE_COMMIT)); + oneOf(crypto).deriveKeyCommitment(alicePubKey); + will(returnValue(aliceCommit)); // Bob sends his public key - oneOf(transport).sendKey(BOB_PUBKEY); + oneOf(transport).sendKey(bobPubKeyBytes); // Bob computes shared secret - oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false); + oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey, + ourKeyPair, false); will(returnValue(sharedSecret)); // Bob receives a bad confirmation record oneOf(transport).receiveConfirm(); - will(returnValue(BAD_CONFIRM)); + will(returnValue(badConfirm)); // Bob verifies Alice's confirmation record - oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD, - BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true); - will(returnValue(ALICE_CONFIRM)); + oneOf(crypto).deriveConfirmationRecord(sharedSecret, alicePayload, + bobPayload, alicePubKey, ourKeyPair, false, true); + will(returnValue(aliceConfirm)); // Bob aborts oneOf(transport).sendAbort(false); // Bob never sends his confirmation record - never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD, - BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false); + never(crypto).deriveConfirmationRecord(sharedSecret, alicePayload, + bobPayload, alicePubKey, ourKeyPair, false, false); }}); // execute diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java index a0ee65e00b072c7e5439389d99353d15b69d6f0b..72e6030b10b207a179abf99be29e7897af2777cb 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java @@ -86,4 +86,44 @@ public interface IntroductionConstants { int TASK_ACTIVATE_CONTACT = 1; int TASK_ABORT = 2; + /** + * Label for deriving the shared secret. + */ + String SHARED_SECRET_LABEL = + "org.briarproject.briar.introduction/SHARED_SECRET"; + + /** + * Label for deriving Alice's key binding nonce from the shared secret. + */ + String ALICE_NONCE_LABEL = + "org.briarproject.briar.introduction/ALICE_NONCE"; + + /** + * Label for deriving Bob's key binding nonce from the shared secret. + */ + String BOB_NONCE_LABEL = + "org.briarproject.briar.introduction/BOB_NONCE"; + + /** + * Label for deriving Alice's MAC key from the shared secret. + */ + String ALICE_MAC_KEY_LABEL = + "org.briarproject.briar.introduction/ALICE_MAC_KEY"; + + /** + * Label for deriving Bob's MAC key from the shared secret. + */ + String BOB_MAC_KEY_LABEL = + "org.briarproject.briar.introduction/BOB_MAC_KEY"; + + /** + * Label for signing the introduction response. + */ + String SIGNING_LABEL = + "org.briarproject.briar.introduction/RESPONSE_SIGNATURE"; + + /** + * Label for MACing the introduction response. + */ + String MAC_LABEL = "org.briarproject.briar.introduction/RESPONSE_MAC"; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeManager.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeManager.java index 846b8b4f6587e20624e6147ebd76358531e52caa..84cd1450ea616279cd282c8cde7662eb90585841 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeManager.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeManager.java @@ -50,7 +50,11 @@ import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.briar.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST; import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT; import static org.briarproject.briar.api.introduction.IntroductionConstants.ADDED_CONTACT_ID; +import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_MAC_KEY_LABEL; +import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_NONCE_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.ANSWERED; +import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_MAC_KEY_LABEL; +import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_NONCE_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT; import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1; import static org.briarproject.briar.api.introduction.IntroductionConstants.EXISTS; @@ -60,6 +64,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.INTR import static org.briarproject.briar.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME; import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME; @@ -76,7 +81,9 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.REMO import static org.briarproject.briar.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US; import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE; import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCEE; +import static org.briarproject.briar.api.introduction.IntroductionConstants.SHARED_SECRET_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE; +import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE; import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.TASK; @@ -89,7 +96,6 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE; -import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID; @Immutable @NotNullByDefault @@ -98,9 +104,6 @@ class IntroduceeManager { private static final Logger LOG = Logger.getLogger(IntroduceeManager.class.getName()); - static final String SIGNING_LABEL_RESPONSE = - CLIENT_ID.getString() + "/RESPONSE"; - private final MessageSender messageSender; private final DatabaseComponent db; private final ClientHelper clientHelper; @@ -288,8 +291,7 @@ class IntroduceeManager { @Nullable private BdfDictionary performTasks(Transaction txn, - BdfDictionary localState) - throws FormatException, DbException { + BdfDictionary localState) throws FormatException, DbException { if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE) return null; @@ -306,22 +308,21 @@ class IntroduceeManager { } // figure out who takes which role by comparing public keys - byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY); - byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY); - int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes), - new Bytes(theirEphemeralKey)); + byte[] ourPublicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY); + byte[] theirPublicKeyBytes = localState.getRaw(E_PUBLIC_KEY); + int comp = Bytes.COMPARATOR.compare(new Bytes(ourPublicKeyBytes), + new Bytes(theirPublicKeyBytes)); boolean alice = comp < 0; // get our local author LocalAuthor author = identityManager.getLocalAuthor(txn); SecretKey secretKey; - byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY); + byte[] ourPrivateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY); try { // derive secret master key - secretKey = - deriveSecretKey(publicKeyBytes, privateKeyBytes, alice, - theirEphemeralKey); + secretKey = deriveSecretKey(ourPublicKeyBytes, + ourPrivateKeyBytes, alice, theirPublicKeyBytes); // derive MAC keys and nonces, sign our nonce and calculate MAC deriveMacKeysAndNonces(localState, author, secretKey, alice); } catch (GeneralSecurityException e) { @@ -410,34 +411,36 @@ class IntroduceeManager { return null; } - private SecretKey deriveSecretKey(byte[] publicKeyBytes, - byte[] privateKeyBytes, boolean alice, byte[] theirPublicKey) - throws GeneralSecurityException { + private SecretKey deriveSecretKey(byte[] ourPublicKeyBytes, + byte[] ourPrivateKeyBytes, boolean alice, + byte[] theirPublicKeyBytes) throws GeneralSecurityException { // parse the local ephemeral key pair KeyParser keyParser = cryptoComponent.getAgreementKeyParser(); - PublicKey publicKey; - PrivateKey privateKey; + PublicKey ourPublicKey; + PrivateKey ourPrivateKey; try { - publicKey = keyParser.parsePublicKey(publicKeyBytes); - privateKey = keyParser.parsePrivateKey(privateKeyBytes); + ourPublicKey = keyParser.parsePublicKey(ourPublicKeyBytes); + ourPrivateKey = keyParser.parsePrivateKey(ourPrivateKeyBytes); } catch (GeneralSecurityException e) { if (LOG.isLoggable(WARNING)) { LOG.log(WARNING, e.toString(), e); } throw new RuntimeException("Our own ephemeral key is invalid"); } - KeyPair keyPair = new KeyPair(publicKey, privateKey); + KeyPair ourKeyPair = new KeyPair(ourPublicKey, ourPrivateKey); + PublicKey theirPublicKey = + keyParser.parsePublicKey(theirPublicKeyBytes); - // The master secret is derived from the local ephemeral key pair + // The shared secret is derived from the local ephemeral key pair // and the remote ephemeral public key - return cryptoComponent - .deriveMasterSecret(theirPublicKey, keyPair, alice); + return cryptoComponent.deriveSharedSecret(SHARED_SECRET_LABEL, + theirPublicKey, ourKeyPair, alice); } /** * Derives nonces, signs our nonce and calculates MAC * <p> - * Derives two nonces and two mac keys from the secret master key. + * Derives two nonces and two MAC keys from the shared secret key. * The other introducee's nonce and MAC key are added to the localState. * <p> * Our nonce is signed with the local author's long-term private key. @@ -448,21 +451,23 @@ class IntroduceeManager { private void deriveMacKeysAndNonces(BdfDictionary localState, LocalAuthor author, SecretKey secretKey, boolean alice) throws FormatException, GeneralSecurityException { - // Derive two nonces and a MAC key from the secret master key - byte[] ourNonce = - cryptoComponent.deriveSignatureNonce(secretKey, alice); - byte[] theirNonce = - cryptoComponent.deriveSignatureNonce(secretKey, !alice); - SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice); - SecretKey theirMacKey = cryptoComponent.deriveMacKey(secretKey, !alice); + // Derive two nonces and MAC keys from the shared secret key + byte[] ourNonce = cryptoComponent.deriveKeyBindingNonce( + alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL, secretKey); + byte[] theirNonce = cryptoComponent.deriveKeyBindingNonce( + alice ? BOB_NONCE_LABEL : ALICE_NONCE_LABEL, secretKey); + SecretKey ourMacKey = cryptoComponent.deriveKey( + alice ? ALICE_MAC_KEY_LABEL : BOB_MAC_KEY_LABEL, secretKey); + SecretKey theirMacKey = cryptoComponent.deriveKey( + alice ? BOB_MAC_KEY_LABEL : ALICE_MAC_KEY_LABEL, secretKey); // Save the other nonce and MAC key for the verification localState.put(NONCE, theirNonce); localState.put(MAC_KEY, theirMacKey.getBytes()); // Sign our nonce with our long-term identity public key - byte[] sig = cryptoComponent - .sign(SIGNING_LABEL_RESPONSE, ourNonce, author.getPrivateKey()); + byte[] sig = cryptoComponent.sign(SIGNING_LABEL, ourNonce, + author.getPrivateKey()); // Calculate a MAC over identity public key, ephemeral public key, // transport properties and timestamp. @@ -472,7 +477,7 @@ class IntroduceeManager { BdfList toMacList = BdfList.of(author.getPublicKey(), publicKeyBytes, tp, ourTime); byte[] toMac = clientHelper.toByteArray(toMacList); - byte[] mac = cryptoComponent.mac(macKey, toMac); + byte[] mac = cryptoComponent.mac(MAC_LABEL, ourMacKey, toMac); // Add MAC and signature to localState, so it can be included in ACK localState.put(OUR_MAC, mac); @@ -486,7 +491,7 @@ class IntroduceeManager { byte[] key = localState.getRaw(PUBLIC_KEY); // Verify the signature - if (!cryptoComponent.verify(SIGNING_LABEL_RESPONSE, nonce, key, sig)) { + if (!cryptoComponent.verify(SIGNING_LABEL, nonce, key, sig)) { LOG.warning("Invalid nonce signature in ACK"); throw new GeneralSecurityException(); } @@ -506,7 +511,7 @@ class IntroduceeManager { long timestamp = localState.getLong(TIME); BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp); byte[] toMac = clientHelper.toByteArray(toMacList); - byte[] calculatedMac = cryptoComponent.mac(macKey, toMac); + byte[] calculatedMac = cryptoComponent.mac(MAC_LABEL, macKey, toMac); if (!Arrays.equals(mac, calculatedMac)) { LOG.warning("Received ACK with invalid MAC"); throw new GeneralSecurityException(); diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroduceeManagerTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroduceeManagerTest.java index 0453530c9a42833d23958ddfd22b91b4cd12fb86..2144b49799cc6f109d79d79cd50298affbe5310f 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroduceeManagerTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroduceeManagerTest.java @@ -53,6 +53,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.INTR import static org.briarproject.briar.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LENGTH; import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME; @@ -66,6 +67,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCEE; import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE; +import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE; import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME; @@ -74,7 +76,6 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE; -import static org.briarproject.briar.introduction.IntroduceeManager.SIGNING_LABEL_RESPONSE; import static org.hamcrest.Matchers.array; import static org.hamcrest.Matchers.samePropertyValuesAs; import static org.junit.Assert.assertFalse; @@ -266,7 +267,7 @@ public class IntroduceeManagerTest extends BriarTestCase { ); context.checking(new Expectations() {{ - oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce, + oneOf(cryptoComponent).verify(SIGNING_LABEL, nonce, introducee2.getAuthor().getPublicKey(), sig); will(returnValue(false)); }}); @@ -296,7 +297,7 @@ public class IntroduceeManagerTest extends BriarTestCase { state.put(SIGNATURE, sig); context.checking(new Expectations() {{ - oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce, + oneOf(cryptoComponent).verify(SIGNING_LABEL, nonce, publicKeyBytes, sig); will(returnValue(true)); }}); @@ -330,7 +331,8 @@ public class IntroduceeManagerTest extends BriarTestCase { BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time)); will(returnValue(signBytes)); //noinspection unchecked - oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)), + oneOf(cryptoComponent).mac(with(MAC_LABEL), + with(samePropertyValuesAs(macKey)), with(array(equal(signBytes)))); will(returnValue(mac)); }}); @@ -343,14 +345,15 @@ public class IntroduceeManagerTest extends BriarTestCase { BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time)); will(returnValue(signBytes)); //noinspection unchecked - oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)), + oneOf(cryptoComponent).mac(with(MAC_LABEL), + with(samePropertyValuesAs(macKey)), with(array(equal(signBytes)))); will(returnValue(TestUtils.getRandomBytes(MAC_LENGTH))); }}); try { introduceeManager.verifyMac(state); fail(); - } catch(GeneralSecurityException e) { + } catch (GeneralSecurityException e) { // expected } context.assertIsSatisfied(); diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java index 4902f58c1fc216312e7f84ab9e84668073f74504..f3e3fcf23ff6b2729fcd182bb95102240a52322a 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java @@ -56,21 +56,25 @@ import javax.inject.Inject; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID; import static org.briarproject.briar.api.client.MessageQueueManager.QUEUE_STATE_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_MAC_KEY_LABEL; +import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_NONCE_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME; import static org.briarproject.briar.api.introduction.IntroductionConstants.NONCE; import static org.briarproject.briar.api.introduction.IntroductionConstants.PUBLIC_KEY; import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.briar.api.introduction.IntroductionConstants.SHARED_SECRET_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE; +import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL; import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME; import static org.briarproject.briar.api.introduction.IntroductionConstants.TRANSPORT; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE; -import static org.briarproject.briar.introduction.IntroduceeManager.SIGNING_LABEL_RESPONSE; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -635,7 +639,7 @@ public class IntroductionIntegrationTest // adapt outgoing message queue to removed message Group g2 = introductionGroupFactory .createIntroductionGroup(contact2From0); - decreaseOutgoingMessageCounter(ch, g2.getId(), 1); + decreaseOutgoingMessageCounter(ch, g2.getId()); // allow visitor to modify response boolean earlyAbort = visitor.visit(response); @@ -746,34 +750,33 @@ public class IntroductionIntegrationTest // create keys KeyPair keyPair1 = crypto.generateSignatureKeyPair(); KeyPair eKeyPair1 = crypto.generateAgreementKeyPair(); - byte[] ePublicKeyBytes1 = eKeyPair1.getPublic().getEncoded(); KeyPair eKeyPair2 = crypto.generateAgreementKeyPair(); - byte[] ePublicKeyBytes2 = eKeyPair2.getPublic().getEncoded(); // Nonce 1 - SecretKey secretKey = - crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1, true); - byte[] nonce1 = crypto.deriveSignatureNonce(secretKey, true); + SecretKey sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + eKeyPair2.getPublic(), eKeyPair1, true); + byte[] nonce1 = crypto.deriveKeyBindingNonce(ALICE_NONCE_LABEL, + sharedSecret); // Signature 1 - byte[] sig1 = crypto.sign(SIGNING_LABEL_RESPONSE, nonce1, + byte[] sig1 = crypto.sign(SIGNING_LABEL, nonce1, keyPair1.getPrivate().getEncoded()); // MAC 1 - SecretKey macKey1 = crypto.deriveMacKey(secretKey, true); + SecretKey macKey1 = crypto.deriveKey(ALICE_MAC_KEY_LABEL, sharedSecret); BdfDictionary tp1 = BdfDictionary.of(new BdfEntry("fake", "fake")); long time1 = clock.currentTimeMillis(); BdfList toMacList = BdfList.of(keyPair1.getPublic().getEncoded(), - ePublicKeyBytes1, tp1, time1); + eKeyPair1.getPublic().getEncoded(), tp1, time1); byte[] toMac = clientHelper.toByteArray(toMacList); - byte[] mac1 = crypto.mac(macKey1, toMac); + byte[] mac1 = crypto.mac(MAC_LABEL, macKey1, toMac); // create only relevant part of state for introducee2 BdfDictionary state = new BdfDictionary(); state.put(PUBLIC_KEY, keyPair1.getPublic().getEncoded()); state.put(TRANSPORT, tp1); state.put(TIME, time1); - state.put(E_PUBLIC_KEY, ePublicKeyBytes1); + state.put(E_PUBLIC_KEY, eKeyPair1.getPublic().getEncoded()); state.put(MAC, mac1); state.put(MAC_KEY, macKey1.getBytes()); state.put(NONCE, nonce1); @@ -786,16 +789,16 @@ public class IntroductionIntegrationTest // replace ephemeral key pair and recalculate matching keys and nonce KeyPair eKeyPair1f = crypto.generateAgreementKeyPair(); byte[] ePublicKeyBytes1f = eKeyPair1f.getPublic().getEncoded(); - secretKey = - crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1f, true); - nonce1 = crypto.deriveSignatureNonce(secretKey, true); + sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL, + eKeyPair2.getPublic(), eKeyPair1f, true); + nonce1 = crypto.deriveKeyBindingNonce(ALICE_NONCE_LABEL, sharedSecret); // recalculate MAC - macKey1 = crypto.deriveMacKey(secretKey, true); + macKey1 = crypto.deriveKey(ALICE_MAC_KEY_LABEL, sharedSecret); toMacList = BdfList.of(keyPair1.getPublic().getEncoded(), ePublicKeyBytes1f, tp1, time1); toMac = clientHelper.toByteArray(toMacList); - mac1 = crypto.mac(macKey1, toMac); + mac1 = crypto.mac(MAC_LABEL, macKey1, toMac); // update state with faked information state.put(E_PUBLIC_KEY, ePublicKeyBytes1f); @@ -970,12 +973,12 @@ public class IntroductionIntegrationTest } - private void decreaseOutgoingMessageCounter(ClientHelper ch, GroupId g, - int num) throws FormatException, DbException { + private void decreaseOutgoingMessageCounter(ClientHelper ch, GroupId g) + throws FormatException, DbException { BdfDictionary gD = ch.getGroupMetadataAsDictionary(g); LOG.warning(gD.toString()); BdfDictionary queue = gD.getDictionary(QUEUE_STATE_KEY); - queue.put("nextOut", queue.getLong("nextOut") - num); + queue.put("nextOut", queue.getLong("nextOut") - 1); gD.put(QUEUE_STATE_KEY, queue); ch.mergeGroupMetadata(g, gD); }