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

@@ -141,8 +141,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
}
// Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
masterSecret);
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret);
// Create the readers
InputStream streamReader =
@@ -156,8 +157,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret);
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret);
// Exchange pseudonyms, signed nonces, and timestamps
long localTimestamp = clock.currentTimeMillis();
@@ -312,8 +313,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return contactId;
}
private void tryToClose(DuplexTransportConnection conn,
boolean exception) {
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(exception, true);

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.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
@@ -30,7 +26,6 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.KeyParameter;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
@@ -44,14 +39,8 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent {
@@ -65,39 +54,6 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final int PBKDF_TARGET_MILLIS = 500;
private static final int PBKDF_SAMPLES = 30;
private static final int HASH_SIZE = 256 / 8;
private static byte[] ascii(String s) {
return s.getBytes(Charset.forName("US-ASCII"));
}
// KDF labels for contact exchange stream header key derivation
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
// KDF labels for contact exchange signature nonce derivation
private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
// Hash label for BQP public key commitment derivation
private static final String COMMIT =
"org.briarproject.bramble.COMMIT";
// Hash label for shared secret derivation
private static final String SHARED_SECRET =
"org.briarproject.bramble.SHARED_SECRET";
// KDF label for BQP confirmation key derivation
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
// KDF label for master key derivation
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
// KDF labels for tag key derivation
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
// KDF labels for header key derivation
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
// KDF labels for MAC key derivation
private static final byte[] A_MAC = ascii("ALICE_MAC_KEY");
private static final byte[] B_MAC = ascii("BOB_MAC_KEY");
// KDF label for key rotation
private static final byte[] ROTATE = ascii("ROTATE");
private final SecureRandom secureRandom;
private final ECKeyPairGenerator agreementKeyPairGenerator;
@@ -263,179 +219,26 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public SecretKey deriveHeaderKey(SecretKey master,
boolean alice) {
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
byte[] mac = mac(label, k, inputs);
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
return new SecretKey(mac);
}
@Override
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
}
@Override
public byte[] deriveSignatureNonce(SecretKey master,
boolean alice) {
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
}
@Override
public byte[] deriveKeyCommitment(byte[] publicKey) {
byte[] hash = hash(COMMIT, publicKey);
// The output is the first COMMIT_LENGTH bytes of the hash
byte[] commitment = new byte[COMMIT_LENGTH];
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
return commitment;
}
@Override
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
PrivateKey ourPriv = ourKeyPair.getPrivate();
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
byte[] raw = performRawKeyAgreement(ourPriv, theirPublicKey);
byte[] alicePub, bobPub;
if (alice) {
alicePub = ourKeyPair.getPublic().getEncoded();
bobPub = theirPublicKey;
bobPub = theirPublicKey.getEncoded();
} else {
alicePub = theirPublicKey;
alicePub = theirPublicKey.getEncoded();
bobPub = ourKeyPair.getPublic().getEncoded();
}
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
}
@Override
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
byte[] alicePayload, alicePub, bobPayload, bobPub;
if (alice) {
alicePayload = ourPayload;
alicePub = ourKeyPair.getPublic().getEncoded();
bobPayload = theirPayload;
bobPub = theirPublicKey;
} 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);
return new SecretKey(hash(label, raw, alicePub, bobPub));
}
@Override
@@ -513,14 +316,13 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public int getHashLength() {
return HASH_SIZE;
}
@Override
public byte[] mac(SecretKey macKey, byte[]... inputs) {
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
byte[] labelBytes = StringUtils.toUtf8(label);
Digest mac = new Blake2sDigest(macKey.getBytes());
byte[] length = new byte[INT_32_BYTES];
ByteUtils.writeUint32(labelBytes.length, length, 0);
mac.update(length, 0, length.length);
mac.update(labelBytes, 0, labelBytes.length);
for (byte[] input : inputs) {
ByteUtils.writeUint32(input.length, length, 0);
mac.update(length, 0, length.length);
@@ -612,30 +414,6 @@ class CryptoComponentImpl implements CryptoComponent {
return AsciiArmour.wrap(b, lineLength);
}
// Key derivation function based on a pseudo-random function - see
// NIST SP 800-108, section 5.1
private byte[] macKdf(SecretKey key, byte[]... inputs) {
// Initialise the PRF
Digest prf = new Blake2sDigest(key.getBytes());
// The output of the PRF must be long enough to use as a key
int macLength = prf.getDigestSize();
if (macLength < SecretKey.LENGTH) throw new IllegalStateException();
// Calculate the PRF over the concatenated length-prefixed inputs
byte[] length = new byte[INT_32_BYTES];
for (byte[] input : inputs) {
ByteUtils.writeUint32(input.length, length, 0);
prf.update(length, 0, length.length);
prf.update(input, 0, input.length);
}
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first SecretKey.LENGTH bytes of the MAC
if (mac.length == SecretKey.LENGTH) return mac;
byte[] truncated = new byte[SecretKey.LENGTH];
System.arraycopy(mac, 0, truncated, 0, truncated.length);
return truncated;
}
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
byte[] utf8 = StringUtils.toUtf8(password);

View File

@@ -3,9 +3,11 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SecureRandomProvider;
@@ -74,6 +76,12 @@ public class CryptoModule {
return new PasswordStrengthEstimatorImpl();
}
@Provides
TransportCrypto provideTransportCrypto(
TransportCryptoImpl transportCrypto) {
return transportCrypto;
}
@Provides
StreamDecrypterFactory provideStreamDecrypterFactory(
Provider<AuthenticatedCipher> cipherProvider) {
@@ -81,9 +89,17 @@ public class CryptoModule {
}
@Provides
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
StreamEncrypterFactory provideStreamEncrypterFactory(
CryptoComponent crypto, TransportCrypto transportCrypto,
Provider<AuthenticatedCipher> cipherProvider) {
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
cipherProvider);
}
@Provides
KeyAgreementCrypto provideKeyAgreementCrypto(
KeyAgreementCryptoImpl keyAgreementCrypto) {
return keyAgreementCrypto;
}
@Provides

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.StreamEncrypter;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamContext;
@@ -22,12 +23,15 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
private final CryptoComponent crypto;
private final TransportCrypto transportCrypto;
private final Provider<AuthenticatedCipher> cipherProvider;
@Inject
StreamEncrypterFactoryImpl(CryptoComponent crypto,
TransportCrypto transportCrypto,
Provider<AuthenticatedCipher> cipherProvider) {
this.crypto = crypto;
this.transportCrypto = transportCrypto;
this.cipherProvider = cipherProvider;
}
@@ -37,7 +41,8 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey();

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;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
@@ -46,7 +46,7 @@ class KeyAgreementConnector {
private final Callbacks callbacks;
private final Clock clock;
private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final PluginManager pluginManager;
private final CompletionService<KeyAgreementConnection> connect;
@@ -58,11 +58,11 @@ class KeyAgreementConnector {
private volatile boolean alice = false;
KeyAgreementConnector(Callbacks callbacks, Clock clock,
CryptoComponent crypto, PluginManager pluginManager,
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
Executor ioExecutor) {
this.callbacks = callbacks;
this.clock = clock;
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.pluginManager = pluginManager;
connect = new ExecutorCompletionService<>(ioExecutor);
}
@@ -70,8 +70,8 @@ class KeyAgreementConnector {
public Payload listen(KeyPair localKeyPair) {
LOG.info("Starting BQP listeners");
// Derive commitment
byte[] commitment = crypto.deriveKeyCommitment(
localKeyPair.getPublic().getEncoded());
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
localKeyPair.getPublic());
// Start all listeners and collect their descriptors
List<TransportDescriptor> descriptors = new ArrayList<>();
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {

View File

@@ -1,19 +1,10 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@@ -22,13 +13,9 @@ import dagger.Provides;
public class KeyAgreementModule {
@Provides
@Singleton
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
CryptoComponent crypto, EventBus eventBus,
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
PluginManager pluginManager) {
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
ioExecutor, payloadEncoder, pluginManager);
KeyAgreementTask provideKeyAgreementTask(
KeyAgreementTaskImpl keyAgreementTask) {
return keyAgreementTask;
}
@Provides

View File

@@ -1,7 +1,10 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
@@ -11,6 +14,9 @@ import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
/**
* Implementation of the BQP protocol.
* <p/>
@@ -57,6 +63,7 @@ class KeyAgreementProtocol {
private final Callbacks callbacks;
private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final PayloadEncoder payloadEncoder;
private final KeyAgreementTransport transport;
private final Payload theirPayload, ourPayload;
@@ -64,11 +71,13 @@ class KeyAgreementProtocol {
private final boolean alice;
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto,
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
boolean alice) {
this.callbacks = callbacks;
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.payloadEncoder = payloadEncoder;
this.transport = transport;
this.theirPayload = theirPayload;
@@ -86,7 +95,7 @@ class KeyAgreementProtocol {
*/
SecretKey perform() throws AbortException, IOException {
try {
byte[] theirPublicKey;
PublicKey theirPublicKey;
if (alice) {
sendKey();
// Alice waits here until Bob obtains her payload.
@@ -104,7 +113,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey);
}
return crypto.deriveMasterSecret(s);
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
} catch (AbortException e) {
sendAbort(e.getCause() != null);
throw e;
@@ -115,27 +124,34 @@ class KeyAgreementProtocol {
transport.sendKey(ourKeyPair.getPublic().getEncoded());
}
private byte[] receiveKey() throws AbortException {
byte[] publicKey = transport.receiveKey();
private PublicKey receiveKey() throws AbortException {
byte[] publicKeyBytes = transport.receiveKey();
callbacks.initialRecordReceived();
byte[] expected = crypto.deriveKeyCommitment(publicKey);
if (!Arrays.equals(expected, theirPayload.getCommitment()))
KeyParser keyParser = crypto.getAgreementKeyParser();
try {
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
if (!Arrays.equals(expected, theirPayload.getCommitment()))
throw new AbortException();
return publicKey;
} catch (GeneralSecurityException e) {
throw new AbortException();
return publicKey;
}
}
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
throws AbortException {
try {
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
theirPublicKey, ourKeyPair, alice);
} catch (GeneralSecurityException e) {
throw new AbortException(e);
}
}
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
throws IOException {
byte[] confirm = crypto.deriveConfirmationRecord(s,
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload),
theirPublicKey, ourKeyPair,
@@ -143,10 +159,10 @@ class KeyAgreementProtocol {
transport.sendConfirm(confirm);
}
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
throws AbortException {
byte[] confirm = transport.receiveConfirm();
byte[] expected = crypto.deriveConfirmationRecord(s,
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload),
theirPublicKey, ourKeyPair,

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;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.event.EventBus;
@@ -14,6 +15,7 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
@@ -23,6 +25,8 @@ import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@@ -35,6 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final EventBus eventBus;
private final PayloadEncoder payloadEncoder;
private final KeyPair localKeyPair;
@@ -43,14 +48,17 @@ class KeyAgreementTaskImpl extends Thread implements
private Payload localPayload;
private Payload remotePayload;
@Inject
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
EventBus eventBus, PayloadEncoder payloadEncoder,
PluginManager pluginManager, Executor ioExecutor) {
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
PayloadEncoder payloadEncoder, PluginManager pluginManager,
@IoExecutor Executor ioExecutor) {
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.eventBus = eventBus;
this.payloadEncoder = payloadEncoder;
localKeyPair = crypto.generateAgreementKeyPair();
connector = new KeyAgreementConnector(this, clock, crypto,
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
pluginManager, ioExecutor);
}
@@ -100,8 +108,8 @@ class KeyAgreementTaskImpl extends Thread implements
// Run BQP protocol over the connection
LOG.info("Starting BQP protocol");
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
payloadEncoder, transport, remotePayload, localPayload,
localKeyPair, alice);
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
localPayload, localKeyPair, alice);
try {
SecretKey master = protocol.perform();
KeyAgreementResult result =

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -20,17 +20,18 @@ class TransportKeyManagerFactoryImpl implements
TransportKeyManagerFactory {
private final DatabaseComponent db;
private final CryptoComponent crypto;
private final TransportCrypto transportCrypto;
private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final Clock clock;
@Inject
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
TransportKeyManagerFactoryImpl(DatabaseComponent db,
TransportCrypto transportCrypto,
@DatabaseExecutor Executor dbExecutor,
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
this.db = db;
this.crypto = crypto;
this.transportCrypto = transportCrypto;
this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.clock = clock;
@@ -39,8 +40,8 @@ class TransportKeyManagerFactoryImpl implements
@Override
public TransportKeyManager createTransportKeyManager(
TransportId transportId, long maxLatency) {
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
clock, transportId, maxLatency);
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
scheduler, clock, transportId, maxLatency);
}
}

View File

@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
Logger.getLogger(TransportKeyManagerImpl.class.getName());
private final DatabaseComponent db;
private final CryptoComponent crypto;
private final TransportCrypto transportCrypto;
private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final Clock clock;
@@ -54,11 +54,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final Map<ContactId, MutableOutgoingKeys> outContexts;
private final Map<ContactId, MutableTransportKeys> keys;
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
Clock clock, TransportId transportId, long maxLatency) {
TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, Executor dbExecutor,
@Scheduler ScheduledExecutorService scheduler, Clock clock,
TransportId transportId, long maxLatency) {
this.db = db;
this.crypto = crypto;
this.transportCrypto = transportCrypto;
this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.clock = clock;
@@ -99,7 +100,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue();
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
TransportKeys k1 =
transportCrypto.rotateTransportKeys(k, rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod())
rotationResult.rotated.put(c, k1);
rotationResult.current.put(c, k1);
@@ -127,7 +129,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
inContexts.put(new Bytes(tag), tagCtx);
}
@@ -162,11 +164,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Work out what rotation period the timestamp belongs to
long rotationPeriod = timestamp / rotationPeriodLength;
// Derive the transport keys
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
rotationPeriod, alice);
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, rotationPeriod, alice);
// Rotate the keys to the current rotation period if necessary
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
k = crypto.rotateTransportKeys(k, rotationPeriod);
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
// Initialise mutable state for the contact
addKeys(c, new MutableTransportKeys(k));
// Write the keys back to the DB
@@ -234,8 +236,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber));
}
@@ -243,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
crypto.encodeTag(removeTag, inKeys.getTagKey(),
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag));
}

