mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Merge branch '617-crypto-labels' into 'master'
Use namespaced labels for all crypto operations See merge request !632
This commit is contained in:
@@ -12,6 +12,27 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ContactExchangeTask {
|
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.
|
* Exchanges contact information with a remote peer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
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.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
@@ -27,115 +24,35 @@ public interface CryptoComponent {
|
|||||||
KeyParser getMessageKeyParser();
|
KeyParser getMessageKeyParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a stream header key from the given master secret.
|
* Derives another secret key from the given secret key.
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @param publicKey the public key
|
* @param label a namespaced label indicating the purpose of the derived
|
||||||
* @return the commitment to the provided public key.
|
* 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
|
* Derives a common shared secret from two public keys and one of the
|
||||||
* corresponding private keys.
|
* corresponding private keys.
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param theirPublicKey the ephemeral public key of the remote party
|
* @param label a namespaced label indicating the purpose of this shared
|
||||||
* @param ourKeyPair our ephemeral keypair
|
* secret, to prevent it from being repurposed or colliding with a shared
|
||||||
* @param alice true if ourKeyPair belongs to Alice
|
* 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
|
* @return the shared secret
|
||||||
*/
|
*/
|
||||||
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
boolean alice) throws GeneralSecurityException;
|
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
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given byte[] with the given ECDSA private key.
|
* Signs the given byte[] with the given ECDSA private key.
|
||||||
*
|
*
|
||||||
* @param label A label specific to this signature
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* to ensure that the signature cannot be repurposed
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
|
* signature created for another purpose
|
||||||
*/
|
*/
|
||||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
@@ -153,8 +70,9 @@ public interface CryptoComponent {
|
|||||||
* Verifies that the given signature is valid for the signed data
|
* Verifies that the given signature is valid for the signed data
|
||||||
* and the given ECDSA public key.
|
* and the given ECDSA public key.
|
||||||
*
|
*
|
||||||
* @param label A label that was specific to this signature
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* to ensure that the signature cannot be repurposed
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
|
* signature created for another purpose
|
||||||
* @return true if the signature was valid, false otherwise.
|
* @return true if the signature was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
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
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
* combined by prefixing each input with its length.
|
* combined by prefixing each input with its length.
|
||||||
*
|
*
|
||||||
* @param label A label specific to this hash to ensure that hashes
|
* @param label a namespaced label indicating the purpose of this hash, to
|
||||||
* calculated for distinct purposes don't collide.
|
* prevent it from being repurposed or colliding with a hash created for
|
||||||
|
* another purpose
|
||||||
*/
|
*/
|
||||||
byte[] hash(String label, byte[]... inputs);
|
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
|
* Returns a message authentication code with the given key over the
|
||||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||||
* input with its length.
|
* input with its length.
|
||||||
|
*
|
||||||
|
* @param label a namespaced label indicating the purpose of this MAC, to
|
||||||
|
* 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
|
* Encrypts and authenticates the given plaintext so it can be written to
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing authors to calculate their identities.
|
* 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) {
|
public AuthorId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
|
|||||||
/**
|
/**
|
||||||
* The current version of the BQP protocol.
|
* The current version of the BQP protocol.
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 2;
|
byte PROTOCOL_VERSION = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the record header in bytes.
|
* The length of the record header in bytes.
|
||||||
@@ -22,7 +22,10 @@ public interface KeyAgreementConstants {
|
|||||||
*/
|
*/
|
||||||
int COMMIT_LENGTH = 16;
|
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.
|
* The transport identifier for Bluetooth.
|
||||||
@@ -33,4 +36,16 @@ public interface KeyAgreementConstants {
|
|||||||
* The transport identifier for LAN.
|
* The transport identifier for LAN.
|
||||||
*/
|
*/
|
||||||
int TRANSPORT_ID_LAN = 1;
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing groups to calculate their identifiers.
|
* 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) {
|
public GroupId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing messages to calculate their identifiers.
|
* 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) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public interface TransportConstants {
|
|||||||
/**
|
/**
|
||||||
* The current version of the transport protocol.
|
* The current version of the transport protocol.
|
||||||
*/
|
*/
|
||||||
int PROTOCOL_VERSION = 3;
|
int PROTOCOL_VERSION = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the pseudo-random tag in bytes.
|
* The length of the pseudo-random tag in bytes.
|
||||||
@@ -80,4 +80,32 @@ public interface TransportConstants {
|
|||||||
* The size of the reordering window.
|
* The size of the reordering window.
|
||||||
*/
|
*/
|
||||||
int REORDERING_WINDOW_SIZE = 32;
|
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";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,8 +141,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Derive the header keys for the transport streams
|
// Derive the header keys for the transport streams
|
||||||
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
|
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
|
||||||
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
|
masterSecret);
|
||||||
|
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret);
|
||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
InputStream streamReader =
|
InputStream streamReader =
|
||||||
@@ -156,8 +157,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||||
|
|
||||||
// Derive the nonces to be signed
|
// Derive the nonces to be signed
|
||||||
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
|
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret);
|
||||||
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
|
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret);
|
||||||
|
|
||||||
// Exchange pseudonyms, signed nonces, and timestamps
|
// Exchange pseudonyms, signed nonces, and timestamps
|
||||||
long localTimestamp = clock.currentTimeMillis();
|
long localTimestamp = clock.currentTimeMillis();
|
||||||
@@ -312,8 +313,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(DuplexTransportConnection conn,
|
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||||
boolean exception) {
|
|
||||||
try {
|
try {
|
||||||
LOG.info("Closing connection");
|
LOG.info("Closing connection");
|
||||||
conn.getReader().dispose(exception, true);
|
conn.getReader().dispose(exception, true);
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
|||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.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.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
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.ECPublicKeyParameters;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
@@ -44,14 +39,8 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
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.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_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 {
|
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_SALT_BYTES = 32; // 256 bits
|
||||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||||
private static final int PBKDF_SAMPLES = 30;
|
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 SecureRandom secureRandom;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
@@ -263,179 +219,26 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveHeaderKey(SecretKey master,
|
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
|
||||||
boolean alice) {
|
byte[] mac = mac(label, k, inputs);
|
||||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
|
return new SecretKey(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
|
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,
|
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||||
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
|
byte[] raw = performRawKeyAgreement(ourPriv, theirPublicKey);
|
||||||
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
|
|
||||||
byte[] alicePub, bobPub;
|
byte[] alicePub, bobPub;
|
||||||
if (alice) {
|
if (alice) {
|
||||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||||
bobPub = theirPublicKey;
|
bobPub = theirPublicKey.getEncoded();
|
||||||
} else {
|
} else {
|
||||||
alicePub = theirPublicKey;
|
alicePub = theirPublicKey.getEncoded();
|
||||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||||
}
|
}
|
||||||
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
return new SecretKey(hash(label, raw, alicePub, bobPub));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
|
||||||
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
|
||||||
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;
|
|
||||||
} else {
|
|
||||||
alicePayload = theirPayload;
|
|
||||||
alicePub = theirPublicKey;
|
|
||||||
bobPayload = ourPayload;
|
|
||||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
|
||||||
}
|
|
||||||
if (aliceRecord)
|
|
||||||
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
|
|
||||||
else
|
|
||||||
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
|
||||||
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
|
||||||
theirPublicKey, ourKeyPair, alice));
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -513,14 +316,13 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHashLength() {
|
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||||
return HASH_SIZE;
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] mac(SecretKey macKey, byte[]... inputs) {
|
|
||||||
Digest mac = new Blake2sDigest(macKey.getBytes());
|
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
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) {
|
for (byte[] input : inputs) {
|
||||||
ByteUtils.writeUint32(input.length, length, 0);
|
ByteUtils.writeUint32(input.length, length, 0);
|
||||||
mac.update(length, 0, length.length);
|
mac.update(length, 0, length.length);
|
||||||
@@ -612,30 +414,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return AsciiArmour.wrap(b, lineLength);
|
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
|
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||||
byte[] utf8 = StringUtils.toUtf8(password);
|
byte[] utf8 = StringUtils.toUtf8(password);
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
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.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
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.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
@@ -74,6 +76,12 @@ public class CryptoModule {
|
|||||||
return new PasswordStrengthEstimatorImpl();
|
return new PasswordStrengthEstimatorImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
TransportCrypto provideTransportCrypto(
|
||||||
|
TransportCryptoImpl transportCrypto) {
|
||||||
|
return transportCrypto;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamDecrypterFactory provideStreamDecrypterFactory(
|
StreamDecrypterFactory provideStreamDecrypterFactory(
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
@@ -81,9 +89,17 @@ public class CryptoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
|
StreamEncrypterFactory provideStreamEncrypterFactory(
|
||||||
|
CryptoComponent crypto, TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
|
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
|
||||||
|
cipherProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
KeyAgreementCrypto provideKeyAgreementCrypto(
|
||||||
|
KeyAgreementCryptoImpl keyAgreementCrypto) {
|
||||||
|
return keyAgreementCrypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
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.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
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 {
|
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final TransportCrypto transportCrypto;
|
||||||
private final Provider<AuthenticatedCipher> cipherProvider;
|
private final Provider<AuthenticatedCipher> cipherProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
||||||
|
TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.transportCrypto = transportCrypto;
|
||||||
this.cipherProvider = cipherProvider;
|
this.cipherProvider = cipherProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +41,8 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
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];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
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.KeyPair;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
@@ -46,7 +46,7 @@ class KeyAgreementConnector {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CryptoComponent crypto;
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final CompletionService<KeyAgreementConnection> connect;
|
private final CompletionService<KeyAgreementConnection> connect;
|
||||||
|
|
||||||
@@ -58,11 +58,11 @@ class KeyAgreementConnector {
|
|||||||
private volatile boolean alice = false;
|
private volatile boolean alice = false;
|
||||||
|
|
||||||
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
||||||
CryptoComponent crypto, PluginManager pluginManager,
|
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
|
||||||
Executor ioExecutor) {
|
Executor ioExecutor) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.crypto = crypto;
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
connect = new ExecutorCompletionService<>(ioExecutor);
|
connect = new ExecutorCompletionService<>(ioExecutor);
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,8 @@ class KeyAgreementConnector {
|
|||||||
public Payload listen(KeyPair localKeyPair) {
|
public Payload listen(KeyPair localKeyPair) {
|
||||||
LOG.info("Starting BQP listeners");
|
LOG.info("Starting BQP listeners");
|
||||||
// Derive commitment
|
// Derive commitment
|
||||||
byte[] commitment = crypto.deriveKeyCommitment(
|
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
|
||||||
localKeyPair.getPublic().getEncoded());
|
localKeyPair.getPublic());
|
||||||
// Start all listeners and collect their descriptors
|
// Start all listeners and collect their descriptors
|
||||||
List<TransportDescriptor> descriptors = new ArrayList<>();
|
List<TransportDescriptor> descriptors = new ArrayList<>();
|
||||||
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
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.BdfReaderFactory;
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||||
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.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
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.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -22,13 +13,9 @@ import dagger.Provides;
|
|||||||
public class KeyAgreementModule {
|
public class KeyAgreementModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
KeyAgreementTask provideKeyAgreementTask(
|
||||||
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
|
KeyAgreementTaskImpl keyAgreementTask) {
|
||||||
CryptoComponent crypto, EventBus eventBus,
|
return keyAgreementTask;
|
||||||
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
|
|
||||||
PluginManager pluginManager) {
|
|
||||||
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
|
|
||||||
ioExecutor, payloadEncoder, pluginManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.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.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
@@ -11,6 +14,9 @@ import java.io.IOException;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
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.
|
* Implementation of the BQP protocol.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -57,6 +63,7 @@ class KeyAgreementProtocol {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyAgreementTransport transport;
|
private final KeyAgreementTransport transport;
|
||||||
private final Payload theirPayload, ourPayload;
|
private final Payload theirPayload, ourPayload;
|
||||||
@@ -64,11 +71,13 @@ class KeyAgreementProtocol {
|
|||||||
private final boolean alice;
|
private final boolean alice;
|
||||||
|
|
||||||
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
||||||
|
KeyAgreementCrypto keyAgreementCrypto,
|
||||||
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
||||||
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.theirPayload = theirPayload;
|
this.theirPayload = theirPayload;
|
||||||
@@ -86,7 +95,7 @@ class KeyAgreementProtocol {
|
|||||||
*/
|
*/
|
||||||
SecretKey perform() throws AbortException, IOException {
|
SecretKey perform() throws AbortException, IOException {
|
||||||
try {
|
try {
|
||||||
byte[] theirPublicKey;
|
PublicKey theirPublicKey;
|
||||||
if (alice) {
|
if (alice) {
|
||||||
sendKey();
|
sendKey();
|
||||||
// Alice waits here until Bob obtains her payload.
|
// Alice waits here until Bob obtains her payload.
|
||||||
@@ -104,7 +113,7 @@ class KeyAgreementProtocol {
|
|||||||
receiveConfirm(s, theirPublicKey);
|
receiveConfirm(s, theirPublicKey);
|
||||||
sendConfirm(s, theirPublicKey);
|
sendConfirm(s, theirPublicKey);
|
||||||
}
|
}
|
||||||
return crypto.deriveMasterSecret(s);
|
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
|
||||||
} catch (AbortException e) {
|
} catch (AbortException e) {
|
||||||
sendAbort(e.getCause() != null);
|
sendAbort(e.getCause() != null);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -115,27 +124,34 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] receiveKey() throws AbortException {
|
private PublicKey receiveKey() throws AbortException {
|
||||||
byte[] publicKey = transport.receiveKey();
|
byte[] publicKeyBytes = transport.receiveKey();
|
||||||
callbacks.initialRecordReceived();
|
callbacks.initialRecordReceived();
|
||||||
byte[] expected = crypto.deriveKeyCommitment(publicKey);
|
KeyParser keyParser = crypto.getAgreementKeyParser();
|
||||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
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();
|
throw new AbortException();
|
||||||
return publicKey;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
|
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
try {
|
try {
|
||||||
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
|
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
|
theirPublicKey, ourKeyPair, alice);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new AbortException(e);
|
throw new AbortException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
|
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
byte[] confirm = crypto.deriveConfirmationRecord(s,
|
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
@@ -143,10 +159,10 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendConfirm(confirm);
|
transport.sendConfirm(confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
|
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
byte[] confirm = transport.receiveConfirm();
|
byte[] confirm = transport.receiveConfirm();
|
||||||
byte[] expected = crypto.deriveConfirmationRecord(s,
|
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
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.KeyAgreementListeningEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
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.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
@@ -23,6 +25,8 @@ import java.io.IOException;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -35,6 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyPair localKeyPair;
|
private final KeyPair localKeyPair;
|
||||||
@@ -43,14 +48,17 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
private Payload localPayload;
|
private Payload localPayload;
|
||||||
private Payload remotePayload;
|
private Payload remotePayload;
|
||||||
|
|
||||||
|
@Inject
|
||||||
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
|
||||||
PluginManager pluginManager, Executor ioExecutor) {
|
PayloadEncoder payloadEncoder, PluginManager pluginManager,
|
||||||
|
@IoExecutor Executor ioExecutor) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
localKeyPair = crypto.generateAgreementKeyPair();
|
localKeyPair = crypto.generateAgreementKeyPair();
|
||||||
connector = new KeyAgreementConnector(this, clock, crypto,
|
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
|
||||||
pluginManager, ioExecutor);
|
pluginManager, ioExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +108,8 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
// Run BQP protocol over the connection
|
// Run BQP protocol over the connection
|
||||||
LOG.info("Starting BQP protocol");
|
LOG.info("Starting BQP protocol");
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
||||||
payloadEncoder, transport, remotePayload, localPayload,
|
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
|
||||||
localKeyPair, alice);
|
localPayload, localKeyPair, alice);
|
||||||
try {
|
try {
|
||||||
SecretKey master = protocol.perform();
|
SecretKey master = protocol.perform();
|
||||||
KeyAgreementResult result =
|
KeyAgreementResult result =
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.transport;
|
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.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -20,17 +20,18 @@ class TransportKeyManagerFactoryImpl implements
|
|||||||
TransportKeyManagerFactory {
|
TransportKeyManagerFactory {
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final CryptoComponent crypto;
|
private final TransportCrypto transportCrypto;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
|
TransportKeyManagerFactoryImpl(DatabaseComponent db,
|
||||||
|
TransportCrypto transportCrypto,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.crypto = crypto;
|
this.transportCrypto = transportCrypto;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
@@ -39,8 +40,8 @@ class TransportKeyManagerFactoryImpl implements
|
|||||||
@Override
|
@Override
|
||||||
public TransportKeyManager createTransportKeyManager(
|
public TransportKeyManager createTransportKeyManager(
|
||||||
TransportId transportId, long maxLatency) {
|
TransportId transportId, long maxLatency) {
|
||||||
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
|
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
|
||||||
clock, transportId, maxLatency);
|
scheduler, clock, transportId, maxLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final CryptoComponent crypto;
|
private final TransportCrypto transportCrypto;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
@@ -54,11 +54,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
||||||
private final Map<ContactId, MutableTransportKeys> keys;
|
private final Map<ContactId, MutableTransportKeys> keys;
|
||||||
|
|
||||||
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
TransportKeyManagerImpl(DatabaseComponent db,
|
||||||
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
|
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||||
Clock clock, TransportId transportId, long maxLatency) {
|
@Scheduler ScheduledExecutorService scheduler, Clock clock,
|
||||||
|
TransportId transportId, long maxLatency) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.crypto = crypto;
|
this.transportCrypto = transportCrypto;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
@@ -99,7 +100,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
TransportKeys k = e.getValue();
|
TransportKeys k = e.getValue();
|
||||||
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
|
TransportKeys k1 =
|
||||||
|
transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||||
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
||||||
rotationResult.rotated.put(c, k1);
|
rotationResult.rotated.put(c, k1);
|
||||||
rotationResult.current.put(c, k1);
|
rotationResult.current.put(c, k1);
|
||||||
@@ -127,7 +129,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
streamNumber);
|
streamNumber);
|
||||||
inContexts.put(new Bytes(tag), tagCtx);
|
inContexts.put(new Bytes(tag), tagCtx);
|
||||||
}
|
}
|
||||||
@@ -162,11 +164,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Work out what rotation period the timestamp belongs to
|
// Work out what rotation period the timestamp belongs to
|
||||||
long rotationPeriod = timestamp / rotationPeriodLength;
|
long rotationPeriod = timestamp / rotationPeriodLength;
|
||||||
// Derive the transport keys
|
// Derive the transport keys
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
rotationPeriod, alice);
|
master, rotationPeriod, alice);
|
||||||
// Rotate the keys to the current rotation period if necessary
|
// Rotate the keys to the current rotation period if necessary
|
||||||
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
||||||
k = crypto.rotateTransportKeys(k, rotationPeriod);
|
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||||
// Initialise mutable state for the contact
|
// Initialise mutable state for the contact
|
||||||
addKeys(c, new MutableTransportKeys(k));
|
addKeys(c, new MutableTransportKeys(k));
|
||||||
// Write the keys back to the DB
|
// 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
|
// Add tags for any stream numbers added to the window
|
||||||
for (long streamNumber : change.getAdded()) {
|
for (long streamNumber : change.getAdded()) {
|
||||||
byte[] addTag = new byte[TAG_LENGTH];
|
byte[] addTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||||
streamNumber);
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.put(new Bytes(addTag), new TagContext(
|
inContexts.put(new Bytes(addTag), new TagContext(
|
||||||
tagCtx.contactId, inKeys, streamNumber));
|
tagCtx.contactId, inKeys, streamNumber));
|
||||||
}
|
}
|
||||||
@@ -243,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : change.getRemoved()) {
|
for (long streamNumber : change.getRemoved()) {
|
||||||
if (streamNumber == tagCtx.streamNumber) continue;
|
if (streamNumber == tagCtx.streamNumber) continue;
|
||||||
byte[] removeTag = new byte[TAG_LENGTH];
|
byte[] removeTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||||
PROTOCOL_VERSION, streamNumber);
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.remove(new Bytes(removeTag));
|
inContexts.remove(new Bytes(removeTag));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,40 +3,25 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
public class KeyAgreementTest extends BrambleTestCase {
|
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
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
SecureRandomProvider
|
CryptoComponent crypto =
|
||||||
secureRandomProvider = new TestSecureRandomProvider();
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] bPub = bPair.getPublic().getEncoded();
|
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
|
bPair.getPublic(), aPair, true);
|
||||||
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
|
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
|
aPair.getPublic(), bPair, false);
|
||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -16,35 +16,34 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BrambleTestCase {
|
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 TransportId transportId = new TransportId("id");
|
||||||
private final CryptoComponent crypto;
|
private final SecretKey master = getSecretKey();
|
||||||
private final SecretKey master;
|
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
master = TestUtils.getSecretKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreDistinct() {
|
public void testKeysAreDistinct() {
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
assertAllDifferent(k);
|
assertAllDifferent(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -56,8 +55,8 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
||||||
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
||||||
// Rotate into the future
|
// Rotate into the future
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 456);
|
kB = transportCrypto.rotateTransportKeys(kB, 456);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -73,22 +72,23 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Compare Alice's previous keys in period 456 with Bob's current keys
|
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||||
// in period 455
|
// in period 455
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||||
// Alice's previous incoming keys should equal Bob's outgoing keys
|
// Alice's previous incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
assertArrayEquals(
|
||||||
|
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
||||||
// Compare Alice's current keys in period 456 with Bob's previous keys
|
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = crypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||||
// Alice's outgoing keys should equal Bob's previous incoming keys
|
// Alice's outgoing keys should equal Bob's previous incoming keys
|
||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||||
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
||||||
@@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testNextKeysMatchNextKeysOfContact() {
|
public void testNextKeysMatchNextKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Compare Alice's current keys in period 456 with Bob's next keys in
|
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||||
// period 455
|
// period 455
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||||
// Alice's outgoing keys should equal Bob's next incoming keys
|
// Alice's outgoing keys should equal Bob's next incoming keys
|
||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||||
kB.getNextIncomingKeys().getTagKey().getBytes());
|
kB.getNextIncomingKeys().getTagKey().getBytes());
|
||||||
@@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
||||||
// Compare Alice's next keys in period 456 with Bob's current keys
|
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = crypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||||
// Alice's next incoming keys should equal Bob's outgoing keys
|
// Alice's next incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMasterKeyAffectsOutput() {
|
public void testMasterKeyAffectsOutput() {
|
||||||
SecretKey master1 = TestUtils.getSecretKey();
|
SecretKey master1 = getSecretKey();
|
||||||
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1,
|
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master1, 123, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
public void testTransportIdAffectsOutput() {
|
public void testTransportIdAffectsOutput() {
|
||||||
TransportId transportId1 = new TransportId("id1");
|
TransportId transportId1 = new TransportId("id1");
|
||||||
assertFalse(transportId.getString().equals(transportId1.getString()));
|
assertFalse(transportId.getString().equals(transportId1.getString()));
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master,
|
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
|
||||||
123, true);
|
master, 123, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,42 +4,49 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
public class MacTest extends BrambleTestCase {
|
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 SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final String label1 = getRandomString(123);
|
||||||
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
|
private final String label2 = getRandomString(123);
|
||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] input1 = getRandomBytes(123);
|
||||||
|
private final byte[] input2 = getRandomBytes(234);
|
||||||
public MacTest() {
|
private final byte[] input3 = new byte[0];
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
||||||
// Calculate the MAC twice - the results should be identical
|
// Calculate the MAC twice - the results should be identical
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
assertArrayEquals(mac, mac1);
|
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
|
@Test
|
||||||
public void testDifferentKeysProduceDifferentMacs() {
|
public void testDifferentKeysProduceDifferentMacs() {
|
||||||
// Generate second random key
|
|
||||||
SecretKey k1 = TestUtils.getSecretKey();
|
|
||||||
// Calculate the MAC with each key - the results should be different
|
// Calculate the MAC with each key - the results should be different
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3);
|
||||||
assertFalse(Arrays.equals(mac, mac1));
|
assertFalse(Arrays.equals(mac, mac1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +54,8 @@ public class MacTest extends BrambleTestCase {
|
|||||||
public void testDifferentInputsProduceDifferentMacs() {
|
public void testDifferentInputsProduceDifferentMacs() {
|
||||||
// Calculate the MAC with the inputs in different orders - the results
|
// Calculate the MAC with the inputs in different orders - the results
|
||||||
// should be different
|
// should be different
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
|
byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1);
|
||||||
assertFalse(Arrays.equals(mac, mac1));
|
assertFalse(Arrays.equals(mac, mac1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -14,25 +13,25 @@ import java.util.Set;
|
|||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
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.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 CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||||
private final SecretKey tagKey;
|
|
||||||
|
private final TransportCrypto transportCrypto =
|
||||||
|
new TransportCryptoImpl(crypto);
|
||||||
|
private final SecretKey tagKey = getSecretKey();
|
||||||
private final long streamNumber = 1234567890;
|
private final long streamNumber = 1234567890;
|
||||||
|
|
||||||
public TagEncodingTest() {
|
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
tagKey = TestUtils.getSecretKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeyAffectsTag() throws Exception {
|
public void testKeyAffectsTag() throws Exception {
|
||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
SecretKey tagKey = TestUtils.getSecretKey();
|
SecretKey tagKey = getSecretKey();
|
||||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
|
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
assertTrue(set.add(new Bytes(tag)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +41,8 @@ public class TagEncodingTest extends BrambleTestCase {
|
|||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
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)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,8 @@ public class TagEncodingTest extends BrambleTestCase {
|
|||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
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)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.auto.Mock;
|
import org.jmock.auto.Mock;
|
||||||
import org.jmock.integration.junit4.JUnitRuleMockery;
|
import org.jmock.integration.junit4.JUnitRuleMockery;
|
||||||
@@ -16,6 +17,10 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
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.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
@@ -28,34 +33,33 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
setImposteriser(ClassImposteriser.INSTANCE);
|
setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
|
private final PublicKey alicePubKey =
|
||||||
private static final byte[] ALICE_COMMIT =
|
context.mock(PublicKey.class, "alice");
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
private final byte[] alicePubKeyBytes = getRandomBytes(32);
|
||||||
private static final byte[] ALICE_PAYLOAD =
|
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
|
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
||||||
|
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
|
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
|
||||||
private static final byte[] BOB_COMMIT =
|
private final byte[] bobPubKeyBytes = getRandomBytes(32);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
private static final byte[] BOB_PAYLOAD =
|
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
|
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private static final byte[] ALICE_CONFIRM =
|
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
|
||||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
private final byte[] badPubKeyBytes = getRandomBytes(32);
|
||||||
private static final byte[] BOB_CONFIRM =
|
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
private final byte[] badConfirm = 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);
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
KeyAgreementProtocol.Callbacks callbacks;
|
KeyAgreementProtocol.Callbacks callbacks;
|
||||||
@Mock
|
@Mock
|
||||||
CryptoComponent crypto;
|
CryptoComponent crypto;
|
||||||
@Mock
|
@Mock
|
||||||
|
KeyAgreementCrypto keyAgreementCrypto;
|
||||||
|
@Mock
|
||||||
|
KeyParser keyParser;
|
||||||
|
@Mock
|
||||||
PayloadEncoder payloadEncoder;
|
PayloadEncoder payloadEncoder;
|
||||||
@Mock
|
@Mock
|
||||||
KeyAgreementTransport transport;
|
KeyAgreementTransport transport;
|
||||||
@@ -65,60 +69,67 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testAliceProtocol() throws Exception {
|
public void testAliceProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
SecretKey masterSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||||
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||||
will(returnValue(BOB_COMMIT));
|
will(returnValue(bobCommit));
|
||||||
|
|
||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
|
ourKeyPair, true);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
true, true);
|
||||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
will(returnValue(aliceConfirm));
|
||||||
|
oneOf(transport).sendConfirm(aliceConfirm);
|
||||||
|
|
||||||
// Alice receives Bob's confirmation record
|
// Alice receives Bob's confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BOB_CONFIRM));
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice verifies Bob's confirmation record
|
// Alice verifies Bob's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
true, false);
|
||||||
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice computes master secret
|
// Alice computes master secret
|
||||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterSecret));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -129,59 +140,66 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testBobProtocol() throws Exception {
|
public void testBobProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
SecretKey masterSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||||
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||||
will(returnValue(ALICE_COMMIT));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
|
ourKeyPair, false);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives Alices's confirmation record
|
// Bob receives Alices's confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(ALICE_CONFIRM));
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob verifies Alice's confirmation record
|
// Bob verifies Alice's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
false, true);
|
||||||
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob sends his confirmation record
|
// Bob sends his confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
false, false);
|
||||||
oneOf(transport).sendConfirm(BOB_CONFIRM);
|
will(returnValue(bobConfirm));
|
||||||
|
oneOf(transport).sendConfirm(bobConfirm);
|
||||||
|
|
||||||
// Bob computes master secret
|
// Bob computes master secret
|
||||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterSecret));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -192,38 +210,43 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives a bad public key
|
// Alice receives a bad public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BAD_PUBKEY));
|
will(returnValue(badPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||||
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||||
will(returnValue(BAD_COMMIT));
|
will(returnValue(badCommit));
|
||||||
|
|
||||||
// Alice aborts
|
// Alice aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Alice never computes shared secret
|
// Alice never computes shared secret
|
||||||
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
|
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
||||||
|
ourKeyPair, true);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -233,34 +256,38 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadKey() throws Exception {
|
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Bob receives a bad public key
|
// Bob receives a bad public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BAD_PUBKEY));
|
will(returnValue(badPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||||
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||||
will(returnValue(BAD_COMMIT));
|
will(returnValue(badCommit));
|
||||||
|
|
||||||
// Bob aborts
|
// Bob aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Bob never sends his public key
|
// Bob never sends his public key
|
||||||
never(transport).sendKey(BOB_PUBKEY);
|
never(transport).sendKey(bobPubKeyBytes);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -270,62 +297,69 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||||
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||||
will(returnValue(BOB_COMMIT));
|
will(returnValue(bobCommit));
|
||||||
|
|
||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
|
ourKeyPair, true);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
true, true);
|
||||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
will(returnValue(aliceConfirm));
|
||||||
|
oneOf(transport).sendConfirm(aliceConfirm);
|
||||||
|
|
||||||
// Alice receives a bad confirmation record
|
// Alice receives a bad confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BAD_CONFIRM));
|
will(returnValue(badConfirm));
|
||||||
|
|
||||||
// Alice verifies Bob's confirmation record
|
// Alice verifies Bob's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
true, false);
|
||||||
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice aborts
|
// Alice aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Alice never computes master secret
|
// Alice never computes master secret
|
||||||
never(crypto).deriveMasterSecret(sharedSecret);
|
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -335,56 +369,63 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||||
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||||
will(returnValue(ALICE_COMMIT));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
|
ourKeyPair, false);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives a bad confirmation record
|
// Bob receives a bad confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BAD_CONFIRM));
|
will(returnValue(badConfirm));
|
||||||
|
|
||||||
// Bob verifies Alice's confirmation record
|
// Bob verifies Alice's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
false, true);
|
||||||
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob aborts
|
// Bob aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Bob never sends his confirmation record
|
// Bob never sends his confirmation record
|
||||||
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
never(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
|
false, false);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
@@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
@Inject
|
@Inject
|
||||||
RecordWriterFactory recordWriterFactory;
|
RecordWriterFactory recordWriterFactory;
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponent crypto;
|
TransportCrypto transportCrypto;
|
||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
@@ -117,7 +117,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
private void read(byte[] connectionData) throws Exception {
|
private void read(byte[] connectionData) throws Exception {
|
||||||
// Calculate the expected tag
|
// Calculate the expected tag
|
||||||
byte[] expectedTag = new byte[TAG_LENGTH];
|
byte[] expectedTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
|
transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
|
|
||||||
// Read the tag
|
// Read the tag
|
||||||
InputStream in = new ByteArrayInputStream(connectionData);
|
InputStream in = new ByteArrayInputStream(connectionData);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
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.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
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.RunAction;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.jmock.api.Action;
|
import org.jmock.api.Action;
|
||||||
import org.jmock.api.Invocation;
|
import org.jmock.api.Invocation;
|
||||||
import org.junit.Test;
|
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.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
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 TransportId transportId = new TransportId("id");
|
||||||
private final long maxLatency = 30 * 1000; // 30 seconds
|
private final long maxLatency = 30 * 1000; // 30 seconds
|
||||||
@@ -55,14 +62,6 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
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<>();
|
Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
|
||||||
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
||||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
||||||
@@ -79,14 +78,15 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||||
will(returnValue(shouldNotRotate));
|
will(returnValue(shouldNotRotate));
|
||||||
// Encode the tags (3 sets per contact)
|
// Encode the tags (3 sets per contact)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(6).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
@@ -97,161 +97,124 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
transportKeyManager.start(txn);
|
transportKeyManager.start(txn);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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;
|
|
||||||
TransportKeys transportKeys = createTransportKeys(999, 0);
|
TransportKeys transportKeys = createTransportKeys(999, 0);
|
||||||
TransportKeys rotated = createTransportKeys(1000, 0);
|
TransportKeys rotated = createTransportKeys(1000, 0);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
999, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (1 ms after start of rotation period 1000)
|
// Get the current time (1 ms after start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000 + 1));
|
will(returnValue(rotationPeriodLength * 1000 + 1));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is 1 ms before the start of rotation period 1000
|
// The timestamp is 1 ms before the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||||
throws Exception {
|
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);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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;
|
|
||||||
// The stream counter has been exhausted
|
// The stream counter has been exhausted
|
||||||
TransportKeys transportKeys = createTransportKeys(1000,
|
TransportKeys transportKeys = createTransportKeys(1000,
|
||||||
MAX_32_BIT_UNSIGNED + 1);
|
MAX_32_BIT_UNSIGNED + 1);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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;
|
|
||||||
// The stream counter can be used one more time before being exhausted
|
// The stream counter can be used one more time before being exhausted
|
||||||
TransportKeys transportKeys = createTransportKeys(1000,
|
TransportKeys transportKeys = createTransportKeys(1000,
|
||||||
MAX_32_BIT_UNSIGNED);
|
MAX_32_BIT_UNSIGNED);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
@@ -259,9 +222,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
@@ -277,94 +240,76 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
||||||
// The second request should return null, the counter is exhausted
|
// The second request should return null, the counter is exhausted
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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;
|
|
||||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn,
|
assertNull(transportKeyManager.getStreamContext(txn,
|
||||||
new byte[TAG_LENGTH]));
|
new byte[TAG_LENGTH]));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTagIsNotRecognisedTwice() throws Exception {
|
public void testTagIsNotRecognisedTwice() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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;
|
|
||||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
// Keep a copy of the tags
|
// Keep a copy of the tags
|
||||||
List<byte[]> tags = new ArrayList<>();
|
List<byte[]> tags = new ArrayList<>();
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
// Encode a new tag after sliding the window
|
// 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(tagKey), with(PROTOCOL_VERSION),
|
||||||
with((long) REORDERING_WINDOW_SIZE));
|
with((long) REORDERING_WINDOW_SIZE));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
@@ -373,9 +318,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
@@ -395,20 +340,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
||||||
// The second request should return null, the tag has already been used
|
// The second request should return null, the tag has already been used
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
|
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);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
Map<ContactId, TransportKeys> loaded =
|
Map<ContactId, TransportKeys> loaded =
|
||||||
Collections.singletonMap(contactId, transportKeys);
|
Collections.singletonMap(contactId, transportKeys);
|
||||||
@@ -424,12 +359,13 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Schedule key rotation at the start of the next rotation period
|
// Schedule key rotation at the start of the next rotation period
|
||||||
@@ -445,13 +381,14 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1001));
|
will(returnValue(rotationPeriodLength * 1001));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)),
|
oneOf(transportCrypto).rotateTransportKeys(
|
||||||
with(1001L));
|
with(any(TransportKeys.class)), with(1001L));
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
@@ -465,12 +402,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn1);
|
oneOf(db).endTransaction(txn1);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
transportKeyManager.start(txn);
|
transportKeyManager.start(txn);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
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.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
@@ -125,7 +125,7 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
ContactExchangeTask contactExchangeTask();
|
ContactExchangeTask contactExchangeTask();
|
||||||
|
|
||||||
KeyAgreementTaskFactory keyAgreementTaskFactory();
|
KeyAgreementTask keyAgreementTask();
|
||||||
|
|
||||||
PayloadEncoder payloadEncoder();
|
PayloadEncoder payloadEncoder();
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.google.zxing.Result;
|
|||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
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.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
@@ -48,6 +47,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||||
@@ -68,7 +68,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
private static final Logger LOG = Logger.getLogger(TAG);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KeyAgreementTaskFactory keyAgreementTaskFactory;
|
Provider<KeyAgreementTask> keyAgreementTaskProvider;
|
||||||
@Inject
|
@Inject
|
||||||
PayloadEncoder payloadEncoder;
|
PayloadEncoder payloadEncoder;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -187,7 +187,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void startListening() {
|
private void startListening() {
|
||||||
KeyAgreementTask oldTask = task;
|
KeyAgreementTask oldTask = task;
|
||||||
KeyAgreementTask newTask = keyAgreementTaskFactory.createTask();
|
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
|
||||||
task = newTask;
|
task = newTask;
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
if (oldTask != null) oldTask.stopListening();
|
if (oldTask != null) oldTask.stopListening();
|
||||||
|
|||||||
@@ -86,4 +86,44 @@ public interface IntroductionConstants {
|
|||||||
int TASK_ACTIVATE_CONTACT = 1;
|
int TASK_ACTIVATE_CONTACT = 1;
|
||||||
int TASK_ABORT = 2;
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT;
|
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.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.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;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.EXISTS;
|
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.LOCAL_AUTHOR_ID;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
|
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_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_ID;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
|
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.REMOTE_AUTHOR_IS_US;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
|
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.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.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.STATE;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TASK;
|
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_ABORT;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
|
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.IntroductionConstants.TYPE_RESPONSE;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -98,9 +104,6 @@ class IntroduceeManager {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(IntroduceeManager.class.getName());
|
Logger.getLogger(IntroduceeManager.class.getName());
|
||||||
|
|
||||||
static final String SIGNING_LABEL_RESPONSE =
|
|
||||||
CLIENT_ID.getString() + "/RESPONSE";
|
|
||||||
|
|
||||||
private final MessageSender messageSender;
|
private final MessageSender messageSender;
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final ClientHelper clientHelper;
|
private final ClientHelper clientHelper;
|
||||||
@@ -288,8 +291,7 @@ class IntroduceeManager {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private BdfDictionary performTasks(Transaction txn,
|
private BdfDictionary performTasks(Transaction txn,
|
||||||
BdfDictionary localState)
|
BdfDictionary localState) throws FormatException, DbException {
|
||||||
throws FormatException, DbException {
|
|
||||||
|
|
||||||
if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE)
|
if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE)
|
||||||
return null;
|
return null;
|
||||||
@@ -306,22 +308,21 @@ class IntroduceeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// figure out who takes which role by comparing public keys
|
// figure out who takes which role by comparing public keys
|
||||||
byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
byte[] ourPublicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||||
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
byte[] theirPublicKeyBytes = localState.getRaw(E_PUBLIC_KEY);
|
||||||
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
int comp = Bytes.COMPARATOR.compare(new Bytes(ourPublicKeyBytes),
|
||||||
new Bytes(theirEphemeralKey));
|
new Bytes(theirPublicKeyBytes));
|
||||||
boolean alice = comp < 0;
|
boolean alice = comp < 0;
|
||||||
|
|
||||||
// get our local author
|
// get our local author
|
||||||
LocalAuthor author = identityManager.getLocalAuthor(txn);
|
LocalAuthor author = identityManager.getLocalAuthor(txn);
|
||||||
|
|
||||||
SecretKey secretKey;
|
SecretKey secretKey;
|
||||||
byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
byte[] ourPrivateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||||
try {
|
try {
|
||||||
// derive secret master key
|
// derive secret master key
|
||||||
secretKey =
|
secretKey = deriveSecretKey(ourPublicKeyBytes,
|
||||||
deriveSecretKey(publicKeyBytes, privateKeyBytes, alice,
|
ourPrivateKeyBytes, alice, theirPublicKeyBytes);
|
||||||
theirEphemeralKey);
|
|
||||||
// derive MAC keys and nonces, sign our nonce and calculate MAC
|
// derive MAC keys and nonces, sign our nonce and calculate MAC
|
||||||
deriveMacKeysAndNonces(localState, author, secretKey, alice);
|
deriveMacKeysAndNonces(localState, author, secretKey, alice);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
@@ -410,34 +411,36 @@ class IntroduceeManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveSecretKey(byte[] publicKeyBytes,
|
private SecretKey deriveSecretKey(byte[] ourPublicKeyBytes,
|
||||||
byte[] privateKeyBytes, boolean alice, byte[] theirPublicKey)
|
byte[] ourPrivateKeyBytes, boolean alice,
|
||||||
throws GeneralSecurityException {
|
byte[] theirPublicKeyBytes) throws GeneralSecurityException {
|
||||||
// parse the local ephemeral key pair
|
// parse the local ephemeral key pair
|
||||||
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
||||||
PublicKey publicKey;
|
PublicKey ourPublicKey;
|
||||||
PrivateKey privateKey;
|
PrivateKey ourPrivateKey;
|
||||||
try {
|
try {
|
||||||
publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
ourPublicKey = keyParser.parsePublicKey(ourPublicKeyBytes);
|
||||||
privateKey = keyParser.parsePrivateKey(privateKeyBytes);
|
ourPrivateKey = keyParser.parsePrivateKey(ourPrivateKeyBytes);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
if (LOG.isLoggable(WARNING)) {
|
if (LOG.isLoggable(WARNING)) {
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Our own ephemeral key is invalid");
|
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
|
// and the remote ephemeral public key
|
||||||
return cryptoComponent
|
return cryptoComponent.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
.deriveMasterSecret(theirPublicKey, keyPair, alice);
|
theirPublicKey, ourKeyPair, alice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives nonces, signs our nonce and calculates MAC
|
* Derives nonces, signs our nonce and calculates MAC
|
||||||
* <p>
|
* <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.
|
* The other introducee's nonce and MAC key are added to the localState.
|
||||||
* <p>
|
* <p>
|
||||||
* Our nonce is signed with the local author's long-term private key.
|
* Our nonce is signed with the local author's long-term private key.
|
||||||
@@ -448,21 +451,24 @@ class IntroduceeManager {
|
|||||||
private void deriveMacKeysAndNonces(BdfDictionary localState,
|
private void deriveMacKeysAndNonces(BdfDictionary localState,
|
||||||
LocalAuthor author, SecretKey secretKey, boolean alice)
|
LocalAuthor author, SecretKey secretKey, boolean alice)
|
||||||
throws FormatException, GeneralSecurityException {
|
throws FormatException, GeneralSecurityException {
|
||||||
// Derive two nonces and a MAC key from the secret master key
|
// Derive two nonces and two MAC keys from the shared secret key
|
||||||
byte[] ourNonce =
|
String ourNonceLabel = alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL;
|
||||||
cryptoComponent.deriveSignatureNonce(secretKey, alice);
|
String theirNonceLabel = alice ? BOB_NONCE_LABEL : ALICE_NONCE_LABEL;
|
||||||
byte[] theirNonce =
|
byte[] ourNonce = cryptoComponent.mac(ourNonceLabel, secretKey);
|
||||||
cryptoComponent.deriveSignatureNonce(secretKey, !alice);
|
byte[] theirNonce = cryptoComponent.mac(theirNonceLabel, secretKey);
|
||||||
SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice);
|
String ourKeyLabel = alice ? ALICE_MAC_KEY_LABEL : BOB_MAC_KEY_LABEL;
|
||||||
SecretKey theirMacKey = cryptoComponent.deriveMacKey(secretKey, !alice);
|
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
|
// Save the other nonce and MAC key for the verification
|
||||||
localState.put(NONCE, theirNonce);
|
localState.put(NONCE, theirNonce);
|
||||||
localState.put(MAC_KEY, theirMacKey.getBytes());
|
localState.put(MAC_KEY, theirMacKey.getBytes());
|
||||||
|
|
||||||
// Sign our nonce with our long-term identity public key
|
// Sign our nonce with our long-term identity public key
|
||||||
byte[] sig = cryptoComponent
|
byte[] sig = cryptoComponent.sign(SIGNING_LABEL, ourNonce,
|
||||||
.sign(SIGNING_LABEL_RESPONSE, ourNonce, author.getPrivateKey());
|
author.getPrivateKey());
|
||||||
|
|
||||||
// Calculate a MAC over identity public key, ephemeral public key,
|
// Calculate a MAC over identity public key, ephemeral public key,
|
||||||
// transport properties and timestamp.
|
// transport properties and timestamp.
|
||||||
@@ -472,7 +478,7 @@ class IntroduceeManager {
|
|||||||
BdfList toMacList = BdfList.of(author.getPublicKey(),
|
BdfList toMacList = BdfList.of(author.getPublicKey(),
|
||||||
publicKeyBytes, tp, ourTime);
|
publicKeyBytes, tp, ourTime);
|
||||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
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
|
// Add MAC and signature to localState, so it can be included in ACK
|
||||||
localState.put(OUR_MAC, mac);
|
localState.put(OUR_MAC, mac);
|
||||||
@@ -486,7 +492,7 @@ class IntroduceeManager {
|
|||||||
byte[] key = localState.getRaw(PUBLIC_KEY);
|
byte[] key = localState.getRaw(PUBLIC_KEY);
|
||||||
|
|
||||||
// Verify the signature
|
// 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");
|
LOG.warning("Invalid nonce signature in ACK");
|
||||||
throw new GeneralSecurityException();
|
throw new GeneralSecurityException();
|
||||||
}
|
}
|
||||||
@@ -506,7 +512,7 @@ class IntroduceeManager {
|
|||||||
long timestamp = localState.getLong(TIME);
|
long timestamp = localState.getLong(TIME);
|
||||||
BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
||||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||||
byte[] calculatedMac = cryptoComponent.mac(macKey, toMac);
|
byte[] calculatedMac = cryptoComponent.mac(MAC_LABEL, macKey, toMac);
|
||||||
if (!Arrays.equals(mac, calculatedMac)) {
|
if (!Arrays.equals(mac, calculatedMac)) {
|
||||||
LOG.warning("Received ACK with invalid MAC");
|
LOG.warning("Received ACK with invalid MAC");
|
||||||
throw new GeneralSecurityException();
|
throw new GeneralSecurityException();
|
||||||
|
|||||||
@@ -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.LOCAL_AUTHOR_ID;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
|
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_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.MAC_LENGTH;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID;
|
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.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.ROLE_INTRODUCEE;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
|
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.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.STATE;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME;
|
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_ACK;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
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.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.array;
|
||||||
import static org.hamcrest.Matchers.samePropertyValuesAs;
|
import static org.hamcrest.Matchers.samePropertyValuesAs;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -266,7 +267,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
);
|
);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce,
|
oneOf(cryptoComponent).verify(SIGNING_LABEL, nonce,
|
||||||
introducee2.getAuthor().getPublicKey(), sig);
|
introducee2.getAuthor().getPublicKey(), sig);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
}});
|
}});
|
||||||
@@ -296,7 +297,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
state.put(SIGNATURE, sig);
|
state.put(SIGNATURE, sig);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce,
|
oneOf(cryptoComponent).verify(SIGNING_LABEL, nonce,
|
||||||
publicKeyBytes, sig);
|
publicKeyBytes, sig);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
@@ -330,7 +331,8 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
||||||
will(returnValue(signBytes));
|
will(returnValue(signBytes));
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)),
|
oneOf(cryptoComponent).mac(with(MAC_LABEL),
|
||||||
|
with(samePropertyValuesAs(macKey)),
|
||||||
with(array(equal(signBytes))));
|
with(array(equal(signBytes))));
|
||||||
will(returnValue(mac));
|
will(returnValue(mac));
|
||||||
}});
|
}});
|
||||||
@@ -343,14 +345,15 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
||||||
will(returnValue(signBytes));
|
will(returnValue(signBytes));
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)),
|
oneOf(cryptoComponent).mac(with(MAC_LABEL),
|
||||||
|
with(samePropertyValuesAs(macKey)),
|
||||||
with(array(equal(signBytes))));
|
with(array(equal(signBytes))));
|
||||||
will(returnValue(TestUtils.getRandomBytes(MAC_LENGTH)));
|
will(returnValue(TestUtils.getRandomBytes(MAC_LENGTH)));
|
||||||
}});
|
}});
|
||||||
try {
|
try {
|
||||||
introduceeManager.verifyMac(state);
|
introduceeManager.verifyMac(state);
|
||||||
fail();
|
fail();
|
||||||
} catch(GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|||||||
@@ -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.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
|
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.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.E_PUBLIC_KEY;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
|
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;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY;
|
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.NAME;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.NONCE;
|
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.PUBLIC_KEY;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
|
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.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.TIME;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TRANSPORT;
|
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;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
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.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.briarproject.briar.test.BriarTestUtils.assertGroupCount;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -635,7 +639,7 @@ public class IntroductionIntegrationTest
|
|||||||
// adapt outgoing message queue to removed message
|
// adapt outgoing message queue to removed message
|
||||||
Group g2 = introductionGroupFactory
|
Group g2 = introductionGroupFactory
|
||||||
.createIntroductionGroup(contact2From0);
|
.createIntroductionGroup(contact2From0);
|
||||||
decreaseOutgoingMessageCounter(ch, g2.getId(), 1);
|
decreaseOutgoingMessageCounter(ch, g2.getId());
|
||||||
|
|
||||||
// allow visitor to modify response
|
// allow visitor to modify response
|
||||||
boolean earlyAbort = visitor.visit(response);
|
boolean earlyAbort = visitor.visit(response);
|
||||||
@@ -746,34 +750,32 @@ public class IntroductionIntegrationTest
|
|||||||
// create keys
|
// create keys
|
||||||
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
|
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
|
||||||
KeyPair eKeyPair1 = crypto.generateAgreementKeyPair();
|
KeyPair eKeyPair1 = crypto.generateAgreementKeyPair();
|
||||||
byte[] ePublicKeyBytes1 = eKeyPair1.getPublic().getEncoded();
|
|
||||||
KeyPair eKeyPair2 = crypto.generateAgreementKeyPair();
|
KeyPair eKeyPair2 = crypto.generateAgreementKeyPair();
|
||||||
byte[] ePublicKeyBytes2 = eKeyPair2.getPublic().getEncoded();
|
|
||||||
|
|
||||||
// Nonce 1
|
// Nonce 1
|
||||||
SecretKey secretKey =
|
SecretKey sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1, true);
|
eKeyPair2.getPublic(), eKeyPair1, true);
|
||||||
byte[] nonce1 = crypto.deriveSignatureNonce(secretKey, true);
|
byte[] nonce1 = crypto.mac(ALICE_NONCE_LABEL, sharedSecret);
|
||||||
|
|
||||||
// Signature 1
|
// Signature 1
|
||||||
byte[] sig1 = crypto.sign(SIGNING_LABEL_RESPONSE, nonce1,
|
byte[] sig1 = crypto.sign(SIGNING_LABEL, nonce1,
|
||||||
keyPair1.getPrivate().getEncoded());
|
keyPair1.getPrivate().getEncoded());
|
||||||
|
|
||||||
// MAC 1
|
// 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"));
|
BdfDictionary tp1 = BdfDictionary.of(new BdfEntry("fake", "fake"));
|
||||||
long time1 = clock.currentTimeMillis();
|
long time1 = clock.currentTimeMillis();
|
||||||
BdfList toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
BdfList toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
||||||
ePublicKeyBytes1, tp1, time1);
|
eKeyPair1.getPublic().getEncoded(), tp1, time1);
|
||||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
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
|
// create only relevant part of state for introducee2
|
||||||
BdfDictionary state = new BdfDictionary();
|
BdfDictionary state = new BdfDictionary();
|
||||||
state.put(PUBLIC_KEY, keyPair1.getPublic().getEncoded());
|
state.put(PUBLIC_KEY, keyPair1.getPublic().getEncoded());
|
||||||
state.put(TRANSPORT, tp1);
|
state.put(TRANSPORT, tp1);
|
||||||
state.put(TIME, time1);
|
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, mac1);
|
||||||
state.put(MAC_KEY, macKey1.getBytes());
|
state.put(MAC_KEY, macKey1.getBytes());
|
||||||
state.put(NONCE, nonce1);
|
state.put(NONCE, nonce1);
|
||||||
@@ -786,16 +788,16 @@ public class IntroductionIntegrationTest
|
|||||||
// replace ephemeral key pair and recalculate matching keys and nonce
|
// replace ephemeral key pair and recalculate matching keys and nonce
|
||||||
KeyPair eKeyPair1f = crypto.generateAgreementKeyPair();
|
KeyPair eKeyPair1f = crypto.generateAgreementKeyPair();
|
||||||
byte[] ePublicKeyBytes1f = eKeyPair1f.getPublic().getEncoded();
|
byte[] ePublicKeyBytes1f = eKeyPair1f.getPublic().getEncoded();
|
||||||
secretKey =
|
sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1f, true);
|
eKeyPair2.getPublic(), eKeyPair1f, true);
|
||||||
nonce1 = crypto.deriveSignatureNonce(secretKey, true);
|
nonce1 = crypto.mac(ALICE_NONCE_LABEL, sharedSecret);
|
||||||
|
|
||||||
// recalculate MAC
|
// recalculate MAC
|
||||||
macKey1 = crypto.deriveMacKey(secretKey, true);
|
macKey1 = crypto.deriveKey(ALICE_MAC_KEY_LABEL, sharedSecret);
|
||||||
toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
||||||
ePublicKeyBytes1f, tp1, time1);
|
ePublicKeyBytes1f, tp1, time1);
|
||||||
toMac = clientHelper.toByteArray(toMacList);
|
toMac = clientHelper.toByteArray(toMacList);
|
||||||
mac1 = crypto.mac(macKey1, toMac);
|
mac1 = crypto.mac(MAC_LABEL, macKey1, toMac);
|
||||||
|
|
||||||
// update state with faked information
|
// update state with faked information
|
||||||
state.put(E_PUBLIC_KEY, ePublicKeyBytes1f);
|
state.put(E_PUBLIC_KEY, ePublicKeyBytes1f);
|
||||||
@@ -970,12 +972,12 @@ public class IntroductionIntegrationTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decreaseOutgoingMessageCounter(ClientHelper ch, GroupId g,
|
private void decreaseOutgoingMessageCounter(ClientHelper ch, GroupId g)
|
||||||
int num) throws FormatException, DbException {
|
throws FormatException, DbException {
|
||||||
BdfDictionary gD = ch.getGroupMetadataAsDictionary(g);
|
BdfDictionary gD = ch.getGroupMetadataAsDictionary(g);
|
||||||
LOG.warning(gD.toString());
|
LOG.warning(gD.toString());
|
||||||
BdfDictionary queue = gD.getDictionary(QUEUE_STATE_KEY);
|
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);
|
gD.put(QUEUE_STATE_KEY, queue);
|
||||||
ch.mergeGroupMetadata(g, gD);
|
ch.mergeGroupMetadata(g, gD);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user