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 97afdc133e1a672ecfcd5bda9959aa64ca619e2f..1e26a7b3d05f079bab61e44b61465bd05b223115 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 @@ -16,13 +16,21 @@ public interface KeyManager { /** * Informs the key manager that a new contact has been added. Derives and - * stores transport keys for communicating with the contact. + * stores a set of transport keys for communicating with the contact over + * each transport. + * <p/> * {@link StreamContext StreamContexts} for the contact can be created * after this method has returned. */ void addContact(Transaction txn, ContactId c, SecretKey master, long timestamp, boolean alice) throws DbException; + /** + * Derives and stores a set of unbound transport keys for each transport. + */ + void addUnboundKeys(Transaction txn, SecretKey master, long timestamp, + boolean alice) throws DbException; + /** * Returns a {@link StreamContext} for sending a stream to the given * contact over the given transport, or null if an error occurs or the diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java index 25e0681c963778bf649bef07e801a2a06474314a..afac362ced269204d1a9c0376ea12962513d241e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java @@ -50,7 +50,7 @@ class ContactManagerImpl implements ContactManager { @Override public ContactId addContact(Transaction txn, Author remote, AuthorId local, - SecretKey master,long timestamp, boolean alice, boolean verified, + SecretKey master, long timestamp, boolean alice, boolean verified, boolean active) throws DbException { ContactId c = db.addContact(txn, remote, local, verified, active); keyManager.addContact(txn, c, master, timestamp, alice); 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 d0c6fd709d619911f6cc9862b99947df3eb82bfe..be50963e0df00656b0216f0ebe6016ed0e80064f 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 @@ -104,6 +104,13 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { m.addContact(txn, c, master, timestamp, alice); } + @Override + public void addUnboundKeys(Transaction txn, SecretKey master, + long timestamp, boolean alice) throws DbException { + for (TransportKeyManager m : managers.values()) + m.addUnboundKeys(txn, master, timestamp, alice); + } + @Override public StreamContext getStreamContext(ContactId c, TransportId t) 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 6aa6d360fe2b24af7a643167347c955f75e90bdb..8c9b03fd8b8bd1eeaba7c7419e20c118f76ecc90 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 @@ -17,6 +17,9 @@ interface TransportKeyManager { void addContact(Transaction txn, ContactId c, SecretKey master, long timestamp, boolean alice) throws DbException; + void addUnboundKeys(Transaction txn, SecretKey master, long timestamp, + boolean alice) throws DbException; + void removeContact(ContactId c); @Nullable 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 1220beee580ea7af59c0b682af94546fd80e0068..7b4893e5617b27093b45d88edeb1b65c6fcc0561 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 @@ -172,6 +172,18 @@ class TransportKeyManagerImpl implements TransportKeyManager { @Override public void addContact(Transaction txn, ContactId c, SecretKey master, long timestamp, boolean alice) throws DbException { + deriveAndAddKeys(txn, c, master, timestamp, alice); + } + + @Override + public void addUnboundKeys(Transaction txn, SecretKey master, + long timestamp, boolean alice) throws DbException { + deriveAndAddKeys(txn, null, master, timestamp, alice); + } + + private void deriveAndAddKeys(Transaction txn, @Nullable ContactId c, + SecretKey master, long timestamp, boolean alice) + throws DbException { lock.lock(); try { // Work out what rotation period the timestamp belongs to 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 edf073a4f6df64a53b1dceda71f6b06883b00f92..7320d60ce7b3aa5a1ec57c01f03fff81e2d0dd9f 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 @@ -113,6 +113,21 @@ public class KeyManagerImplTest extends BrambleTestCase { context.assertIsSatisfied(); } + @Test + public void testAddUnboundKeys() throws Exception { + SecretKey secretKey = getSecretKey(); + long timestamp = System.currentTimeMillis(); + boolean alice = new Random().nextBoolean(); + + context.checking(new Expectations() {{ + oneOf(transportKeyManager).addUnboundKeys(txn, secretKey, + timestamp, alice); + }}); + + keyManager.addUnboundKeys(txn, secretKey, timestamp, alice); + context.assertIsSatisfied(); + } + @Test public void testGetStreamContextForInactiveContact() throws Exception { assertEquals(null, 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 213400f1e6096181c5c18a8fdc3bd437d64a4c6e..b926707df4effe116569cfd02e5d948ed4d87c24 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 @@ -30,7 +30,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; @@ -57,6 +56,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { private final ContactId contactId = new ContactId(123); private final ContactId contactId1 = new ContactId(234); private final KeySetId keySetId = new KeySetId(345); + private final KeySetId keySetId1 = new KeySetId(456); + private final KeySetId keySetId2 = new KeySetId(567); private final SecretKey tagKey = TestUtils.getSecretKey(); private final SecretKey headerKey = TestUtils.getSecretKey(); private final SecretKey masterKey = TestUtils.getSecretKey(); @@ -66,11 +67,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { public void testKeysAreRotatedAtStartup() throws Exception { TransportKeys shouldRotate = createTransportKeys(900, 0); TransportKeys shouldNotRotate = createTransportKeys(1000, 0); + TransportKeys shouldRotate1 = createTransportKeys(999, 0); Collection<KeySet> loaded = asList( new KeySet(keySetId, contactId, shouldRotate), - new KeySet(keySetId, contactId1, shouldNotRotate) + new KeySet(keySetId1, contactId1, shouldNotRotate), + new KeySet(keySetId2, null, shouldRotate1) ); TransportKeys rotated = createTransportKeys(1000, 0); + TransportKeys rotated1 = createTransportKeys(1000, 0); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ @@ -85,6 +89,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { will(returnValue(rotated)); oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000); will(returnValue(shouldNotRotate)); + oneOf(transportCrypto).rotateTransportKeys(shouldRotate1, 1000); + will(returnValue(rotated1)); // Encode the tags (3 sets per contact) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(6).of(transportCrypto).encodeTag( @@ -93,8 +99,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { will(new EncodeTagAction()); } // Save the keys that were rotated - oneOf(db).updateTransportKeys(txn, - singletonList(new KeySet(keySetId, contactId, rotated))); + oneOf(db).updateTransportKeys(txn, asList( + new KeySet(keySetId, contactId, rotated), + new KeySet(keySetId2, null, rotated1)) + ); // Schedule key rotation at the start of the next rotation period oneOf(scheduler).schedule(with(any(Runnable.class)), with(rotationPeriodLength - 1), with(MILLISECONDS)); @@ -144,6 +152,36 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { alice); } + @Test + public void testKeysAreRotatedWhenAddingUnboundKeys() throws Exception { + 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(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(transportCrypto).rotateTransportKeys(transportKeys, 1000); + will(returnValue(rotated)); + // Save the keys + oneOf(db).addTransportKeys(txn, null, rotated); + will(returnValue(keySetId)); + }}); + + 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.addUnboundKeys(txn, masterKey, timestamp, alice); + } + @Test public void testOutgoingStreamContextIsNullIfContactIsNotFound() throws Exception { @@ -353,9 +391,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { @Test public void testKeysAreRotatedToCurrentPeriod() throws Exception { TransportKeys transportKeys = createTransportKeys(1000, 0); - Collection<KeySet> loaded = - singletonList(new KeySet(keySetId, contactId, transportKeys)); + TransportKeys transportKeys1 = createTransportKeys(1000, 0); + Collection<KeySet> loaded = asList( + new KeySet(keySetId, contactId, transportKeys), + new KeySet(keySetId1, null, transportKeys1) + ); TransportKeys rotated = createTransportKeys(1001, 0); + TransportKeys rotated1 = createTransportKeys(1001, 0); Transaction txn = new Transaction(null, false); Transaction txn1 = new Transaction(null, false); @@ -369,6 +411,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { // Rotate the transport keys (the keys are unaffected) oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); + oneOf(transportCrypto).rotateTransportKeys(transportKeys1, 1000); + will(returnValue(transportKeys1)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(3).of(transportCrypto).encodeTag( @@ -392,6 +436,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { oneOf(transportCrypto).rotateTransportKeys( with(any(TransportKeys.class)), with(1001L)); will(returnValue(rotated)); + oneOf(transportCrypto).rotateTransportKeys( + with(any(TransportKeys.class)), with(1001L)); + will(returnValue(rotated1)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(3).of(transportCrypto).encodeTag( @@ -400,8 +447,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { will(new EncodeTagAction()); } // Save the keys that were rotated - oneOf(db).updateTransportKeys(txn1, - singletonList(new KeySet(keySetId, contactId, rotated))); + oneOf(db).updateTransportKeys(txn1, asList( + new KeySet(keySetId, contactId, rotated), + new KeySet(keySetId1, null, rotated1) + )); // Schedule key rotation at the start of the next rotation period oneOf(scheduler).schedule(with(any(Runnable.class)), with(rotationPeriodLength), with(MILLISECONDS));