From dd50f4bcd4647f04d9bea7998d9201951e84ec13 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 9 May 2019 13:07:15 +0100 Subject: [PATCH] Add key manager methods for pending contacts. --- .../org/briarproject/bramble/api/Bytes.java | 3 +- .../bramble/api/contact/ContactId.java | 3 +- .../bramble/api/contact/PendingContactId.java | 1 - .../bramble/api/transport/KeyManager.java | 35 +++++- .../bramble/transport/KeyManagerImpl.java | 32 +++++ .../transport/TransportKeyManager.java | 12 ++ .../transport/TransportKeyManagerImpl.java | 112 +++++++++++++++--- .../bramble/transport/KeyManagerImplTest.java | 59 +++++++-- .../TransportKeyManagerImplTest.java | 70 ++++++++++- 9 files changed, 294 insertions(+), 33 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/Bytes.java b/bramble-api/src/main/java/org/briarproject/bramble/api/Bytes.java index 1a862bbd8..6da75fdff 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/Bytes.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/Bytes.java @@ -6,6 +6,7 @@ import org.briarproject.bramble.util.StringUtils; import java.util.Arrays; import java.util.Comparator; +import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; /** @@ -38,7 +39,7 @@ public class Bytes implements Comparable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactId.java index 21293599f..4a4f138e4 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactId.java @@ -2,6 +2,7 @@ package org.briarproject.bramble.api.contact; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -28,7 +29,7 @@ public class ContactId { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof ContactId && id == ((ContactId) o).id; } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactId.java index 76526018e..a0a7c7f40 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactId.java @@ -17,7 +17,6 @@ public class PendingContactId extends UniqueId { super(id); } - @Override public boolean equals(Object o) { return o instanceof PendingContactId && super.equals(o); 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 8a6e914b1..1651a135a 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 @@ -1,6 +1,7 @@ package org.briarproject.bramble.api.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; @@ -39,11 +40,26 @@ public interface KeyManager { * {@link StreamContext StreamContexts} for the contact can be created * after this method has returned. * - * @param alice True if the local party is ALice + * @param alice True if the local party is Alice */ Map addContact(Transaction txn, ContactId c, SecretKey rootKey, boolean alice) throws DbException; + /** + * Informs the key manager that a new pending contact has been added. + * Derives and stores a set of handshake mode transport keys for + * communicating with the pending contact over each transport and returns + * the key set IDs. + *

+ * {@link StreamContext StreamContexts} for the pending contact can be + * created after this method has returned. + * + * @param alice True if the local party is Alice + */ + Map addPendingContact(Transaction txn, + PendingContactId p, SecretKey rootKey, boolean alice) + throws DbException; + /** * Marks the given transport keys as usable for outgoing streams. */ @@ -56,15 +72,28 @@ public interface KeyManager { */ boolean canSendOutgoingStreams(ContactId c, TransportId t); + /** + * Returns true if we have keys that can be used for outgoing streams to + * the given pending contact over the given transport. + */ + boolean canSendOutgoingStreams(PendingContactId p, TransportId t); + /** * Returns a {@link StreamContext} for sending a stream to the given - * contact over the given transport, or null if an error occurs or the - * contact does not support the transport. + * contact over the given transport, or null if an error occurs. */ @Nullable StreamContext getStreamContext(ContactId c, TransportId t) throws DbException; + /** + * Returns a {@link StreamContext} for sending a stream to the given + * pending contact over the given transport, or null if an error occurs. + */ + @Nullable + StreamContext getStreamContext(PendingContactId p, TransportId t) + throws DbException; + /** * Looks up the given tag and returns a {@link StreamContext} for reading * from the corresponding stream, or null if an error occurs or the tag was 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 499e9dc22..036b9d2d9 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 @@ -1,6 +1,7 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseComponent; @@ -112,6 +113,19 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { return ids; } + @Override + public Map addPendingContact(Transaction txn, + PendingContactId p, 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.addPendingContact(txn, p, rootKey, alice)); + } + return ids; + } + @Override public void activateKeys(Transaction txn, Map keys) throws DbException { @@ -132,6 +146,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { return m != null && m.canSendOutgoingStreams(c); } + @Override + public boolean canSendOutgoingStreams(PendingContactId p, TransportId t) { + TransportKeyManager m = managers.get(t); + return m != null && m.canSendOutgoingStreams(p); + } + @Override public StreamContext getStreamContext(ContactId c, TransportId t) throws DbException { @@ -144,6 +164,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { m.getStreamContext(txn, c)); } + @Override + public StreamContext getStreamContext(PendingContactId p, TransportId t) + throws DbException { + TransportKeyManager m = managers.get(t); + if (m == null) { + if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t); + return null; + } + return db.transactionWithNullableResult(false, txn -> + m.getStreamContext(txn, p)); + } + @Override public StreamContext getStreamContext(TransportId t, byte[] tag) 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 d7758043f..bed597398 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 @@ -1,6 +1,7 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; @@ -21,16 +22,27 @@ interface TransportKeyManager { KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey, boolean alice) throws DbException; + KeySetId addPendingContact(Transaction txn, PendingContactId p, + SecretKey rootKey, boolean alice) throws DbException; + void activateKeys(Transaction txn, KeySetId k) throws DbException; void removeContact(ContactId c); + void removePendingContact(PendingContactId p); + boolean canSendOutgoingStreams(ContactId c); + boolean canSendOutgoingStreams(PendingContactId p); + @Nullable StreamContext getStreamContext(Transaction txn, ContactId c) throws DbException; + @Nullable + StreamContext getStreamContext(Transaction txn, PendingContactId p) + throws DbException; + @Nullable StreamContext getStreamContext(Transaction txn, byte[] tag) throws DbException; 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 86ce89e10..7488d168c 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 @@ -64,8 +64,11 @@ class TransportKeyManagerImpl implements TransportKeyManager { @GuardedBy("lock") private final Map inContexts = new HashMap<>(); @GuardedBy("lock") - private final Map outContexts = - new HashMap<>(); + private final Map + contactOutContexts = new HashMap<>(); + @GuardedBy("lock") + private final Map + pendingContactOutContexts = new HashMap<>(); TransportKeyManagerImpl(DatabaseComponent db, TransportCrypto transportCrypto, Executor dbExecutor, @@ -162,19 +165,29 @@ class TransportKeyManagerImpl implements TransportKeyManager { @GuardedBy("lock") private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) { - // Use the active outgoing keys with the highest key set ID - ContactId c = ks.getContactId(); - if (c != null && ks.getKeys().getCurrentOutgoingKeys().isActive()) { - MutableTransportKeySet old = outContexts.get(c); - if (old == null || - (old.getKeys().isHandshakeMode() && - !ks.getKeys().isHandshakeMode()) || + // Use the active outgoing keys with the highest key set ID, preferring + // rotation keys to handshake keys + if (ks.getKeys().getCurrentOutgoingKeys().isActive()) { + MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(), + ks.getPendingContactId()); + if (old == null || (old.getKeys().isHandshakeMode() && + !ks.getKeys().isHandshakeMode()) || old.getKeySetId().getInt() < ks.getKeySetId().getInt()) { - outContexts.put(c, ks); + if (ks.getContactId() == null) + pendingContactOutContexts.put(ks.getPendingContactId(), ks); + else contactOutContexts.put(ks.getContactId(), ks); } } } + @GuardedBy("lock") + @Nullable + private MutableTransportKeySet getOutgoingKeySet(@Nullable ContactId c, + @Nullable PendingContactId p) { + if (c == null) return pendingContactOutContexts.get(p); + else return contactOutContexts.get(c); + } + private void scheduleKeyUpdate(long now) { long delay = timePeriodLength - now % timePeriodLength; scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS); @@ -233,6 +246,26 @@ class TransportKeyManagerImpl implements TransportKeyManager { } } + @Override + public KeySetId addPendingContact(Transaction txn, PendingContactId p, + 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, p, k); + // Initialise mutable state for the pending contact + addKeys(keySetId, null, p, new MutableTransportKeys(k)); + return keySetId; + } finally { + lock.unlock(); + } + } + @Override public void activateKeys(Transaction txn, KeySetId k) throws DbException { lock.lock(); @@ -254,8 +287,9 @@ class TransportKeyManagerImpl implements TransportKeyManager { try { // Remove mutable state for the contact Iterator it = inContexts.values().iterator(); - while (it.hasNext()) if (c.equals(it.next().contactId)) it.remove(); - outContexts.remove(c); + while (it.hasNext()) + if (c.equals(it.next().contactId)) it.remove(); + contactOutContexts.remove(c); Iterator it1 = keys.values().iterator(); while (it1.hasNext()) if (c.equals(it1.next().getContactId())) it1.remove(); @@ -265,13 +299,39 @@ class TransportKeyManagerImpl implements TransportKeyManager { } @Override - public boolean canSendOutgoingStreams(ContactId c) { + public void removePendingContact(PendingContactId p) { lock.lock(); try { - MutableTransportKeySet ks = outContexts.get(c); + // Remove mutable state for the pending contact + Iterator it = inContexts.values().iterator(); + while (it.hasNext()) + if (p.equals(it.next().pendingContactId)) it.remove(); + pendingContactOutContexts.remove(p); + Iterator it1 = keys.values().iterator(); + while (it1.hasNext()) + if (p.equals(it1.next().getPendingContactId())) it1.remove(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean canSendOutgoingStreams(ContactId c) { + return canSendOutgoingStreams(c, null); + } + + @Override + public boolean canSendOutgoingStreams(PendingContactId p) { + return canSendOutgoingStreams(null, p); + } + + private boolean canSendOutgoingStreams(@Nullable ContactId c, + @Nullable PendingContactId p) { + lock.lock(); + try { + MutableTransportKeySet ks = getOutgoingKeySet(c, p); if (ks == null) return false; - MutableOutgoingKeys outKeys = - ks.getKeys().getCurrentOutgoingKeys(); + MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys(); if (!outKeys.isActive()) throw new AssertionError(); return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED; } finally { @@ -282,17 +342,30 @@ class TransportKeyManagerImpl implements TransportKeyManager { @Override public StreamContext getStreamContext(Transaction txn, ContactId c) throws DbException { + return getStreamContext(txn, c, null); + } + + @Override + public StreamContext getStreamContext(Transaction txn, PendingContactId p) + throws DbException { + return getStreamContext(txn, null, p); + } + + @Nullable + private StreamContext getStreamContext(Transaction txn, + @Nullable ContactId c, @Nullable PendingContactId p) + throws DbException { lock.lock(); try { // Look up the outgoing keys for the contact - MutableTransportKeySet ks = outContexts.get(c); + MutableTransportKeySet ks = getOutgoingKeySet(c, p); if (ks == null) return null; MutableTransportKeys keys = ks.getKeys(); MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys(); if (!outKeys.isActive()) throw new AssertionError(); if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null; // Create a stream context - StreamContext ctx = new StreamContext(c, null, transportId, + StreamContext ctx = new StreamContext(c, p, transportId, outKeys.getTagKey(), outKeys.getHeaderKey(), outKeys.getStreamCounter(), keys.isHandshakeMode()); // Increment the stream counter and write it back to the DB @@ -373,7 +446,8 @@ class TransportKeyManagerImpl implements TransportKeyManager { UpdateResult updateResult = updateKeys(snapshot, now); // Rebuild the mutable state for all contacts inContexts.clear(); - outContexts.clear(); + contactOutContexts.clear(); + pendingContactOutContexts.clear(); keys.clear(); addKeys(updateResult.current); // Write any updated keys back to the DB 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 5f25ff777..7d212aff5 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 @@ -1,6 +1,7 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseComponent; @@ -26,6 +27,7 @@ import static java.util.Collections.singletonMap; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.junit.Assert.assertEquals; @@ -43,11 +45,17 @@ public class KeyManagerImplTest extends BrambleMockTestCase { private final DeterministicExecutor executor = new DeterministicExecutor(); private final Transaction txn = new Transaction(null, false); private final ContactId contactId = getContactId(); + private final PendingContactId pendingContactId = + new PendingContactId(getRandomId()); private final KeySetId keySetId = new KeySetId(345); private final TransportId transportId = getTransportId(); private final TransportId unknownTransportId = getTransportId(); - private final StreamContext streamContext = new StreamContext(contactId, - null, transportId, getSecretKey(), getSecretKey(), 1, false); + private final StreamContext contactStreamContext = + new StreamContext(contactId, null, transportId, getSecretKey(), + getSecretKey(), 1, false); + private final StreamContext pendingContactStreamContext = + new StreamContext(null, pendingContactId, transportId, + getSecretKey(), getSecretKey(), 1, true); private final byte[] tag = getRandomBytes(TAG_LENGTH); private final Random random = new Random(); @@ -117,23 +125,60 @@ public class KeyManagerImplTest extends BrambleMockTestCase { } @Test - public void testGetStreamContextForUnknownTransport() throws Exception { + public void testAddPendingContact() throws Exception { + SecretKey secretKey = getSecretKey(); + boolean alice = random.nextBoolean(); + + context.checking(new Expectations() {{ + oneOf(transportKeyManager).addPendingContact(txn, pendingContactId, + secretKey, alice); + will(returnValue(keySetId)); + }}); + + Map ids = keyManager.addPendingContact(txn, + pendingContactId, secretKey, alice); + assertEquals(singletonMap(transportId, keySetId), ids); + } + + @Test + public void testGetStreamContextForContactWithUnknownTransport() + throws Exception { assertNull(keyManager.getStreamContext(contactId, unknownTransportId)); } + @Test + public void testGetStreamContextForPendingContactWithUnknownTransport() + throws Exception { + assertNull(keyManager.getStreamContext(pendingContactId, + unknownTransportId)); + } + @Test public void testGetStreamContextForContact() throws Exception { context.checking(new DbExpectations() {{ oneOf(db).transactionWithNullableResult(with(false), withNullableDbCallable(txn)); oneOf(transportKeyManager).getStreamContext(txn, contactId); - will(returnValue(streamContext)); + will(returnValue(contactStreamContext)); }}); - assertEquals(streamContext, + assertEquals(contactStreamContext, keyManager.getStreamContext(contactId, transportId)); } + @Test + public void testGetStreamContextForPendingContact() throws Exception { + context.checking(new DbExpectations() {{ + oneOf(db).transactionWithNullableResult(with(false), + withNullableDbCallable(txn)); + oneOf(transportKeyManager).getStreamContext(txn, pendingContactId); + will(returnValue(pendingContactStreamContext)); + }}); + + assertEquals(pendingContactStreamContext, + keyManager.getStreamContext(pendingContactId, transportId)); + } + @Test public void testGetStreamContextForTagAndUnknownTransport() throws Exception { @@ -146,10 +191,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase { oneOf(db).transactionWithNullableResult(with(false), withNullableDbCallable(txn)); oneOf(transportKeyManager).getStreamContext(txn, tag); - will(returnValue(streamContext)); + will(returnValue(contactStreamContext)); }}); - assertEquals(streamContext, + assertEquals(contactStreamContext, keyManager.getStreamContext(transportId, tag)); } 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 188be95f2..57abdab52 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 @@ -1,6 +1,7 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.db.DatabaseComponent; @@ -37,6 +38,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.test.TestUtils.getContactId; +import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; @@ -61,6 +63,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; private final ContactId contactId = getContactId(); private final ContactId contactId1 = getContactId(); + private final PendingContactId pendingContactId = + new PendingContactId(getRandomId()); private final KeySetId keySetId = new KeySetId(345); private final KeySetId keySetId1 = new KeySetId(456); private final SecretKey tagKey = getSecretKey(); @@ -162,7 +166,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { public void testHandshakeKeysAreDerivedWhenAddingContact() throws Exception { boolean alice = random.nextBoolean(); - TransportKeys transportKeys = createTransportKeys(1000, 0, true); + TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ @@ -193,6 +197,42 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); } + @Test + public void testHandshakeKeysAreDerivedWhenAddingPendingContact() + throws Exception { + boolean alice = random.nextBoolean(); + TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice); + 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, pendingContactId, transportKeys); + will(returnValue(keySetId)); + }}); + + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); + assertEquals(keySetId, transportKeyManager.addPendingContact(txn, + pendingContactId, rootKey, alice)); + assertTrue(transportKeyManager.canSendOutgoingStreams( + pendingContactId)); + } + @Test public void testOutgoingStreamContextIsNullIfContactIsNotFound() throws Exception { @@ -205,6 +245,19 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); } + @Test + public void testOutgoingStreamContextIsNullIfPendingContactIsNotFound() + throws Exception { + Transaction txn = new Transaction(null, false); + + TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( + db, transportCrypto, dbExecutor, scheduler, clock, transportId, + maxLatency); + assertNull(transportKeyManager.getStreamContext(txn, pendingContactId)); + assertFalse(transportKeyManager.canSendOutgoingStreams( + pendingContactId)); + } + @Test public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted() throws Exception { @@ -565,6 +618,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); } + @SuppressWarnings("SameParameterValue") + private TransportKeys createHandshakeKeys(long timePeriod, + long streamCounter, boolean alice) { + IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey, + timePeriod - 1); + IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey, + timePeriod); + IncomingKeys inNext = new IncomingKeys(tagKey, headerKey, + timePeriod + 1); + OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, + timePeriod, streamCounter, true); + return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr, + rootKey, alice); + } + private class EncodeTagAction implements Action { private final Collection tags;