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 50f7d8aa0..db906e649 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
@@ -22,8 +22,24 @@ public interface KeyManager {
/**
* Derives and stores a set of rotation mode transport keys for
- * communicating with the given contact over each transport and returns the
- * key set IDs.
+ * communicating with the given contact over the given transport and
+ * returns the key set ID, or null if the transport is not supported.
+ *
+ * {@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
+ */
+ @Nullable
+ KeySetId addRotationKeys(Transaction txn, ContactId c, TransportId t,
+ SecretKey rootKey, long timestamp, boolean alice,
+ boolean active) throws DbException;
+
+ /**
+ * Derives and stores a set of rotation mode transport keys for
+ * communicating with the given contact over each supported transport and
+ * returns the key set IDs.
*
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
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 58aeb7cea..8d6ce998c 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
@@ -51,7 +51,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final DatabaseComponent db;
private final Executor dbExecutor;
private final PluginConfig pluginConfig;
- private final TransportKeyManagerFactory transportKeyManagerFactory;
private final TransportCrypto transportCrypto;
private final ConcurrentHashMap managers;
@@ -61,34 +60,39 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
KeyManagerImpl(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor,
PluginConfig pluginConfig,
- TransportKeyManagerFactory transportKeyManagerFactory,
- TransportCrypto transportCrypto) {
+ TransportCrypto transportCrypto,
+ TransportKeyManagerFactory transportKeyManagerFactory) {
this.db = db;
this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig;
- this.transportKeyManagerFactory = transportKeyManagerFactory;
this.transportCrypto = transportCrypto;
managers = new ConcurrentHashMap<>();
+ for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {
+ TransportKeyManager m = transportKeyManagerFactory.
+ createTransportKeyManager(f.getId(), f.getMaxLatency());
+ managers.put(f.getId(), m);
+ }
+ for (DuplexPluginFactory f : pluginConfig.getDuplexFactories()) {
+ TransportKeyManager m = transportKeyManagerFactory.
+ createTransportKeyManager(f.getId(), f.getMaxLatency());
+ managers.put(f.getId(), m);
+ }
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
- Map transports = new HashMap<>();
- for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
- transports.put(f.getId(), f.getMaxLatency());
- for (DuplexPluginFactory f : pluginConfig.getDuplexFactories())
- transports.put(f.getId(), f.getMaxLatency());
try {
db.transaction(false, txn -> {
- for (Entry e : transports.entrySet())
- db.addTransport(txn, e.getKey(), e.getValue());
- for (Entry e : transports.entrySet()) {
- TransportKeyManager m = transportKeyManagerFactory
- .createTransportKeyManager(e.getKey(),
- e.getValue());
- managers.put(e.getKey(), m);
- m.start(txn);
+ for (SimplexPluginFactory f :
+ pluginConfig.getSimplexFactories()) {
+ db.addTransport(txn, f.getId(), f.getMaxLatency());
+ managers.get(f.getId()).start(txn);
+ }
+ for (DuplexPluginFactory f :
+ pluginConfig.getDuplexFactories()) {
+ db.addTransport(txn, f.getId(), f.getMaxLatency());
+ managers.get(f.getId()).start(txn);
}
});
} catch (DbException e) {
@@ -101,9 +105,17 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
}
@Override
- public Map addRotationKeys(
- Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
- boolean alice, boolean active) throws DbException {
+ public KeySetId addRotationKeys(Transaction txn, ContactId c,
+ TransportId t, SecretKey rootKey, long timestamp, boolean alice,
+ boolean active) throws DbException {
+ return withManager(t, m ->
+ m.addRotationKeys(txn, c, rootKey, timestamp, alice, active));
+ }
+
+ @Override
+ public Map addRotationKeys(Transaction txn,
+ ContactId c, SecretKey rootKey, long timestamp, boolean alice,
+ boolean active) throws DbException {
Map ids = new HashMap<>();
for (Entry e : managers.entrySet()) {
TransportId t = e.getKey();
@@ -137,7 +149,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto
- .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
+ .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);
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 142f06538..5936b89e1 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
@@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Random;
+import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
@@ -71,8 +72,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
private final SecretKey rootKey = getSecretKey();
private final Random random = new Random();
- private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
- pluginConfig, transportKeyManagerFactory, transportCrypto);
+ private KeyManagerImpl keyManager;
@Before
public void testStartService() throws Exception {
@@ -83,18 +83,25 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
singletonList(pluginFactory);
int maxLatency = 1337;
- context.checking(new DbExpectations() {{
- oneOf(pluginConfig).getSimplexFactories();
+ context.checking(new Expectations() {{
+ allowing(pluginConfig).getSimplexFactories();
will(returnValue(factories));
- oneOf(pluginFactory).getId();
+ allowing(pluginFactory).getId();
will(returnValue(transportId));
- oneOf(pluginFactory).getMaxLatency();
+ allowing(pluginFactory).getMaxLatency();
will(returnValue(maxLatency));
- oneOf(db).addTransport(txn, transportId, maxLatency);
+ allowing(pluginConfig).getDuplexFactories();
+ will(returnValue(emptyList()));
oneOf(transportKeyManagerFactory)
.createTransportKeyManager(transportId, maxLatency);
will(returnValue(transportKeyManager));
- oneOf(pluginConfig).getDuplexFactories();
+ }});
+
+ keyManager = new KeyManagerImpl(db, executor,
+ pluginConfig, transportCrypto, transportKeyManagerFactory);
+
+ context.checking(new DbExpectations() {{
+ oneOf(db).addTransport(txn, transportId, maxLatency);
oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(transportKeyManager).start(txn);
}});
@@ -235,4 +242,37 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
keyManager.eventOccurred(event);
executor.runUntilIdle();
}
+
+ @Test
+ public void testAddMultipleRotationKeySets() throws Exception {
+ long timestamp = System.currentTimeMillis();
+ boolean alice = random.nextBoolean();
+ boolean active = random.nextBoolean();
+
+ context.checking(new Expectations() {{
+ oneOf(transportKeyManager).addRotationKeys(txn, contactId,
+ rootKey, timestamp, alice, active);
+ will(returnValue(keySetId));
+ }});
+
+ assertEquals(singletonMap(transportId, keySetId),
+ keyManager.addRotationKeys(txn, contactId, rootKey, timestamp,
+ alice, active));
+ }
+
+ @Test
+ public void testAddSingleRotationKeySet() throws Exception {
+ long timestamp = System.currentTimeMillis();
+ boolean alice = random.nextBoolean();
+ boolean active = random.nextBoolean();
+
+ context.checking(new Expectations() {{
+ oneOf(transportKeyManager).addRotationKeys(txn, contactId,
+ rootKey, timestamp, alice, active);
+ will(returnValue(keySetId));
+ }});
+
+ assertEquals(keySetId, keyManager.addRotationKeys(txn, contactId,
+ transportId, rootKey, timestamp, alice, active));
+ }
}