View File

@@ -3,40 +3,25 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.junit.Assert.assertArrayEquals;
public class KeyAgreementTest extends BrambleTestCase {
@Test
public void testDeriveMasterSecret() throws Exception {
SecureRandomProvider
secureRandomProvider = new TestSecureRandomProvider();
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
KeyPair aPair = crypto.generateAgreementKeyPair();
byte[] aPub = aPair.getPublic().getEncoded();
KeyPair bPair = crypto.generateAgreementKeyPair();
byte[] bPub = bPair.getPublic().getEncoded();
SecretKey aMaster = crypto.deriveMasterSecret(aPub, bPair, true);
SecretKey bMaster = crypto.deriveMasterSecret(bPub, aPair, false);
assertArrayEquals(aMaster.getBytes(), bMaster.getBytes());
}
@Test
public void testDeriveSharedSecret() throws Exception {
SecureRandomProvider
secureRandomProvider = new TestSecureRandomProvider();
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider());
KeyPair aPair = crypto.generateAgreementKeyPair();
byte[] aPub = aPair.getPublic().getEncoded();
KeyPair bPair = crypto.generateAgreementKeyPair();
byte[] bPub = bPair.getPublic().getEncoded();
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
bPair.getPublic(), aPair, true);
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
aPair.getPublic(), bPair, false);
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
}
}

