Add key manager method for contacts with handshake keys.

This commit is contained in:
akwizgran
2019-05-09 12:10:07 +01:00
parent 84e2402404
commit f42fc5213e
6 changed files with 118 additions and 14 deletions

View File

@@ -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.
* <p/>
* {@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<TransportId, KeySetId> 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.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice True if the local party is ALice
*/
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
SecretKey rootKey, boolean alice) throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/

View File

@@ -100,6 +100,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ids;
}
@Override
public Map<TransportId, KeySetId> addContact(Transaction txn,
ContactId c, SecretKey rootKey, boolean alice) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> 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<TransportId, KeySetId> keys)
throws DbException {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
secretKey, alice);
assertEquals(singletonMap(transportId, keySetId), ids);
}
@Test
public void testGetStreamContextForUnknownTransport() throws Exception {
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));

View File

@@ -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<TransportKeySet> 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));
}