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..3a2be304a6fbc9d2e1529eebf72fc7f8794c1832 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 @@ -1,8 +1,5 @@ package org.briarproject.bramble.api.crypto; -import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.transport.TransportKeys; - import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -27,115 +24,35 @@ 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. - */ - SecretKey deriveMacKey(SecretKey master, boolean alice); - - /** - * 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. - */ - byte[] deriveSignatureNonce(SecretKey master, boolean alice); - - /** - * Derives a commitment to the provided public key. - * <p/> - * Part of BQP. + * Derives another secret key from the given secret key. * - * @param publicKey the public key - * @return the commitment to the provided public 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 */ - byte[] deriveKeyCommitment(byte[] publicKey); + SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); /** * 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 - * @return the shared secret - */ - SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair, - boolean alice) throws GeneralSecurityException; - - /** - * Derives the content of a confirmation record. - * <p/> - * Part of BQP. - * - * @param sharedSecret the common shared secret - * @param theirPayload the commit payload from the remote party - * @param ourPayload the commit payload we sent - * @param theirPublicKey the ephemeral public key of the remote party - * @param ourKeyPair our ephemeral keypair - * @param alice true if ourKeyPair belongs to 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, - 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 + * @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 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. - * @param alice whether the keys are for use by Alice or Bob. - */ - TransportKeys deriveTransportKeys(TransportId t, SecretKey master, - long rotationPeriod, boolean alice); - - /** - * Rotates the given transport keys to the given rotation period. If the - * keys are for a future rotation period they are not rotated. - */ - TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); - - /** Encodes the pseudo-random tag that is used to recognise a stream. */ - void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, - long streamNumber); + SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, + KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException; /** * 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 +70,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 +93,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/crypto/KeyAgreementCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/KeyAgreementCrypto.java new file mode 100644 index 0000000000000000000000000000000000000000..a35cd3114e26e3ff69e991fb2e35138faed2c042 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/KeyAgreementCrypto.java @@ -0,0 +1,50 @@ +package org.briarproject.bramble.api.crypto; + +/** + * Crypto operations for the key agreement protocol - see + * https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md + */ +public interface KeyAgreementCrypto { + + /** + * Hash label for public key commitment. + */ + String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT"; + + /** + * Key derivation label for confirmation record. + */ + String CONFIRMATION_KEY_LABEL = + "org.briarproject.bramble.keyagreement/CONFIRMATION_KEY"; + + /** + * MAC label for confirmation record. + */ + String CONFIRMATION_MAC_LABEL = + "org.briarproject.bramble.keyagreement/CONFIRMATION_MAC"; + + /** + * Derives a commitment to the provided public key. + * + * @param publicKey the public key + * @return the commitment to the provided public key. + */ + byte[] deriveKeyCommitment(PublicKey publicKey); + + /** + * Derives the content of a confirmation record. + * + * @param sharedSecret the common shared secret + * @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 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, + PublicKey theirPublicKey, KeyPair ourKeyPair, + boolean alice, boolean aliceRecord); +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java new file mode 100644 index 0000000000000000000000000000000000000000..6385d1f015bbf7d0dff30d0c03390caf7af11196 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java @@ -0,0 +1,32 @@ +package org.briarproject.bramble.api.crypto; + +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.transport.TransportKeys; + +/** + * Crypto operations for the transport security protocol - see + * https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md + */ +public interface TransportCrypto { + + /** + * Derives initial transport keys for the given transport in the given + * rotation period from the given master secret. + * + * @param alice whether the keys are for use by Alice or Bob. + */ + TransportKeys deriveTransportKeys(TransportId t, SecretKey master, + long rotationPeriod, boolean alice); + + /** + * Rotates the given transport keys to the given rotation period. If the + * keys are for the given period or any later period they are not rotated. + */ + TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); + + /** + * Encodes the pseudo-random tag that is used to recognise a stream. + */ + void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, + long streamNumber); +} 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/keyagreement/KeyAgreementTaskFactory.java b/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementTaskFactory.java deleted file mode 100644 index 40d875cc9d792e53a34c3d6f52ac6611c6fd9dea..0000000000000000000000000000000000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementTaskFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.briarproject.bramble.api.keyagreement; - -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; - -/** - * Manages tasks for conducting key agreements with remote peers. - */ -@NotNullByDefault -public interface KeyAgreementTaskFactory { - - /** - * Gets the current key agreement task. - */ - KeyAgreementTask createTask(); -} 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-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java index af9d09731dcb40aa8549797a7573037161f0d3c5..cf5502a5ff212c523cfef42c659bbc8cf26f8534 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java @@ -7,7 +7,7 @@ public interface TransportConstants { /** * The current version of the transport protocol. */ - int PROTOCOL_VERSION = 3; + int PROTOCOL_VERSION = 4; /** * The length of the pseudo-random tag in bytes. @@ -80,4 +80,32 @@ public interface TransportConstants { * The size of the reordering window. */ int REORDERING_WINDOW_SIZE = 32; + + /** + * Label for deriving Alice's initial tag key from the master secret. + */ + String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY"; + + /** + * Label for deriving Bob's initial tag key from the master secret. + */ + String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY"; + + /** + * Label for deriving Alice's initial header key from the master secret. + */ + String ALICE_HEADER_LABEL = + "org.briarproject.bramble.transport/ALICE_HEADER_KEY"; + + /** + * Label for deriving Bob's initial header key from the master secret. + */ + String BOB_HEADER_LABEL = + "org.briarproject.bramble.transport/BOB_HEADER_KEY"; + + /** + * Label for deriving the next period's key in key rotation. + */ + String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE"; + } 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..50a4f841cba5c01c95113c3c52bdb4eba537784c 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,8 @@ 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.mac(ALICE_NONCE_LABEL, masterSecret); + byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret); // Exchange pseudonyms, signed nonces, and timestamps long localTimestamp = clock.currentTimeMillis(); @@ -312,8 +313,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..37af381df94e92420b5437bb5fc334d1521cc7ec 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 @@ -10,11 +10,7 @@ import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; -import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.system.SecureRandomProvider; -import org.briarproject.bramble.api.transport.IncomingKeys; -import org.briarproject.bramble.api.transport.OutgoingKeys; -import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.StringUtils; import org.spongycastle.crypto.AsymmetricCipherKeyPair; @@ -30,7 +26,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; @@ -44,14 +39,8 @@ import java.util.logging.Logger; import javax.inject.Inject; import static java.util.logging.Level.INFO; -import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; -import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS; -import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; -import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; -import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; -import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; class CryptoComponentImpl implements CryptoComponent { @@ -65,39 +54,6 @@ 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"; - // 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"); - // 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"); - // 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"); - // KDF label for key rotation - private static final byte[] ROTATE = ascii("ROTATE"); private final SecureRandom secureRandom; private final ECKeyPairGenerator agreementKeyPairGenerator; @@ -263,179 +219,26 @@ 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)); - } - - @Override - public byte[] deriveSignatureNonce(SecretKey master, - boolean alice) { - return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE); + public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) { + byte[] mac = mac(label, k, inputs); + if (mac.length != SecretKey.LENGTH) throw new IllegalStateException(); + return new SecretKey(mac); } @Override - public byte[] deriveKeyCommitment(byte[] publicKey) { - byte[] hash = hash(COMMIT, publicKey); - // 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); - return commitment; - } - - @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; - } else { - alicePub = theirPublicKey; - bobPub = ourKeyPair.getPublic().getEncoded(); - } - return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub)); - } - - @Override - public byte[] deriveConfirmationRecord(SecretKey sharedSecret, - byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey, - KeyPair ourKeyPair, boolean alice, boolean aliceRecord) { - SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY)); - 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; - bobPayload = ourPayload; + alicePub = theirPublicKey.getEncoded(); 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)); - } - - @Override - public TransportKeys deriveTransportKeys(TransportId t, - SecretKey master, long rotationPeriod, boolean alice) { - // Keys for the previous period are derived from the master secret - SecretKey inTagPrev = deriveTagKey(master, t, !alice); - SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice); - SecretKey outTagPrev = deriveTagKey(master, t, alice); - SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice); - // Derive the keys for the current and next periods - SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod); - SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod); - SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1); - SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1); - SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod); - SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod); - // Initialise the reordering windows and stream counters - IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev, - rotationPeriod - 1); - IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr, - rotationPeriod); - IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext, - rotationPeriod + 1); - OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr, - rotationPeriod); - // Collect and return the keys - return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); - } - - @Override - public TransportKeys rotateTransportKeys(TransportKeys k, - long rotationPeriod) { - if (k.getRotationPeriod() >= rotationPeriod) return k; - IncomingKeys inPrev = k.getPreviousIncomingKeys(); - IncomingKeys inCurr = k.getCurrentIncomingKeys(); - IncomingKeys inNext = k.getNextIncomingKeys(); - OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); - long startPeriod = outCurr.getRotationPeriod(); - // Rotate the keys - for (long p = startPeriod + 1; p <= rotationPeriod; p++) { - inPrev = inCurr; - inCurr = inNext; - SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); - SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1); - inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1); - SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p); - SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p); - outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p); - } - // Collect and return the keys - return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext, - outCurr); - } - - 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)); - } - - 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)); - } - - 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)); - } - - @Override - public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, - long streamNumber) { - if (tag.length < TAG_LENGTH) throw new IllegalArgumentException(); - if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED) - throw new IllegalArgumentException(); - if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) - throw new IllegalArgumentException(); - // Initialise the PRF - Digest prf = new Blake2sDigest(tagKey.getBytes()); - // The output of the PRF must be long enough to use as a tag - int macLength = prf.getDigestSize(); - if (macLength < TAG_LENGTH) throw new IllegalStateException(); - // The input is the protocol version as a 16-bit integer, followed by - // the stream number as a 64-bit integer - byte[] protocolVersionBytes = new byte[INT_16_BYTES]; - ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0); - prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); - byte[] streamNumberBytes = new byte[INT_64_BYTES]; - ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0); - prf.update(streamNumberBytes, 0, streamNumberBytes.length); - byte[] mac = new byte[macLength]; - prf.doFinal(mac, 0); - // The output is the first TAG_LENGTH bytes of the MAC - System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); + return new SecretKey(hash(label, raw, alicePub, bobPub)); } @Override @@ -513,14 +316,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); @@ -612,30 +414,6 @@ class CryptoComponentImpl implements CryptoComponent { return AsciiArmour.wrap(b, lineLength); } - // 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; - } - // Password-based key derivation function - see PKCS#5 v2.1, section 5.2 private byte[] pbkdf2(String password, byte[] salt, int iterations) { byte[] utf8 = StringUtils.toUtf8(password); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoModule.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoModule.java index 37c37abf3ee2ad5d7fa3b22edbb323f91b6f2629..a40b656282556a5660b23973a5b611f4793dec3e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoModule.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoModule.java @@ -3,9 +3,11 @@ package org.briarproject.bramble.crypto; import org.briarproject.bramble.TimeLoggingExecutor; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoExecutor; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.StreamDecrypterFactory; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.system.SecureRandomProvider; @@ -74,6 +76,12 @@ public class CryptoModule { return new PasswordStrengthEstimatorImpl(); } + @Provides + TransportCrypto provideTransportCrypto( + TransportCryptoImpl transportCrypto) { + return transportCrypto; + } + @Provides StreamDecrypterFactory provideStreamDecrypterFactory( Provider<AuthenticatedCipher> cipherProvider) { @@ -81,9 +89,17 @@ public class CryptoModule { } @Provides - StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto, + StreamEncrypterFactory provideStreamEncrypterFactory( + CryptoComponent crypto, TransportCrypto transportCrypto, Provider<AuthenticatedCipher> cipherProvider) { - return new StreamEncrypterFactoryImpl(crypto, cipherProvider); + return new StreamEncrypterFactoryImpl(crypto, transportCrypto, + cipherProvider); + } + + @Provides + KeyAgreementCrypto provideKeyAgreementCrypto( + KeyAgreementCryptoImpl keyAgreementCrypto) { + return keyAgreementCrypto; } @Provides diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/KeyAgreementCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/KeyAgreementCryptoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..db2f19a7bc604b1200e47dfda0ed6e855fc2fcef --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/KeyAgreementCryptoImpl.java @@ -0,0 +1,56 @@ +package org.briarproject.bramble.crypto; + +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.PublicKey; +import org.briarproject.bramble.api.crypto.SecretKey; + +import javax.inject.Inject; + +import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; + +class KeyAgreementCryptoImpl implements KeyAgreementCrypto { + + private final CryptoComponent crypto; + + @Inject + KeyAgreementCryptoImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + @Override + public byte[] deriveKeyCommitment(PublicKey publicKey) { + byte[] hash = crypto.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); + return commitment; + } + + @Override + public byte[] deriveConfirmationRecord(SecretKey sharedSecret, + byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey, + KeyPair ourKeyPair, boolean alice, boolean aliceRecord) { + SecretKey ck = crypto.deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret); + byte[] alicePayload, alicePub, bobPayload, bobPub; + if (alice) { + alicePayload = ourPayload; + alicePub = ourKeyPair.getPublic().getEncoded(); + bobPayload = theirPayload; + bobPub = theirPublicKey.getEncoded(); + } else { + alicePayload = theirPayload; + alicePub = theirPublicKey.getEncoded(); + bobPayload = ourPayload; + bobPub = ourKeyPair.getPublic().getEncoded(); + } + if (aliceRecord) { + return crypto.mac(CONFIRMATION_MAC_LABEL, ck, alicePayload, + alicePub, bobPayload, bobPub); + } else { + return crypto.mac(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub, + alicePayload, alicePub); + } + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/StreamEncrypterFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/StreamEncrypterFactoryImpl.java index 0d9b4a0fc2bf65d2732d649575ed63bdbcd4d764..984d5e826271191676224b872ce5e95037ff12d0 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/StreamEncrypterFactoryImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/StreamEncrypterFactoryImpl.java @@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.StreamEncrypter; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.transport.StreamContext; @@ -22,12 +23,15 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { private final CryptoComponent crypto; + private final TransportCrypto transportCrypto; private final Provider<AuthenticatedCipher> cipherProvider; @Inject StreamEncrypterFactoryImpl(CryptoComponent crypto, + TransportCrypto transportCrypto, Provider<AuthenticatedCipher> cipherProvider) { this.crypto = crypto; + this.transportCrypto = transportCrypto; this.cipherProvider = cipherProvider; } @@ -37,7 +41,8 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { AuthenticatedCipher cipher = cipherProvider.get(); long streamNumber = ctx.getStreamNumber(); byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber); + transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, + streamNumber); byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH]; crypto.getSecureRandom().nextBytes(streamHeaderNonce); SecretKey frameKey = crypto.generateSecretKey(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ea992491716e6f0e6f57231703086857aacefc6b --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java @@ -0,0 +1,135 @@ +package org.briarproject.bramble.crypto; + +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.crypto.TransportCrypto; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.OutgoingKeys; +import org.briarproject.bramble.api.transport.TransportKeys; +import org.briarproject.bramble.util.ByteUtils; +import org.briarproject.bramble.util.StringUtils; +import org.spongycastle.crypto.Digest; + +import javax.inject.Inject; + +import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; +import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; +import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; +import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; +import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; + +class TransportCryptoImpl implements TransportCrypto { + + private final CryptoComponent crypto; + + @Inject + TransportCryptoImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + @Override + public TransportKeys deriveTransportKeys(TransportId t, + SecretKey master, long rotationPeriod, boolean alice) { + // Keys for the previous period are derived from the master secret + SecretKey inTagPrev = deriveTagKey(master, t, !alice); + SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice); + SecretKey outTagPrev = deriveTagKey(master, t, alice); + SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice); + // Derive the keys for the current and next periods + SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod); + SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod); + SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1); + SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1); + SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod); + SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod); + // Initialise the reordering windows and stream counters + IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev, + rotationPeriod - 1); + IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr, + rotationPeriod); + IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext, + rotationPeriod + 1); + OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr, + rotationPeriod); + // Collect and return the keys + return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); + } + + @Override + public TransportKeys rotateTransportKeys(TransportKeys k, + long rotationPeriod) { + if (k.getRotationPeriod() >= rotationPeriod) return k; + IncomingKeys inPrev = k.getPreviousIncomingKeys(); + IncomingKeys inCurr = k.getCurrentIncomingKeys(); + IncomingKeys inNext = k.getNextIncomingKeys(); + OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); + long startPeriod = outCurr.getRotationPeriod(); + // Rotate the keys + for (long p = startPeriod + 1; p <= rotationPeriod; p++) { + inPrev = inCurr; + inCurr = inNext; + SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); + SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1); + inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1); + SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p); + SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p); + outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p); + } + // Collect and return the keys + return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext, + outCurr); + } + + private SecretKey rotateKey(SecretKey k, long rotationPeriod) { + byte[] period = new byte[INT_64_BYTES]; + ByteUtils.writeUint64(rotationPeriod, period, 0); + return crypto.deriveKey(ROTATE_LABEL, k, period); + } + + private SecretKey deriveTagKey(SecretKey master, TransportId t, + boolean alice) { + String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL; + byte[] id = StringUtils.toUtf8(t.getString()); + return crypto.deriveKey(label, master, id); + } + + private SecretKey deriveHeaderKey(SecretKey master, TransportId t, + boolean alice) { + String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL; + byte[] id = StringUtils.toUtf8(t.getString()); + return crypto.deriveKey(label, master, id); + } + + @Override + public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, + long streamNumber) { + if (tag.length < TAG_LENGTH) throw new IllegalArgumentException(); + if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED) + throw new IllegalArgumentException(); + if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) + throw new IllegalArgumentException(); + // Initialise the PRF + Digest prf = new Blake2sDigest(tagKey.getBytes()); + // The output of the PRF must be long enough to use as a tag + int macLength = prf.getDigestSize(); + if (macLength < TAG_LENGTH) throw new IllegalStateException(); + // The input is the protocol version as a 16-bit integer, followed by + // the stream number as a 64-bit integer + byte[] protocolVersionBytes = new byte[INT_16_BYTES]; + ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0); + prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); + byte[] streamNumberBytes = new byte[INT_64_BYTES]; + ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0); + prf.update(streamNumberBytes, 0, streamNumberBytes.length); + byte[] mac = new byte[macLength]; + prf.doFinal(mac, 0); + // The output is the first TAG_LENGTH bytes of the MAC + System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); + } +} 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..89bf01ee0d8fd0d88cbb8b4c14103b5b752d1ed0 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 @@ -1,6 +1,6 @@ package org.briarproject.bramble.keyagreement; -import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; @@ -46,7 +46,7 @@ class KeyAgreementConnector { private final Callbacks callbacks; private final Clock clock; - private final CryptoComponent crypto; + private final KeyAgreementCrypto keyAgreementCrypto; private final PluginManager pluginManager; private final CompletionService<KeyAgreementConnection> connect; @@ -58,11 +58,11 @@ class KeyAgreementConnector { private volatile boolean alice = false; KeyAgreementConnector(Callbacks callbacks, Clock clock, - CryptoComponent crypto, PluginManager pluginManager, + KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager, Executor ioExecutor) { this.callbacks = callbacks; this.clock = clock; - this.crypto = crypto; + this.keyAgreementCrypto = keyAgreementCrypto; this.pluginManager = pluginManager; connect = new ExecutorCompletionService<>(ioExecutor); } @@ -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 = keyAgreementCrypto.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/KeyAgreementModule.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementModule.java index 9875387db916d282d9bc382077b25d37a8d40286..e7ec82dabb1b8b010c73e5188ef00f8d536714d3 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementModule.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementModule.java @@ -1,19 +1,10 @@ package org.briarproject.bramble.keyagreement; -import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfWriterFactory; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory; +import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadParser; -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.plugin.PluginManager; -import org.briarproject.bramble.api.system.Clock; - -import java.util.concurrent.Executor; - -import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @@ -22,13 +13,9 @@ import dagger.Provides; public class KeyAgreementModule { @Provides - @Singleton - KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock, - CryptoComponent crypto, EventBus eventBus, - @IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder, - PluginManager pluginManager) { - return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus, - ioExecutor, payloadEncoder, pluginManager); + KeyAgreementTask provideKeyAgreementTask( + KeyAgreementTaskImpl keyAgreementTask) { + return keyAgreementTask; } @Provides 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..a5c24c6e4231101717ba2fc892f98875196b666c 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 @@ -1,7 +1,10 @@ package org.briarproject.bramble.keyagreement; import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; 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 +14,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/> @@ -57,6 +63,7 @@ class KeyAgreementProtocol { private final Callbacks callbacks; private final CryptoComponent crypto; + private final KeyAgreementCrypto keyAgreementCrypto; private final PayloadEncoder payloadEncoder; private final KeyAgreementTransport transport; private final Payload theirPayload, ourPayload; @@ -64,11 +71,13 @@ class KeyAgreementProtocol { private final boolean alice; KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto, + KeyAgreementCrypto keyAgreementCrypto, PayloadEncoder payloadEncoder, KeyAgreementTransport transport, Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair, boolean alice) { this.callbacks = callbacks; this.crypto = crypto; + this.keyAgreementCrypto = keyAgreementCrypto; this.payloadEncoder = payloadEncoder; this.transport = transport; this.theirPayload = theirPayload; @@ -86,7 +95,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 +113,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,27 +124,34 @@ 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 = keyAgreementCrypto.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, + byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s, payloadEncoder.encode(theirPayload), payloadEncoder.encode(ourPayload), theirPublicKey, ourKeyPair, @@ -143,10 +159,10 @@ 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, + byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s, payloadEncoder.encode(theirPayload), payloadEncoder.encode(ourPayload), theirPublicKey, ourKeyPair, diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskFactoryImpl.java deleted file mode 100644 index 4d9dc00cf684cb5d2cf4d2454719768d218015f0..0000000000000000000000000000000000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskFactoryImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.briarproject.bramble.keyagreement; - -import org.briarproject.bramble.api.crypto.CryptoComponent; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory; -import org.briarproject.bramble.api.keyagreement.PayloadEncoder; -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.PluginManager; -import org.briarproject.bramble.api.system.Clock; - -import java.util.concurrent.Executor; - -import javax.annotation.concurrent.Immutable; -import javax.inject.Inject; - -@Immutable -@NotNullByDefault -class KeyAgreementTaskFactoryImpl implements KeyAgreementTaskFactory { - - private final Clock clock; - private final CryptoComponent crypto; - private final EventBus eventBus; - private final Executor ioExecutor; - private final PayloadEncoder payloadEncoder; - private final PluginManager pluginManager; - - @Inject - KeyAgreementTaskFactoryImpl(Clock clock, CryptoComponent crypto, - EventBus eventBus, @IoExecutor Executor ioExecutor, - PayloadEncoder payloadEncoder, PluginManager pluginManager) { - this.clock = clock; - this.crypto = crypto; - this.eventBus = eventBus; - this.ioExecutor = ioExecutor; - this.payloadEncoder = payloadEncoder; - this.pluginManager = pluginManager; - } - - @Override - public KeyAgreementTask createTask() { - return new KeyAgreementTaskImpl(clock, crypto, eventBus, payloadEncoder, - pluginManager, ioExecutor); - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java index de6dcecf753b19c351413aa7dfeab9fbc11f8fe7..e0d97313dc9ed6b1e8dc76427b1ea76502ebf50e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.keyagreement; import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.event.EventBus; @@ -14,6 +15,7 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent; +import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.PluginManager; @@ -23,6 +25,8 @@ import java.io.IOException; import java.util.concurrent.Executor; import java.util.logging.Logger; +import javax.inject.Inject; + import static java.util.logging.Level.WARNING; @MethodsNotNullByDefault @@ -35,6 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements Logger.getLogger(KeyAgreementTaskImpl.class.getName()); private final CryptoComponent crypto; + private final KeyAgreementCrypto keyAgreementCrypto; private final EventBus eventBus; private final PayloadEncoder payloadEncoder; private final KeyPair localKeyPair; @@ -43,14 +48,17 @@ class KeyAgreementTaskImpl extends Thread implements private Payload localPayload; private Payload remotePayload; + @Inject KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto, - EventBus eventBus, PayloadEncoder payloadEncoder, - PluginManager pluginManager, Executor ioExecutor) { + KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus, + PayloadEncoder payloadEncoder, PluginManager pluginManager, + @IoExecutor Executor ioExecutor) { this.crypto = crypto; + this.keyAgreementCrypto = keyAgreementCrypto; this.eventBus = eventBus; this.payloadEncoder = payloadEncoder; localKeyPair = crypto.generateAgreementKeyPair(); - connector = new KeyAgreementConnector(this, clock, crypto, + connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto, pluginManager, ioExecutor); } @@ -100,8 +108,8 @@ class KeyAgreementTaskImpl extends Thread implements // Run BQP protocol over the connection LOG.info("Starting BQP protocol"); KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto, - payloadEncoder, transport, remotePayload, localPayload, - localKeyPair, alice); + keyAgreementCrypto, payloadEncoder, transport, remotePayload, + localPayload, localKeyPair, alice); try { SecretKey master = protocol.perform(); KeyAgreementResult result = diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerFactoryImpl.java index d212a027b98f78304f31e6e2e3b6d9b544fca8b0..3a04f121a656837de2f02789f277d1ea7c65a095 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerFactoryImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerFactoryImpl.java @@ -1,6 +1,6 @@ package org.briarproject.bramble.transport; -import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -20,17 +20,18 @@ class TransportKeyManagerFactoryImpl implements TransportKeyManagerFactory { private final DatabaseComponent db; - private final CryptoComponent crypto; + private final TransportCrypto transportCrypto; private final Executor dbExecutor; private final ScheduledExecutorService scheduler; private final Clock clock; @Inject - TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto, + TransportKeyManagerFactoryImpl(DatabaseComponent db, + TransportCrypto transportCrypto, @DatabaseExecutor Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler, Clock clock) { this.db = db; - this.crypto = crypto; + this.transportCrypto = transportCrypto; this.dbExecutor = dbExecutor; this.scheduler = scheduler; this.clock = clock; @@ -39,8 +40,8 @@ class TransportKeyManagerFactoryImpl implements @Override public TransportKeyManager createTransportKeyManager( TransportId transportId, long maxLatency) { - return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, - clock, transportId, maxLatency); + return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor, + scheduler, clock, transportId, maxLatency); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java index 95e49f55346b6fc13ce6cf04b668bc5d12a41818..60b48427ff18f07703a147f827ae39639c6f629e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java @@ -2,8 +2,8 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; @@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { Logger.getLogger(TransportKeyManagerImpl.class.getName()); private final DatabaseComponent db; - private final CryptoComponent crypto; + private final TransportCrypto transportCrypto; private final Executor dbExecutor; private final ScheduledExecutorService scheduler; private final Clock clock; @@ -54,11 +54,12 @@ class TransportKeyManagerImpl implements TransportKeyManager { private final Map<ContactId, MutableOutgoingKeys> outContexts; private final Map<ContactId, MutableTransportKeys> keys; - TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, - Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler, - Clock clock, TransportId transportId, long maxLatency) { + TransportKeyManagerImpl(DatabaseComponent db, + TransportCrypto transportCrypto, Executor dbExecutor, + @Scheduler ScheduledExecutorService scheduler, Clock clock, + TransportId transportId, long maxLatency) { this.db = db; - this.crypto = crypto; + this.transportCrypto = transportCrypto; this.dbExecutor = dbExecutor; this.scheduler = scheduler; this.clock = clock; @@ -99,7 +100,8 @@ class TransportKeyManagerImpl implements TransportKeyManager { for (Entry<ContactId, TransportKeys> e : keys.entrySet()) { ContactId c = e.getKey(); TransportKeys k = e.getValue(); - TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod); + TransportKeys k1 = + transportCrypto.rotateTransportKeys(k, rotationPeriod); if (k1.getRotationPeriod() > k.getRotationPeriod()) rotationResult.rotated.put(c, k1); rotationResult.current.put(c, k1); @@ -127,7 +129,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { for (long streamNumber : inKeys.getWindow().getUnseen()) { TagContext tagCtx = new TagContext(c, inKeys, streamNumber); byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION, + transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION, streamNumber); inContexts.put(new Bytes(tag), tagCtx); } @@ -162,11 +164,11 @@ class TransportKeyManagerImpl implements TransportKeyManager { // Work out what rotation period the timestamp belongs to long rotationPeriod = timestamp / rotationPeriodLength; // Derive the transport keys - TransportKeys k = crypto.deriveTransportKeys(transportId, master, - rotationPeriod, alice); + TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + master, rotationPeriod, alice); // Rotate the keys to the current rotation period if necessary rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength; - k = crypto.rotateTransportKeys(k, rotationPeriod); + k = transportCrypto.rotateTransportKeys(k, rotationPeriod); // Initialise mutable state for the contact addKeys(c, new MutableTransportKeys(k)); // Write the keys back to the DB @@ -234,8 +236,8 @@ class TransportKeyManagerImpl implements TransportKeyManager { // Add tags for any stream numbers added to the window for (long streamNumber : change.getAdded()) { byte[] addTag = new byte[TAG_LENGTH]; - crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION, - streamNumber); + transportCrypto.encodeTag(addTag, inKeys.getTagKey(), + PROTOCOL_VERSION, streamNumber); inContexts.put(new Bytes(addTag), new TagContext( tagCtx.contactId, inKeys, streamNumber)); } @@ -243,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { for (long streamNumber : change.getRemoved()) { if (streamNumber == tagCtx.streamNumber) continue; byte[] removeTag = new byte[TAG_LENGTH]; - crypto.encodeTag(removeTag, inKeys.getTagKey(), + transportCrypto.encodeTag(removeTag, inKeys.getTagKey(), PROTOCOL_VERSION, streamNumber); inContexts.remove(new Bytes(removeTag)); } 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/KeyDerivationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java index 7a3e67c4c2f5d09a5773585a2122d740e5c24f1d..805816e9233064e35064335069014e438d38670d 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java @@ -3,11 +3,11 @@ package org.briarproject.bramble.crypto; import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.transport.TransportKeys; 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.ArrayList; @@ -16,35 +16,34 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class KeyDerivationTest extends BrambleTestCase { + private final CryptoComponent crypto = + new CryptoComponentImpl(new TestSecureRandomProvider()); + private final TransportCrypto transportCrypto = + new TransportCryptoImpl(crypto); private final TransportId transportId = new TransportId("id"); - private final CryptoComponent crypto; - private final SecretKey master; - - public KeyDerivationTest() { - crypto = new CryptoComponentImpl(new TestSecureRandomProvider()); - master = TestUtils.getSecretKey(); - } + private final SecretKey master = getSecretKey(); @Test public void testKeysAreDistinct() { - TransportKeys k = crypto.deriveTransportKeys(transportId, master, - 123, true); + TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); assertAllDifferent(k); } @Test public void testCurrentKeysMatchCurrentKeysOfContact() { // Start in rotation period 123 - TransportKeys kA = crypto.deriveTransportKeys(transportId, master, - 123, true); - TransportKeys kB = crypto.deriveTransportKeys(transportId, master, - 123, false); + TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); + TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + master, 123, false); // Alice's incoming keys should equal Bob's outgoing keys assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(), kB.getCurrentOutgoingKeys().getTagKey().getBytes()); @@ -56,8 +55,8 @@ public class KeyDerivationTest extends BrambleTestCase { assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(), kB.getCurrentIncomingKeys().getHeaderKey().getBytes()); // Rotate into the future - kA = crypto.rotateTransportKeys(kA, 456); - kB = crypto.rotateTransportKeys(kB, 456); + kA = transportCrypto.rotateTransportKeys(kA, 456); + kB = transportCrypto.rotateTransportKeys(kB, 456); // Alice's incoming keys should equal Bob's outgoing keys assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(), kB.getCurrentOutgoingKeys().getTagKey().getBytes()); @@ -73,22 +72,23 @@ public class KeyDerivationTest extends BrambleTestCase { @Test public void testPreviousKeysMatchPreviousKeysOfContact() { // Start in rotation period 123 - TransportKeys kA = crypto.deriveTransportKeys(transportId, master, - 123, true); - TransportKeys kB = crypto.deriveTransportKeys(transportId, master, - 123, false); + TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); + TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + master, 123, false); // Compare Alice's previous keys in period 456 with Bob's current keys // in period 455 - kA = crypto.rotateTransportKeys(kA, 456); - kB = crypto.rotateTransportKeys(kB, 455); + kA = transportCrypto.rotateTransportKeys(kA, 456); + kB = transportCrypto.rotateTransportKeys(kB, 455); // Alice's previous incoming keys should equal Bob's outgoing keys assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(), kB.getCurrentOutgoingKeys().getTagKey().getBytes()); - assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(), + assertArrayEquals( + kA.getPreviousIncomingKeys().getHeaderKey().getBytes(), kB.getCurrentOutgoingKeys().getHeaderKey().getBytes()); // Compare Alice's current keys in period 456 with Bob's previous keys // in period 457 - kB = crypto.rotateTransportKeys(kB, 457); + kB = transportCrypto.rotateTransportKeys(kB, 457); // Alice's outgoing keys should equal Bob's previous incoming keys assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(), kB.getPreviousIncomingKeys().getTagKey().getBytes()); @@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase { @Test public void testNextKeysMatchNextKeysOfContact() { // Start in rotation period 123 - TransportKeys kA = crypto.deriveTransportKeys(transportId, master, - 123, true); - TransportKeys kB = crypto.deriveTransportKeys(transportId, master, - 123, false); + TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); + TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + master, 123, false); // Compare Alice's current keys in period 456 with Bob's next keys in // period 455 - kA = crypto.rotateTransportKeys(kA, 456); - kB = crypto.rotateTransportKeys(kB, 455); + kA = transportCrypto.rotateTransportKeys(kA, 456); + kB = transportCrypto.rotateTransportKeys(kB, 455); // Alice's outgoing keys should equal Bob's next incoming keys assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(), kB.getNextIncomingKeys().getTagKey().getBytes()); @@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase { kB.getNextIncomingKeys().getHeaderKey().getBytes()); // Compare Alice's next keys in period 456 with Bob's current keys // in period 457 - kB = crypto.rotateTransportKeys(kB, 457); + kB = transportCrypto.rotateTransportKeys(kB, 457); // Alice's next incoming keys should equal Bob's outgoing keys assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(), kB.getCurrentOutgoingKeys().getTagKey().getBytes()); @@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase { @Test public void testMasterKeyAffectsOutput() { - SecretKey master1 = TestUtils.getSecretKey(); + SecretKey master1 = getSecretKey(); assertFalse(Arrays.equals(master.getBytes(), master1.getBytes())); - TransportKeys k = crypto.deriveTransportKeys(transportId, master, - 123, true); - TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1, - 123, true); + TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); + TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId, + master1, 123, true); assertAllDifferent(k, k1); } @@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase { public void testTransportIdAffectsOutput() { TransportId transportId1 = new TransportId("id1"); assertFalse(transportId.getString().equals(transportId1.getString())); - TransportKeys k = crypto.deriveTransportKeys(transportId, master, - 123, true); - TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master, - 123, true); + TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + master, 123, true); + TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1, + master, 123, true); assertAllDifferent(k, k1); } 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/crypto/TagEncodingTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java index cfbc6d37529510c2ce37260eaa53c8a05ee4493d..b6cce5ae242432a2bf9ccb9409d20bd494df5d9e 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java @@ -3,9 +3,8 @@ package org.briarproject.bramble.crypto; import org.briarproject.bramble.api.Bytes; 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.briarproject.bramble.api.crypto.TransportCrypto; +import org.briarproject.bramble.test.BrambleMockTestCase; import org.junit.Test; import java.util.HashSet; @@ -14,25 +13,25 @@ import java.util.Set; import static junit.framework.TestCase.assertTrue; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; -public class TagEncodingTest extends BrambleTestCase { +public class TagEncodingTest extends BrambleMockTestCase { - private final CryptoComponent crypto; - private final SecretKey tagKey; - private final long streamNumber = 1234567890; + private final CryptoComponent crypto = context.mock(CryptoComponent.class); - public TagEncodingTest() { - crypto = new CryptoComponentImpl(new TestSecureRandomProvider()); - tagKey = TestUtils.getSecretKey(); - } + private final TransportCrypto transportCrypto = + new TransportCryptoImpl(crypto); + private final SecretKey tagKey = getSecretKey(); + private final long streamNumber = 1234567890; @Test public void testKeyAffectsTag() throws Exception { Set<Bytes> set = new HashSet<>(); for (int i = 0; i < 100; i++) { byte[] tag = new byte[TAG_LENGTH]; - SecretKey tagKey = TestUtils.getSecretKey(); - crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber); + SecretKey tagKey = getSecretKey(); + transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, + streamNumber); assertTrue(set.add(new Bytes(tag))); } } @@ -42,7 +41,8 @@ public class TagEncodingTest extends BrambleTestCase { Set<Bytes> set = new HashSet<>(); for (int i = 0; i < 100; i++) { byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber); + transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, + streamNumber); assertTrue(set.add(new Bytes(tag))); } } @@ -52,7 +52,8 @@ public class TagEncodingTest extends BrambleTestCase { Set<Bytes> set = new HashSet<>(); for (int i = 0; i < 100; i++) { byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i); + transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, + streamNumber + i); assertTrue(set.add(new Bytes(tag))); } } 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..c6c55ce0407b765f9832aa3c88eaa59ef3aaf64f 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 @@ -1,13 +1,14 @@ package org.briarproject.bramble.keyagreement; import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; 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 +17,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 +33,33 @@ 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 + KeyAgreementCrypto keyAgreementCrypto; + @Mock + KeyParser keyParser; + @Mock PayloadEncoder payloadEncoder; @Mock KeyAgreementTransport transport; @@ -65,60 +69,67 @@ 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, - transport, theirPayload, ourPayload, ourKeyPair, true); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, true); // expectations 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(keyAgreementCrypto).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(keyAgreementCrypto).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(keyAgreementCrypto).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,59 +140,66 @@ 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, - transport, theirPayload, ourPayload, ourKeyPair, false); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, false); // expectations 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(keyAgreementCrypto).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(keyAgreementCrypto).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(keyAgreementCrypto).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,38 +210,43 @@ 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 = - new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, - transport, theirPayload, ourPayload, ourKeyPair, true); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, true); // expectations 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(keyAgreementCrypto).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,34 +256,38 @@ 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 = - new KeyAgreementProtocol(callbacks, crypto, payloadEncoder, - transport, theirPayload, ourPayload, ourKeyPair, false); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, false); // expectations 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(keyAgreementCrypto).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,62 +297,69 @@ 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, - transport, theirPayload, ourPayload, ourKeyPair, true); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, true); // expectations 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(keyAgreementCrypto).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(keyAgreementCrypto).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(keyAgreementCrypto).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,56 +369,63 @@ 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, - transport, theirPayload, ourPayload, ourKeyPair, false); + KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, + crypto, keyAgreementCrypto, payloadEncoder, transport, + theirPayload, ourPayload, ourKeyPair, false); // expectations 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(keyAgreementCrypto).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(keyAgreementCrypto).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(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret, + alicePayload, bobPayload, alicePubKey, ourKeyPair, + false, false); }}); // execute diff --git a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java index bc5c86a8d5e1ec844885ddcc6ff760039f9c2a9c..7bb53e8435c05d56a0eec01b4eb944c4ab6a1cff 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java @@ -1,8 +1,8 @@ package org.briarproject.bramble.sync; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.ClientId; @@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase { @Inject RecordWriterFactory recordWriterFactory; @Inject - CryptoComponent crypto; + TransportCrypto transportCrypto; private final ContactId contactId; private final TransportId transportId; @@ -117,7 +117,8 @@ public class SyncIntegrationTest extends BrambleTestCase { private void read(byte[] connectionData) throws Exception { // Calculate the expected tag byte[] expectedTag = new byte[TAG_LENGTH]; - crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber); + transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, + streamNumber); // Read the tag InputStream in = new ByteArrayInputStream(connectionData); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java index 0cdfaeb561228cfd7d22c991a7dae780ba5becb4..4dc1f980240d9d91daa57860c5da1ea40d47b93d 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java @@ -1,8 +1,8 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.plugin.TransportId; @@ -11,12 +11,11 @@ import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.TransportKeys; -import org.briarproject.bramble.test.BrambleTestCase; +import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.RunAction; import org.briarproject.bramble.test.TestUtils; import org.hamcrest.Description; import org.jmock.Expectations; -import org.jmock.Mockery; import org.jmock.api.Action; import org.jmock.api.Invocation; import org.junit.Test; @@ -41,7 +40,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -public class TransportKeyManagerImplTest extends BrambleTestCase { +public class TransportKeyManagerImplTest extends BrambleMockTestCase { + + private final DatabaseComponent db = context.mock(DatabaseComponent.class); + private final TransportCrypto transportCrypto = + context.mock(TransportCrypto.class); + private final Executor dbExecutor = context.mock(Executor.class); + private final ScheduledExecutorService scheduler = + context.mock(ScheduledExecutorService.class); + private final Clock clock = context.mock(Clock.class); private final TransportId transportId = new TransportId("id"); private final long maxLatency = 30 * 1000; // 30 seconds @@ -55,14 +62,6 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { @Test public void testKeysAreRotatedAtStartup() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>(); TransportKeys shouldRotate = createTransportKeys(900, 0); TransportKeys shouldNotRotate = createTransportKeys(1000, 0); @@ -79,14 +78,15 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { oneOf(db).getTransportKeys(txn, transportId); will(returnValue(loaded)); // Rotate the transport keys - oneOf(crypto).rotateTransportKeys(shouldRotate, 1000); + oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000); will(returnValue(rotated)); - oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000); + oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000); will(returnValue(shouldNotRotate)); // Encode the tags (3 sets per contact) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(6).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(6).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Save the keys that were rotated @@ -97,161 +97,124 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { with(rotationPeriodLength - 1), with(MILLISECONDS)); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); transportKeyManager.start(txn); - - context.assertIsSatisfied(); } @Test public void testKeysAreRotatedWhenAddingContact() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - - boolean alice = true; + boolean alice = random.nextBoolean(); TransportKeys transportKeys = createTransportKeys(999, 0); TransportKeys rotated = createTransportKeys(1000, 0); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999, - alice); + oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, + 999, alice); will(returnValue(transportKeys)); // Get the current time (1 ms after start of rotation period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1000 + 1)); // Rotate the transport keys - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(rotated)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Save the keys oneOf(db).addTransportKeys(txn, contactId, rotated); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); // The timestamp is 1 ms before the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000 - 1; transportKeyManager.addContact(txn, contactId, masterKey, timestamp, alice); - - context.assertIsSatisfied(); } @Test public void testOutgoingStreamContextIsNullIfContactIsNotFound() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - Transaction txn = new Transaction(null, false); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); assertNull(transportKeyManager.getStreamContext(txn, contactId)); - - context.assertIsSatisfied(); } @Test public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - - boolean alice = true; + boolean alice = random.nextBoolean(); // The stream counter has been exhausted TransportKeys transportKeys = createTransportKeys(1000, MAX_32_BIT_UNSIGNED + 1); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, - alice); + oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, + 1000, alice); will(returnValue(transportKeys)); // Get the current time (the start of rotation period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1000)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Rotate the transport keys (the keys are unaffected) - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; transportKeyManager.addContact(txn, contactId, masterKey, timestamp, alice); assertNull(transportKeyManager.getStreamContext(txn, contactId)); - - context.assertIsSatisfied(); } @Test public void testOutgoingStreamCounterIsIncremented() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - - boolean alice = true; + boolean alice = random.nextBoolean(); // The stream counter can be used one more time before being exhausted TransportKeys transportKeys = createTransportKeys(1000, MAX_32_BIT_UNSIGNED); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, - alice); + oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, + 1000, alice); will(returnValue(transportKeys)); // Get the current time (the start of rotation period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1000)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Rotate the transport keys (the keys are unaffected) - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); @@ -259,9 +222,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; transportKeyManager.addContact(txn, contactId, masterKey, timestamp, @@ -277,94 +240,76 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber()); // The second request should return null, the counter is exhausted assertNull(transportKeyManager.getStreamContext(txn, contactId)); - - context.assertIsSatisfied(); } @Test public void testIncomingStreamContextIsNullIfTagIsNotFound() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - - boolean alice = true; + boolean alice = random.nextBoolean(); TransportKeys transportKeys = createTransportKeys(1000, 0); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, - alice); + oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, + 1000, alice); will(returnValue(transportKeys)); // Get the current time (the start of rotation period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1000)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Rotate the transport keys (the keys are unaffected) - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; transportKeyManager.addContact(txn, contactId, masterKey, timestamp, alice); assertNull(transportKeyManager.getStreamContext(txn, new byte[TAG_LENGTH])); - - context.assertIsSatisfied(); } @Test public void testTagIsNotRecognisedTwice() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - - boolean alice = true; + boolean alice = random.nextBoolean(); TransportKeys transportKeys = createTransportKeys(1000, 0); // Keep a copy of the tags List<byte[]> tags = new ArrayList<>(); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, - alice); + oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, + 1000, alice); will(returnValue(transportKeys)); // Get the current time (the start of rotation period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1000)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction(tags)); } // Rotate the transport keys (the keys are unaffected) - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); // Encode a new tag after sliding the window - oneOf(crypto).encodeTag(with(any(byte[].class)), + oneOf(transportCrypto).encodeTag(with(any(byte[].class)), with(tagKey), with(PROTOCOL_VERSION), with((long) REORDERING_WINDOW_SIZE)); will(new EncodeTagAction(tags)); @@ -373,9 +318,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { 1, new byte[REORDERING_WINDOW_SIZE / 8]); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; transportKeyManager.addContact(txn, contactId, masterKey, timestamp, @@ -395,20 +340,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size()); // The second request should return null, the tag has already been used assertNull(transportKeyManager.getStreamContext(txn, tag)); - - context.assertIsSatisfied(); } @Test public void testKeysAreRotatedToCurrentPeriod() throws Exception { - Mockery context = new Mockery(); - DatabaseComponent db = context.mock(DatabaseComponent.class); - CryptoComponent crypto = context.mock(CryptoComponent.class); - Executor dbExecutor = context.mock(Executor.class); - ScheduledExecutorService scheduler = - context.mock(ScheduledExecutorService.class); - Clock clock = context.mock(Clock.class); - TransportKeys transportKeys = createTransportKeys(1000, 0); Map<ContactId, TransportKeys> loaded = Collections.singletonMap(contactId, transportKeys); @@ -424,12 +359,13 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { oneOf(db).getTransportKeys(txn, transportId); will(returnValue(loaded)); // Rotate the transport keys (the keys are unaffected) - oneOf(crypto).rotateTransportKeys(transportKeys, 1000); + oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Schedule key rotation at the start of the next rotation period @@ -445,13 +381,14 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(rotationPeriodLength * 1001)); // Rotate the transport keys - oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)), - with(1001L)); + oneOf(transportCrypto).rotateTransportKeys( + with(any(TransportKeys.class)), with(1001L)); will(returnValue(rotated)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { - exactly(3).of(crypto).encodeTag(with(any(byte[].class)), - with(tagKey), with(PROTOCOL_VERSION), with(i)); + exactly(3).of(transportCrypto).encodeTag( + with(any(byte[].class)), with(tagKey), + with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } // Save the keys that were rotated @@ -465,12 +402,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase { oneOf(db).endTransaction(txn1); }}); - TransportKeyManager - transportKeyManager = new TransportKeyManagerImpl(db, - crypto, dbExecutor, scheduler, clock, transportId, maxLatency); + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); transportKeyManager.start(txn); - - context.assertIsSatisfied(); } private TransportKeys createTransportKeys(long rotationPeriod, diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 763c50332da6d0ff26bf6ee5f683281c1900b95f..1c6cc9d86f359dc248f9c30f54198fc4274d94c6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -12,7 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.identity.IdentityManager; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory; +import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.lifecycle.IoExecutor; @@ -125,7 +125,7 @@ public interface AndroidComponent ContactExchangeTask contactExchangeTask(); - KeyAgreementTaskFactory keyAgreementTaskFactory(); + KeyAgreementTask keyAgreementTask(); PayloadEncoder payloadEncoder(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java index 54df56f3ae9caab020390a4e14aacd670a42c82d..fd0d46445404da75628165430afafab21f88aec5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java @@ -24,7 +24,6 @@ import com.google.zxing.Result; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory; import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadParser; @@ -48,6 +47,7 @@ import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; +import javax.inject.Provider; import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.EXTRA_STATE; @@ -68,7 +68,7 @@ public class ShowQrCodeFragment extends BaseEventFragment private static final Logger LOG = Logger.getLogger(TAG); @Inject - KeyAgreementTaskFactory keyAgreementTaskFactory; + Provider<KeyAgreementTask> keyAgreementTaskProvider; @Inject PayloadEncoder payloadEncoder; @Inject @@ -187,7 +187,7 @@ public class ShowQrCodeFragment extends BaseEventFragment @UiThread private void startListening() { KeyAgreementTask oldTask = task; - KeyAgreementTask newTask = keyAgreementTaskFactory.createTask(); + KeyAgreementTask newTask = keyAgreementTaskProvider.get(); task = newTask; ioExecutor.execute(() -> { if (oldTask != null) oldTask.stopListening(); 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..6ecfcef49cfa0b54d7ef7c305a7eedf66153f352 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,24 @@ 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 two MAC keys from the shared secret key + String ourNonceLabel = alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL; + String theirNonceLabel = alice ? BOB_NONCE_LABEL : ALICE_NONCE_LABEL; + byte[] ourNonce = cryptoComponent.mac(ourNonceLabel, secretKey); + byte[] theirNonce = cryptoComponent.mac(theirNonceLabel, secretKey); + String ourKeyLabel = alice ? ALICE_MAC_KEY_LABEL : BOB_MAC_KEY_LABEL; + String theirKeyLabel = alice ? BOB_MAC_KEY_LABEL : ALICE_MAC_KEY_LABEL; + SecretKey ourMacKey = cryptoComponent.deriveKey(ourKeyLabel, secretKey); + SecretKey theirMacKey = + cryptoComponent.deriveKey(theirKeyLabel, 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 +478,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 +492,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 +512,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..b6b088f9d5293f86e3a29ed9e630ca0cf54fb268 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,32 @@ 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.mac(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 +788,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.mac(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 +972,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); }