View File

@@ -3,11 +3,11 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.ArrayList;
@@ -16,35 +16,34 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class KeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider());
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = new TransportId("id");
private final CryptoComponent crypto;
private final SecretKey master;
public KeyDerivationTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
master = TestUtils.getSecretKey();
}
private final SecretKey master = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
assertAllDifferent(k);
}
@Test
public void testCurrentKeysMatchCurrentKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
123, false);
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
@@ -56,8 +55,8 @@ public class KeyDerivationTest extends BrambleTestCase {
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
// Rotate into the future
kA = crypto.rotateTransportKeys(kA, 456);
kB = crypto.rotateTransportKeys(kB, 456);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
@@ -73,22 +72,23 @@ public class KeyDerivationTest extends BrambleTestCase {
@Test
public void testPreviousKeysMatchPreviousKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
123, false);
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = crypto.rotateTransportKeys(kA, 456);
kB = crypto.rotateTransportKeys(kB, 455);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
assertArrayEquals(
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = crypto.rotateTransportKeys(kB, 457);
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's outgoing keys should equal Bob's previous incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getPreviousIncomingKeys().getTagKey().getBytes());
@@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase {
@Test
public void testNextKeysMatchNextKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
123, false);
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = crypto.rotateTransportKeys(kA, 456);
kB = crypto.rotateTransportKeys(kB, 455);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's outgoing keys should equal Bob's next incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getNextIncomingKeys().getTagKey().getBytes());
@@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase {
kB.getNextIncomingKeys().getHeaderKey().getBytes());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = crypto.rotateTransportKeys(kB, 457);
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
@@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase {
@Test
public void testMasterKeyAffectsOutput() {
SecretKey master1 = TestUtils.getSecretKey();
SecretKey master1 = getSecretKey();
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1,
123, true);
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
master1, 123, true);
assertAllDifferent(k, k1);
}
@@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase {
public void testTransportIdAffectsOutput() {
TransportId transportId1 = new TransportId("id1");
assertFalse(transportId.getString().equals(transportId1.getString()));
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
123, true);
TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master,
123, true);
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
master, 123, true);
assertAllDifferent(k, k1);
}

