diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
index 827c94fb7..8a6e914b1 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
@@ -18,19 +18,32 @@ public interface KeyManager {
/**
* Informs the key manager that a new contact has been added. Derives and
- * stores a set of transport keys for communicating with the contact over
- * each transport and returns the key set IDs.
+ * stores a set of rotation mode transport keys for communicating with the
+ * contact over each transport and returns the key set IDs.
*
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
- * @param alice true if the local party is Alice
- * @param active whether the derived keys can be used for outgoing streams
+ * @param alice True if the local party is Alice
+ * @param active Whether the derived keys can be used for outgoing streams
*/
Map addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException;
+ /**
+ * Informs the key manager that a new contact has been added. Derives and
+ * stores a set of handshake mode transport keys for communicating with the
+ * contact over each transport and returns the key set IDs.
+ *
+ * {@link StreamContext StreamContexts} for the contact can be created
+ * after this method has returned.
+ *
+ * @param alice True if the local party is ALice
+ */
+ Map addContact(Transaction txn, ContactId c,
+ SecretKey rootKey, boolean alice) throws DbException;
+
/**
* Marks the given transport keys as usable for outgoing streams.
*/
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
index 18d1b63ff..499e9dc22 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
@@ -100,6 +100,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ids;
}
+ @Override
+ public Map addContact(Transaction txn,
+ ContactId c, SecretKey rootKey, boolean alice) throws DbException {
+ Map ids = new HashMap<>();
+ for (Entry e : managers.entrySet()) {
+ TransportId t = e.getKey();
+ TransportKeyManager m = e.getValue();
+ ids.put(t, m.addContact(txn, c, rootKey, alice));
+ }
+ return ids;
+ }
+
@Override
public void activateKeys(Transaction txn, Map keys)
throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
index fa3f54e11..d7758043f 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
@@ -18,6 +18,9 @@ interface TransportKeyManager {
KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
long timestamp, boolean alice, boolean active) throws DbException;
+ KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
+ boolean alice) throws DbException;
+
void activateKeys(Transaction txn, KeySetId k) throws DbException;
void removeContact(ContactId c);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
index 3e070e28f..86ce89e10 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
@@ -213,6 +213,26 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
}
+ @Override
+ public KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
+ boolean alice) throws DbException {
+ lock.lock();
+ try {
+ // Work out what time period we're in
+ long timePeriod = clock.currentTimeMillis() / timePeriodLength;
+ // Derive the transport keys
+ TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
+ rootKey, timePeriod, alice);
+ // Write the keys back to the DB
+ KeySetId keySetId = db.addTransportKeys(txn, c, k);
+ // Initialise mutable state for the contact
+ addKeys(keySetId, c, null, new MutableTransportKeys(k));
+ return keySetId;
+ } finally {
+ lock.unlock();
+ }
+ }
+
@Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
lock.lock();
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java
index e81264c3f..5f25ff777 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java
@@ -83,7 +83,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
}
@Test
- public void testAddContact() throws Exception {
+ public void testAddContactWithRotationModeKeys() throws Exception {
SecretKey secretKey = getSecretKey();
long timestamp = System.currentTimeMillis();
boolean alice = random.nextBoolean();
@@ -100,6 +100,22 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
assertEquals(singletonMap(transportId, keySetId), ids);
}
+ @Test
+ public void testAddContactWithHandshakeModeKeys() throws Exception {
+ SecretKey secretKey = getSecretKey();
+ boolean alice = random.nextBoolean();
+
+ context.checking(new Expectations() {{
+ oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
+ alice);
+ will(returnValue(keySetId));
+ }});
+
+ Map ids = keyManager.addContact(txn, contactId,
+ secretKey, alice);
+ assertEquals(singletonMap(transportId, keySetId), ids);
+ }
+
@Test
public void testGetStreamContextForUnknownTransport() throws Exception {
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java
index 1aeabdc44..188be95f2 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java
@@ -70,14 +70,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
@Test
public void testKeysAreUpdatedAtStartup() throws Exception {
- TransportKeys shouldUpdate = createTransportKeys(900, 0, true);
- TransportKeys shouldNotUpdate = createTransportKeys(1000, 0, true);
+ boolean active = random.nextBoolean();
+ TransportKeys shouldUpdate = createTransportKeys(900, 0, active);
+ TransportKeys shouldNotUpdate = createTransportKeys(1000, 0, active);
Collection loaded = asList(
new TransportKeySet(keySetId, contactId, null, shouldUpdate),
new TransportKeySet(keySetId1, contactId1, null,
shouldNotUpdate)
);
- TransportKeys updated = createTransportKeys(1000, 0, true);
+ TransportKeys updated = createTransportKeys(1000, 0, active);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
@@ -111,19 +112,22 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
transportKeyManager.start(txn);
- assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
+ assertEquals(active,
+ transportKeyManager.canSendOutgoingStreams(contactId));
}
@Test
- public void testKeysAreUpdatedWhenAddingContact() throws Exception {
+ public void testRotationKeysAreDerivedAndUpdatedWhenAddingContact()
+ throws Exception {
boolean alice = random.nextBoolean();
- TransportKeys transportKeys = createTransportKeys(999, 0, true);
- TransportKeys updated = createTransportKeys(1000, 0, true);
+ boolean active = random.nextBoolean();
+ TransportKeys transportKeys = createTransportKeys(999, 0, active);
+ TransportKeys updated = createTransportKeys(1000, 0, active);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
- 999, alice, true);
+ 999, alice, active);
will(returnValue(transportKeys));
// Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis();
@@ -149,7 +153,43 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
// The timestamp is 1 ms before the start of time period 1000
long timestamp = timePeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
- rootKey, timestamp, alice, true));
+ rootKey, timestamp, alice, active));
+ assertEquals(active,
+ transportKeyManager.canSendOutgoingStreams(contactId));
+ }
+
+ @Test
+ public void testHandshakeKeysAreDerivedWhenAddingContact()
+ throws Exception {
+ boolean alice = random.nextBoolean();
+ TransportKeys transportKeys = createTransportKeys(1000, 0, true);
+ Transaction txn = new Transaction(null, false);
+
+ context.checking(new Expectations() {{
+ // Get the current time (1 ms after start of time period 1000)
+ oneOf(clock).currentTimeMillis();
+ will(returnValue(timePeriodLength * 1000 + 1));
+ // Derive the transport keys
+ oneOf(transportCrypto).deriveHandshakeKeys(transportId, rootKey,
+ 1000, alice);
+ will(returnValue(transportKeys));
+ // Encode the tags (3 sets)
+ for (long i = 0; i < REORDERING_WINDOW_SIZE; 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, transportKeys);
+ will(returnValue(keySetId));
+ }});
+
+ TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
+ db, transportCrypto, dbExecutor, scheduler, clock, transportId,
+ maxLatency);
+ assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+ rootKey, alice));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
}