Merge branch '1556-key-manager-methods-for-pending-contacts' into 'master'

Add key manager methods for pending contacts

Closes #1556

See merge request briar/briar!1089
This commit is contained in:
Torsten Grote
2019-05-15 19:57:32 +00:00
16 changed files with 495 additions and 105 deletions

View File

@@ -73,8 +73,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified);
will(returnValue(contactId));
oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
alice, active);
oneOf(keyManager).addContactWithRotationKeys(txn, contactId,
rootKey, timestamp, alice, active);
oneOf(db).getContact(txn, contactId);
will(returnValue(contact));
}});

View File

@@ -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();
@@ -83,41 +91,94 @@ 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();
boolean active = random.nextBoolean();
context.checking(new Expectations() {{
oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
timestamp, alice, active);
oneOf(transportKeyManager).addContactWithRotationKeys(txn,
contactId, secretKey, timestamp, alice, active);
will(returnValue(keySetId));
}});
Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
secretKey, timestamp, alice, active);
Map<TransportId, KeySetId> ids = keyManager.addContactWithRotationKeys(
txn, contactId, secretKey, timestamp, alice, active);
assertEquals(singletonMap(transportId, keySetId), ids);
}
@Test
public void testGetStreamContextForUnknownTransport() throws Exception {
public void testAddContactWithHandshakeModeKeys() throws Exception {
SecretKey secretKey = getSecretKey();
boolean alice = random.nextBoolean();
context.checking(new Expectations() {{
oneOf(transportKeyManager).addContactWithHandshakeKeys(
txn, contactId, secretKey, alice);
will(returnValue(keySetId));
}});
Map<TransportId, KeySetId> ids = keyManager.addContactWithHandshakeKeys(
txn, contactId, secretKey, alice);
assertEquals(singletonMap(transportId, keySetId), ids);
}
@Test
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<TransportId, KeySetId> 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 {
@@ -130,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));
}

View File

@@ -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();
@@ -70,14 +74,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 +116,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();
@@ -148,11 +156,83 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// 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));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, active));
assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId));
}
@Test
public void testHandshakeKeysAreDerivedWhenAddingContact()
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, contactId, transportKeys);
will(returnValue(keySetId));
}});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertEquals(keySetId, transportKeyManager.addContactWithHandshakeKeys(
txn, contactId, rootKey, alice));
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 {
@@ -165,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 {
@@ -181,8 +274,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, true));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, true));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
}
@@ -207,8 +300,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, true));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, true));
// The first request should return a stream context
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn,
@@ -239,8 +332,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, active));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, active));
assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId));
// The tag should not be recognised
@@ -292,8 +385,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, true));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous time period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
@@ -395,8 +488,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, false));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -461,8 +554,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency);
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
rootKey, timestamp, alice, false));
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
txn, contactId, rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -525,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<byte[]> tags;