Merge branch '617-crypto-labels' into 'master'

Use namespaced labels for all crypto operations

See merge request !632
This commit is contained in:
akwizgran
2017-12-05 16:04:35 +00:00
36 changed files with 960 additions and 933 deletions

View File

@@ -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.
*/ */

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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";
} }

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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";
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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()) {

View File

@@ -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

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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 =

View File

@@ -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);
} }
} }

View File

@@ -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));
} }

View File

@@ -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());
} }
} }

View File

@@ -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);
} }

View File

@@ -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));
} }

View File

@@ -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)));
} }
} }

View File

@@ -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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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();

View File

@@ -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";
} }

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);
} }