Add methods for adding unbound keys.

This commit is contained in:
akwizgran
2018-03-27 15:47:01 +01:00
parent 78f2d48bc4
commit bb2f94d5eb
7 changed files with 104 additions and 10 deletions

View File

@@ -16,13 +16,21 @@ public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * 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 * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
*/ */
void addContact(Transaction txn, ContactId c, SecretKey master, void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException; 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 * 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 over the given transport, or null if an error occurs or the

View File

@@ -50,7 +50,7 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, 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 { boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice); keyManager.addContact(txn, c, master, timestamp, alice);

View File

@@ -104,6 +104,13 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
m.addContact(txn, c, master, timestamp, alice); 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 @Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {

View File

@@ -17,6 +17,9 @@ interface TransportKeyManager {
void addContact(Transaction txn, ContactId c, SecretKey master, void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException; long timestamp, boolean alice) throws DbException;
void addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
boolean alice) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);
@Nullable @Nullable

View File

@@ -172,6 +172,18 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override @Override
public void addContact(Transaction txn, ContactId c, SecretKey master, public void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException { 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(); lock.lock();
try { try {
// Work out what rotation period the timestamp belongs to // Work out what rotation period the timestamp belongs to

View File

@@ -113,6 +113,21 @@ public class KeyManagerImplTest extends BrambleTestCase {
context.assertIsSatisfied(); 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 @Test
public void testGetStreamContextForInactiveContact() throws Exception { public void testGetStreamContextForInactiveContact() throws Exception {
assertEquals(null, assertEquals(null,

View File

@@ -30,7 +30,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS; 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.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; 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 contactId = new ContactId(123);
private final ContactId contactId1 = new ContactId(234); private final ContactId contactId1 = new ContactId(234);
private final KeySetId keySetId = new KeySetId(345); 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 tagKey = TestUtils.getSecretKey();
private final SecretKey headerKey = TestUtils.getSecretKey(); private final SecretKey headerKey = TestUtils.getSecretKey();
private final SecretKey masterKey = TestUtils.getSecretKey(); private final SecretKey masterKey = TestUtils.getSecretKey();
@@ -66,11 +67,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
public void testKeysAreRotatedAtStartup() throws Exception { public void testKeysAreRotatedAtStartup() throws Exception {
TransportKeys shouldRotate = createTransportKeys(900, 0); TransportKeys shouldRotate = createTransportKeys(900, 0);
TransportKeys shouldNotRotate = createTransportKeys(1000, 0); TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
TransportKeys shouldRotate1 = createTransportKeys(999, 0);
Collection<KeySet> loaded = asList( Collection<KeySet> loaded = asList(
new KeySet(keySetId, contactId, shouldRotate), 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 rotated = createTransportKeys(1000, 0);
TransportKeys rotated1 = createTransportKeys(1000, 0);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -85,6 +89,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(returnValue(rotated)); will(returnValue(rotated));
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000); oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
will(returnValue(shouldNotRotate)); will(returnValue(shouldNotRotate));
oneOf(transportCrypto).rotateTransportKeys(shouldRotate1, 1000);
will(returnValue(rotated1));
// Encode the tags (3 sets per contact) // Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(transportCrypto).encodeTag( exactly(6).of(transportCrypto).encodeTag(
@@ -93,8 +99,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn, oneOf(db).updateTransportKeys(txn, asList(
singletonList(new KeySet(keySetId, contactId, rotated))); new KeySet(keySetId, contactId, rotated),
new KeySet(keySetId2, null, rotated1))
);
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength - 1), with(MILLISECONDS)); with(rotationPeriodLength - 1), with(MILLISECONDS));
@@ -144,6 +152,36 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
alice); 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 @Test
public void testOutgoingStreamContextIsNullIfContactIsNotFound() public void testOutgoingStreamContextIsNullIfContactIsNotFound()
throws Exception { throws Exception {
@@ -353,9 +391,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testKeysAreRotatedToCurrentPeriod() throws Exception { public void testKeysAreRotatedToCurrentPeriod() throws Exception {
TransportKeys transportKeys = createTransportKeys(1000, 0); TransportKeys transportKeys = createTransportKeys(1000, 0);
Collection<KeySet> loaded = TransportKeys transportKeys1 = createTransportKeys(1000, 0);
singletonList(new KeySet(keySetId, contactId, transportKeys)); Collection<KeySet> loaded = asList(
new KeySet(keySetId, contactId, transportKeys),
new KeySet(keySetId1, null, transportKeys1)
);
TransportKeys rotated = createTransportKeys(1001, 0); TransportKeys rotated = createTransportKeys(1001, 0);
TransportKeys rotated1 = createTransportKeys(1001, 0);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Transaction txn1 = 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) // Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
oneOf(transportCrypto).rotateTransportKeys(transportKeys1, 1000);
will(returnValue(transportKeys1));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -392,6 +436,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
oneOf(transportCrypto).rotateTransportKeys( oneOf(transportCrypto).rotateTransportKeys(
with(any(TransportKeys.class)), with(1001L)); with(any(TransportKeys.class)), with(1001L));
will(returnValue(rotated)); will(returnValue(rotated));
oneOf(transportCrypto).rotateTransportKeys(
with(any(TransportKeys.class)), with(1001L));
will(returnValue(rotated1));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -400,8 +447,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn1, oneOf(db).updateTransportKeys(txn1, asList(
singletonList(new KeySet(keySetId, contactId, rotated))); new KeySet(keySetId, contactId, rotated),
new KeySet(keySetId1, null, rotated1)
));
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength), with(MILLISECONDS)); with(rotationPeriodLength), with(MILLISECONDS));