Add a method for checking whether we can send streams.

This commit is contained in:
akwizgran
2018-03-28 12:08:08 +01:00
parent 798b871cc9
commit f7c2f86499
5 changed files with 53 additions and 15 deletions

View File

@@ -57,6 +57,12 @@ public interface KeyManager {
void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys) void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException; throws DbException;
/**
* Returns true if we have keys that can be used for outgoing streams to
* the given contact over the given transport.
*/
boolean canSendOutgoingStreams(ContactId c, TransportId t);
/** /**
* 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

@@ -160,6 +160,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
} }
@Override
public boolean canSendOutgoingStreams(ContactId c, TransportId t) {
TransportKeyManager m = managers.get(t);
return m == null ? false : m.canSendOutgoingStreams(c);
}
@Override @Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {

View File

@@ -29,6 +29,8 @@ interface TransportKeyManager {
void removeContact(ContactId c); void removeContact(ContactId c);
boolean canSendOutgoingStreams(ContactId c);
@Nullable @Nullable
StreamContext getStreamContext(Transaction txn, ContactId c) StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException; throws DbException;

View File

@@ -278,6 +278,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
@Override
public boolean canSendOutgoingStreams(ContactId c) {
lock.lock();
try {
MutableKeySet ks = outContexts.get(c);
if (ks == null) return false;
MutableOutgoingKeys outKeys =
ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError();
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
} finally {
lock.unlock();
}
}
@Override @Override
public StreamContext getStreamContext(Transaction txn, ContactId c) public StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException { throws DbException {

View File

@@ -30,6 +30,7 @@ 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;
@@ -37,8 +38,10 @@ import static org.briarproject.bramble.api.transport.TransportConstants.REORDERI
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TransportKeyManagerImplTest extends BrambleMockTestCase { public class TransportKeyManagerImplTest extends BrambleMockTestCase {
@@ -112,6 +115,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
@@ -150,6 +154,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000 - 1; long timestamp = rotationPeriodLength * 1000 - 1;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
@@ -181,6 +186,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000 - 1; long timestamp = rotationPeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn, assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
masterKey, timestamp, alice)); masterKey, timestamp, alice));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
@@ -192,6 +198,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
@@ -212,6 +219,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
} }
@@ -238,6 +246,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
// The first request should return a stream context // The first request should return a stream context
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn, StreamContext ctx = transportKeyManager.getStreamContext(txn,
contactId); contactId);
assertNotNull(ctx); assertNotNull(ctx);
@@ -247,6 +256,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
assertEquals(headerKey, ctx.getHeaderKey()); assertEquals(headerKey, ctx.getHeaderKey());
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber()); assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
// The second request should return null, the counter is exhausted // The second request should return null, the counter is exhausted
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
} }
@@ -266,6 +276,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// The tag should not be recognised // The tag should not be recognised
assertNull(transportKeyManager.getStreamContext(txn, assertNull(transportKeyManager.getStreamContext(txn,
new byte[TAG_LENGTH])); new byte[TAG_LENGTH]));
@@ -316,6 +327,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous rotation period, stream number 0) // Use the first tag (previous rotation period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
byte[] tag = tags.get(0); byte[] tag = tags.get(0);
@@ -336,13 +348,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testKeysAreRotatedToCurrentPeriod() throws Exception { public void testKeysAreRotatedToCurrentPeriod() throws Exception {
TransportKeys transportKeys = createTransportKeys(1000, 0, true); TransportKeys transportKeys = createTransportKeys(1000, 0, true);
TransportKeys transportKeys1 = createTransportKeys(1000, 0, false); Collection<KeySet> loaded =
Collection<KeySet> loaded = asList( singletonList(new KeySet(keySetId, contactId, transportKeys));
new KeySet(keySetId, contactId, transportKeys),
new KeySet(keySetId1, null, transportKeys1)
);
TransportKeys rotated = createTransportKeys(1001, 0, true); TransportKeys rotated = createTransportKeys(1001, 0, true);
TransportKeys rotated1 = createTransportKeys(1001, 0, false);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Transaction txn1 = new Transaction(null, false); Transaction txn1 = new Transaction(null, false);
@@ -356,8 +364,6 @@ 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(
@@ -381,9 +387,6 @@ 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(
@@ -392,10 +395,8 @@ 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, asList( oneOf(db).updateTransportKeys(txn1,
new KeySet(keySetId1, null, rotated1), singletonList(new KeySet(keySetId, contactId, rotated)));
new KeySet(keySetId, contactId, rotated)
));
// 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));
@@ -408,6 +409,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
@@ -451,11 +453,16 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn, assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
masterKey, timestamp, alice)); masterKey, timestamp, alice));
// The keys are unbound so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
transportKeyManager.bindKeys(txn, contactId, keySetId); transportKeyManager.bindKeys(txn, contactId, keySetId);
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
transportKeyManager.activateKeys(txn, keySetId); transportKeyManager.activateKeys(txn, keySetId);
// The keys are active so a stream context should be returned // The keys are active so a stream context should be returned
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn, StreamContext ctx = transportKeyManager.getStreamContext(txn,
contactId); contactId);
assertNotNull(ctx); assertNotNull(ctx);
@@ -496,7 +503,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn, assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
masterKey, timestamp, alice)); masterKey, timestamp, alice));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
transportKeyManager.removeKeys(txn, keySetId); transportKeyManager.removeKeys(txn, keySetId);
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
} }
private void expectAddContactNoRotation(boolean alice, private void expectAddContactNoRotation(boolean alice,