View File

@@ -4,42 +4,49 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
public class MacTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider());
private final SecretKey k = TestUtils.getSecretKey();
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
private final byte[] inputBytes2 = new byte[0];
public MacTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
}
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
private final String label1 = getRandomString(123);
private final String label2 = getRandomString(123);
private final byte[] input1 = getRandomBytes(123);
private final byte[] input2 = getRandomBytes(234);
private final byte[] input3 = new byte[0];
@Test
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
// Calculate the MAC twice - the results should be identical
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3);
assertArrayEquals(mac, mac1);
}
@Test
public void testDifferentLabelsProduceDifferentMacs() {
// Calculate the MAC with each label - the results should be different
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
byte[] mac1 = crypto.mac(label2, key1, input1, input2, input3);
assertFalse(Arrays.equals(mac, mac1));
}
@Test
public void testDifferentKeysProduceDifferentMacs() {
// Generate second random key
SecretKey k1 = TestUtils.getSecretKey();
// Calculate the MAC with each key - the results should be different
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3);
assertFalse(Arrays.equals(mac, mac1));
}
@@ -47,8 +54,8 @@ public class MacTest extends BrambleTestCase {
public void testDifferentInputsProduceDifferentMacs() {
// Calculate the MAC with the inputs in different orders - the results
// should be different
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1);
assertFalse(Arrays.equals(mac, mac1));
}

View File

@@ -3,9 +3,8 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.junit.Test;
import java.util.HashSet;
@@ -14,25 +13,25 @@ import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
public class TagEncodingTest extends BrambleTestCase {
public class TagEncodingTest extends BrambleMockTestCase {
private final CryptoComponent crypto;
private final SecretKey tagKey;
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final SecretKey tagKey = getSecretKey();
private final long streamNumber = 1234567890;
public TagEncodingTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
tagKey = TestUtils.getSecretKey();
}
@Test
public void testKeyAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = TestUtils.getSecretKey();
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
SecretKey tagKey = getSecretKey();
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@@ -42,7 +41,8 @@ public class TagEncodingTest extends BrambleTestCase {
Set<Bytes> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i,
streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@@ -52,7 +52,8 @@ public class TagEncodingTest extends BrambleTestCase {
Set<Bytes> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
streamNumber + i);
assertTrue(set.add(new Bytes(tag)));
}
}

View File

@@ -1,13 +1,14 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations;
import org.jmock.auto.Mock;
import org.jmock.integration.junit4.JUnitRuleMockery;
@@ -16,6 +17,10 @@ import org.junit.Rule;
import org.junit.Test;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@@ -28,34 +33,33 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
setImposteriser(ClassImposteriser.INSTANCE);
}};
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
private static final byte[] ALICE_COMMIT =
TestUtils.getRandomBytes(COMMIT_LENGTH);
private static final byte[] ALICE_PAYLOAD =
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
private final PublicKey alicePubKey =
context.mock(PublicKey.class, "alice");
private final byte[] alicePubKeyBytes = getRandomBytes(32);
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
private static final byte[] BOB_COMMIT =
TestUtils.getRandomBytes(COMMIT_LENGTH);
private static final byte[] BOB_PAYLOAD =
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
private final byte[] bobPubKeyBytes = getRandomBytes(32);
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
private static final byte[] ALICE_CONFIRM =
TestUtils.getRandomBytes(SecretKey.LENGTH);
private static final byte[] BOB_CONFIRM =
TestUtils.getRandomBytes(SecretKey.LENGTH);
private static final byte[] BAD_PUBKEY = TestUtils.getRandomBytes(32);
private static final byte[] BAD_COMMIT =
TestUtils.getRandomBytes(COMMIT_LENGTH);
private static final byte[] BAD_CONFIRM =
TestUtils.getRandomBytes(SecretKey.LENGTH);
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
private final byte[] badPubKeyBytes = getRandomBytes(32);
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
@Mock
KeyAgreementProtocol.Callbacks callbacks;
@Mock
CryptoComponent crypto;
@Mock
KeyAgreementCrypto keyAgreementCrypto;
@Mock
KeyParser keyParser;
@Mock
PayloadEncoder payloadEncoder;
@Mock
KeyAgreementTransport transport;
@@ -65,60 +69,67 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test
public void testAliceProtocol() throws Exception {
// set up
Payload theirPayload = new Payload(BOB_COMMIT, null);
Payload ourPayload = new Payload(ALICE_COMMIT, null);
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = TestUtils.getSecretKey();
SecretKey masterSecret = TestUtils.getSecretKey();
SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey();
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, true);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, true);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(payloadEncoder).encode(ourPayload);
will(returnValue(ALICE_PAYLOAD));
will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(BOB_PAYLOAD));
will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(ALICE_PUBKEY));
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Alice sends her public key
oneOf(transport).sendKey(ALICE_PUBKEY);
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(BOB_PUBKEY));
will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey));
// Alice verifies Bob's public key
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
will(returnValue(BOB_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
will(returnValue(bobCommit));
// Alice computes shared secret
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, true);
will(returnValue(sharedSecret));
// Alice sends her confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
will(returnValue(ALICE_CONFIRM));
oneOf(transport).sendConfirm(ALICE_CONFIRM);
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
bobPayload, alicePayload, bobPubKey, ourKeyPair,
true, true);
will(returnValue(aliceConfirm));
oneOf(transport).sendConfirm(aliceConfirm);
// Alice receives Bob's confirmation record
oneOf(transport).receiveConfirm();
will(returnValue(BOB_CONFIRM));
will(returnValue(bobConfirm));
// Alice verifies Bob's confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
will(returnValue(BOB_CONFIRM));
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
bobPayload, alicePayload, bobPubKey, ourKeyPair,
true, false);
will(returnValue(bobConfirm));
// Alice computes master secret
oneOf(crypto).deriveMasterSecret(sharedSecret);
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
will(returnValue(masterSecret));
}});
@@ -129,59 +140,66 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test
public void testBobProtocol() throws Exception {
// set up
Payload theirPayload = new Payload(ALICE_COMMIT, null);
Payload ourPayload = new Payload(BOB_COMMIT, null);
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = TestUtils.getSecretKey();
SecretKey masterSecret = TestUtils.getSecretKey();
SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey();
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, false);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, false);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(payloadEncoder).encode(ourPayload);
will(returnValue(BOB_PAYLOAD));
will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(ALICE_PAYLOAD));
will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(BOB_PUBKEY));
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Bob receives Alice's public key
oneOf(transport).receiveKey();
will(returnValue(ALICE_PUBKEY));
will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey));
// Bob verifies Alice's public key
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
will(returnValue(ALICE_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
will(returnValue(aliceCommit));
// Bob sends his public key
oneOf(transport).sendKey(BOB_PUBKEY);
oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, false);
will(returnValue(sharedSecret));
// Bob receives Alices's confirmation record
oneOf(transport).receiveConfirm();
will(returnValue(ALICE_CONFIRM));
will(returnValue(aliceConfirm));
// Bob verifies Alice's confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
will(returnValue(ALICE_CONFIRM));
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
alicePayload, bobPayload, alicePubKey, ourKeyPair,
false, true);
will(returnValue(aliceConfirm));
// Bob sends his confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
will(returnValue(BOB_CONFIRM));
oneOf(transport).sendConfirm(BOB_CONFIRM);
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
alicePayload, bobPayload, alicePubKey, ourKeyPair,
false, false);
will(returnValue(bobConfirm));
oneOf(transport).sendConfirm(bobConfirm);
// Bob computes master secret
oneOf(crypto).deriveMasterSecret(sharedSecret);
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
will(returnValue(masterSecret));
}});
@@ -192,38 +210,43 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadKey() throws Exception {
// set up
Payload theirPayload = new Payload(BOB_COMMIT, null);
Payload ourPayload = new Payload(ALICE_COMMIT, null);
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, true);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, true);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(ALICE_PUBKEY));
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Alice sends her public key
oneOf(transport).sendKey(ALICE_PUBKEY);
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives a bad public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(BAD_PUBKEY));
will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey));
// Alice verifies Bob's public key
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
will(returnValue(BAD_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
will(returnValue(badCommit));
// Alice aborts
oneOf(transport).sendAbort(false);
// Alice never computes shared secret
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
ourKeyPair, true);
}});
// execute
@@ -233,34 +256,38 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadKey() throws Exception {
// set up
Payload theirPayload = new Payload(ALICE_COMMIT, null);
Payload ourPayload = new Payload(BOB_COMMIT, null);
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, false);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, false);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(BOB_PUBKEY));
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Bob receives a bad public key
oneOf(transport).receiveKey();
will(returnValue(BAD_PUBKEY));
will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey));
// Bob verifies Alice's public key
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
will(returnValue(BAD_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
will(returnValue(badCommit));
// Bob aborts
oneOf(transport).sendAbort(false);
// Bob never sends his public key
never(transport).sendKey(BOB_PUBKEY);
never(transport).sendKey(bobPubKeyBytes);
}});
// execute
@@ -270,62 +297,69 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
// set up
Payload theirPayload = new Payload(BOB_COMMIT, null);
Payload ourPayload = new Payload(ALICE_COMMIT, null);
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = TestUtils.getSecretKey();
SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, true);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, true);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(payloadEncoder).encode(ourPayload);
will(returnValue(ALICE_PAYLOAD));
will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(BOB_PAYLOAD));
will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(ALICE_PUBKEY));
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Alice sends her public key
oneOf(transport).sendKey(ALICE_PUBKEY);
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(BOB_PUBKEY));
will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey));
// Alice verifies Bob's public key
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
will(returnValue(BOB_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
will(returnValue(bobCommit));
// Alice computes shared secret
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, true);
will(returnValue(sharedSecret));
// Alice sends her confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
will(returnValue(ALICE_CONFIRM));
oneOf(transport).sendConfirm(ALICE_CONFIRM);
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
bobPayload, alicePayload, bobPubKey, ourKeyPair,
true, true);
will(returnValue(aliceConfirm));
oneOf(transport).sendConfirm(aliceConfirm);
// Alice receives a bad confirmation record
oneOf(transport).receiveConfirm();
will(returnValue(BAD_CONFIRM));
will(returnValue(badConfirm));
// Alice verifies Bob's confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
will(returnValue(BOB_CONFIRM));
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
bobPayload, alicePayload, bobPubKey, ourKeyPair,
true, false);
will(returnValue(bobConfirm));
// Alice aborts
oneOf(transport).sendAbort(false);
// Alice never computes master secret
never(crypto).deriveMasterSecret(sharedSecret);
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
}});
// execute
@@ -335,56 +369,63 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadConfirm() throws Exception {
// set up
Payload theirPayload = new Payload(ALICE_COMMIT, null);
Payload ourPayload = new Payload(BOB_COMMIT, null);
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = TestUtils.getSecretKey();
SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol =
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
transport, theirPayload, ourPayload, ourKeyPair, false);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
theirPayload, ourPayload, ourKeyPair, false);
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(payloadEncoder).encode(ourPayload);
will(returnValue(BOB_PAYLOAD));
will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(ALICE_PAYLOAD));
will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(BOB_PUBKEY));
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Bob receives Alice's public key
oneOf(transport).receiveKey();
will(returnValue(ALICE_PUBKEY));
will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey));
// Bob verifies Alice's public key
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
will(returnValue(ALICE_COMMIT));
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
will(returnValue(aliceCommit));
// Bob sends his public key
oneOf(transport).sendKey(BOB_PUBKEY);
oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, false);
will(returnValue(sharedSecret));
// Bob receives a bad confirmation record
oneOf(transport).receiveConfirm();
will(returnValue(BAD_CONFIRM));
will(returnValue(badConfirm));
// Bob verifies Alice's confirmation record
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
will(returnValue(ALICE_CONFIRM));
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
alicePayload, bobPayload, alicePubKey, ourKeyPair,
false, true);
will(returnValue(aliceConfirm));
// Bob aborts
oneOf(transport).sendAbort(false);
// Bob never sends his confirmation record
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
never(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
alicePayload, bobPayload, alicePubKey, ourKeyPair,
false, false);
}});
// execute

View File

@@ -1,8 +1,8 @@
package org.briarproject.bramble.sync;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.ClientId;
@@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
@Inject
RecordWriterFactory recordWriterFactory;
@Inject
CryptoComponent crypto;
TransportCrypto transportCrypto;
private final ContactId contactId;
private final TransportId transportId;
@@ -117,7 +117,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION,
streamNumber);
// Read the tag
InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -1,8 +1,8 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -11,12 +11,11 @@ import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.RunAction;
import org.briarproject.bramble.test.TestUtils;
import org.hamcrest.Description;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
import org.junit.Test;
@@ -41,7 +40,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class TransportKeyManagerImplTest extends BrambleTestCase {
public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final TransportCrypto transportCrypto =
context.mock(TransportCrypto.class);
private final Executor dbExecutor = context.mock(Executor.class);
private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final Clock clock = context.mock(Clock.class);
private final TransportId transportId = new TransportId("id");
private final long maxLatency = 30 * 1000; // 30 seconds
@@ -55,14 +62,6 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
@Test
public void testKeysAreRotatedAtStartup() throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
TransportKeys shouldRotate = createTransportKeys(900, 0);
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
@@ -79,14 +78,15 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded));
// Rotate the transport keys
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
will(returnValue(rotated));
oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000);
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
will(returnValue(shouldNotRotate));
// Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(6).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated
@@ -97,161 +97,124 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
with(rotationPeriodLength - 1), with(MILLISECONDS));
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
transportKeyManager.start(txn);
context.assertIsSatisfied();
}
@Test
public void testKeysAreRotatedWhenAddingContact() throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
boolean alice = true;
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(999, 0);
TransportKeys rotated = createTransportKeys(1000, 0);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
alice);
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
999, alice);
will(returnValue(transportKeys));
// Get the current time (1 ms after start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1));
// Rotate the transport keys
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(rotated));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, rotated);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is 1 ms before the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000 - 1;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
context.assertIsSatisfied();
}
@Test
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
Transaction txn = new Transaction(null, false);
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@Test
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
boolean alice = true;
boolean alice = random.nextBoolean();
// The stream counter has been exhausted
TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED + 1);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@Test
public void testOutgoingStreamCounterIsIncremented() throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
boolean alice = true;
boolean alice = random.nextBoolean();
// The stream counter can be used one more time before being exhausted
TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
@@ -259,9 +222,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -277,94 +240,76 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
// The second request should return null, the counter is exhausted
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@Test
public void testIncomingStreamContextIsNullIfTagIsNotFound()
throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
boolean alice = true;
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
assertNull(transportKeyManager.getStreamContext(txn,
new byte[TAG_LENGTH]));
context.assertIsSatisfied();
}
@Test
public void testTagIsNotRecognisedTwice() throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
boolean alice = true;
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0);
// Keep a copy of the tags
List<byte[]> tags = new ArrayList<>();
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags));
}
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Encode a new tag after sliding the window
oneOf(crypto).encodeTag(with(any(byte[].class)),
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags));
@@ -373,9 +318,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
1, new byte[REORDERING_WINDOW_SIZE / 8]);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -395,20 +340,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
// The second request should return null, the tag has already been used
assertNull(transportKeyManager.getStreamContext(txn, tag));
context.assertIsSatisfied();
}
@Test
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
Mockery context = new Mockery();
DatabaseComponent db = context.mock(DatabaseComponent.class);
CryptoComponent crypto = context.mock(CryptoComponent.class);
Executor dbExecutor = context.mock(Executor.class);
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
Clock clock = context.mock(Clock.class);
TransportKeys transportKeys = createTransportKeys(1000, 0);
Map<ContactId, TransportKeys> loaded =
Collections.singletonMap(contactId, transportKeys);
@@ -424,12 +359,13 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded));
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Schedule key rotation at the start of the next rotation period
@@ -445,13 +381,14 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1001));
// Rotate the transport keys
oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)),
with(1001L));
oneOf(transportCrypto).rotateTransportKeys(
with(any(TransportKeys.class)), with(1001L));
will(returnValue(rotated));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated
@@ -465,12 +402,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn1);
}});
TransportKeyManager
transportKeyManager = new TransportKeyManagerImpl(db,
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
transportKeyManager.start(txn);
context.assertIsSatisfied();
}
private TransportKeys createTransportKeys(long rotationPeriod,