diff --git a/briar-api/src/org/briarproject/api/transport/TransportKeyManager.java b/briar-api/src/org/briarproject/api/transport/TransportKeyManager.java new file mode 100644 index 0000000000000000000000000000000000000000..dc3e22f9f9eea1fdb0f84513ceffc7a84e668561 --- /dev/null +++ b/briar-api/src/org/briarproject/api/transport/TransportKeyManager.java @@ -0,0 +1,23 @@ +package org.briarproject.api.transport; + +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; + +public interface TransportKeyManager { + + void start(Transaction txn) throws DbException; + + void addContact(Transaction txn, ContactId c, SecretKey master, + long timestamp, boolean alice) throws DbException; + + void removeContact(ContactId c); + + StreamContext getStreamContext(Transaction txn, ContactId c) + throws DbException; + + StreamContext getStreamContext(Transaction txn, byte[] tag) + throws DbException; + +} diff --git a/briar-api/src/org/briarproject/api/transport/TransportKeyManagerFactory.java b/briar-api/src/org/briarproject/api/transport/TransportKeyManagerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1b2f3a29b5c08ca548ccf5a5a6f8ea7b3c1b5ae8 --- /dev/null +++ b/briar-api/src/org/briarproject/api/transport/TransportKeyManagerFactory.java @@ -0,0 +1,10 @@ +package org.briarproject.api.transport; + +import org.briarproject.api.TransportId; + +public interface TransportKeyManagerFactory { + + TransportKeyManager createTransportKeyManager(TransportId transportId, + long maxLatency); + +} diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java index 7d75fd3b3498b894e50fb0899e0a63f3045f18c5..e372cb928d29ba13898e2404daa010857d774c46 100644 --- a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java +++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java @@ -3,7 +3,6 @@ package org.briarproject.transport; import org.briarproject.api.TransportId; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; -import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseExecutor; @@ -18,16 +17,16 @@ import org.briarproject.api.lifecycle.ServiceException; import org.briarproject.api.plugins.PluginConfig; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; -import org.briarproject.api.system.Clock; import org.briarproject.api.transport.KeyManager; import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TransportKeyManager; +import org.briarproject.api.transport.TransportKeyManagerFactory; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; @@ -41,26 +40,21 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { Logger.getLogger(KeyManagerImpl.class.getName()); private final DatabaseComponent db; - private final CryptoComponent crypto; private final Executor dbExecutor; - private final ScheduledExecutorService scheduler; private final PluginConfig pluginConfig; - private final Clock clock; + private final TransportKeyManagerFactory transportKeyManagerFactory; private final Map<ContactId, Boolean> activeContacts; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final AtomicBoolean used = new AtomicBoolean(false); @Inject - KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, - @DatabaseExecutor Executor dbExecutor, - ScheduledExecutorService scheduler, PluginConfig pluginConfig, - Clock clock) { + KeyManagerImpl(DatabaseComponent db, @DatabaseExecutor Executor dbExecutor, + PluginConfig pluginConfig, + TransportKeyManagerFactory transportKeyManagerFactory) { this.db = db; - this.crypto = crypto; this.dbExecutor = dbExecutor; - this.scheduler = scheduler; this.pluginConfig = pluginConfig; - this.clock = clock; + this.transportKeyManagerFactory = transportKeyManagerFactory; // Use a ConcurrentHashMap as a thread-safe set activeContacts = new ConcurrentHashMap<ContactId, Boolean>(); managers = new ConcurrentHashMap<TransportId, TransportKeyManager>(); @@ -83,9 +77,9 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { for (Entry<TransportId, Integer> e : transports.entrySet()) db.addTransport(txn, e.getKey(), e.getValue()); for (Entry<TransportId, Integer> e : transports.entrySet()) { - TransportKeyManager m = new TransportKeyManager(db, crypto, - dbExecutor, scheduler, clock, e.getKey(), - e.getValue()); + TransportKeyManager m = transportKeyManagerFactory + .createTransportKeyManager(e.getKey(), + e.getValue()); managers.put(e.getKey(), m); m.start(txn); } diff --git a/briar-core/src/org/briarproject/transport/TransportKeyManagerFactoryImpl.java b/briar-core/src/org/briarproject/transport/TransportKeyManagerFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4efe408560df30f3736d1c5b90eec90d63f888d2 --- /dev/null +++ b/briar-core/src/org/briarproject/transport/TransportKeyManagerFactoryImpl.java @@ -0,0 +1,43 @@ +package org.briarproject.transport; + +import org.briarproject.api.TransportId; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.system.Clock; +import org.briarproject.api.transport.TransportKeyManager; +import org.briarproject.api.transport.TransportKeyManagerFactory; + +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; + +import javax.inject.Inject; + +public class TransportKeyManagerFactoryImpl implements + TransportKeyManagerFactory { + + private final DatabaseComponent db; + private final CryptoComponent crypto; + private final Executor dbExecutor; + private final ScheduledExecutorService scheduler; + private final Clock clock; + + @Inject + TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto, + @DatabaseExecutor Executor dbExecutor, + ScheduledExecutorService scheduler, Clock clock) { + this.db = db; + this.crypto = crypto; + this.dbExecutor = dbExecutor; + this.scheduler = scheduler; + this.clock = clock; + } + + @Override + public TransportKeyManager createTransportKeyManager( + TransportId transportId, long maxLatency) { + return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, + clock, transportId, maxLatency); + } + +} diff --git a/briar-core/src/org/briarproject/transport/TransportKeyManager.java b/briar-core/src/org/briarproject/transport/TransportKeyManagerImpl.java similarity index 94% rename from briar-core/src/org/briarproject/transport/TransportKeyManager.java rename to briar-core/src/org/briarproject/transport/TransportKeyManagerImpl.java index 4210c31ed4a961b0694587435609e85635450ee3..4565849d85af115cd3f06b4ffc75425318e782ef 100644 --- a/briar-core/src/org/briarproject/transport/TransportKeyManager.java +++ b/briar-core/src/org/briarproject/transport/TransportKeyManagerImpl.java @@ -10,6 +10,7 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.db.Transaction; import org.briarproject.api.system.Clock; import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TransportKeyManager; import org.briarproject.api.transport.TransportKeys; import org.briarproject.transport.ReorderingWindow.Change; @@ -28,10 +29,10 @@ import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFER import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; -class TransportKeyManager { +class TransportKeyManagerImpl implements TransportKeyManager { private static final Logger LOG = - Logger.getLogger(TransportKeyManager.class.getName()); + Logger.getLogger(TransportKeyManagerImpl.class.getName()); private final DatabaseComponent db; private final CryptoComponent crypto; @@ -47,7 +48,7 @@ class TransportKeyManager { private final Map<ContactId, MutableOutgoingKeys> outContexts; private final Map<ContactId, MutableTransportKeys> keys; - TransportKeyManager(DatabaseComponent db, CryptoComponent crypto, + TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, Executor dbExecutor, ScheduledExecutorService scheduler, Clock clock, TransportId transportId, long maxLatency) { this.db = db; @@ -63,7 +64,7 @@ class TransportKeyManager { keys = new HashMap<ContactId, MutableTransportKeys>(); } - void start(Transaction txn) throws DbException { + public void start(Transaction txn) throws DbException { long now = clock.currentTimeMillis(); lock.lock(); try { @@ -155,7 +156,7 @@ class TransportKeyManager { }); } - void addContact(Transaction txn, ContactId c, SecretKey master, + public void addContact(Transaction txn, ContactId c, SecretKey master, long timestamp, boolean alice) throws DbException { lock.lock(); try { @@ -176,7 +177,7 @@ class TransportKeyManager { } } - void removeContact(ContactId c) { + public void removeContact(ContactId c) { lock.lock(); try { // Remove mutable state for the contact @@ -191,7 +192,7 @@ class TransportKeyManager { } } - StreamContext getStreamContext(Transaction txn, ContactId c) + public StreamContext getStreamContext(Transaction txn, ContactId c) throws DbException { lock.lock(); try { @@ -213,7 +214,7 @@ class TransportKeyManager { } } - StreamContext getStreamContext(Transaction txn, byte[] tag) + public StreamContext getStreamContext(Transaction txn, byte[] tag) throws DbException { lock.lock(); try { diff --git a/briar-core/src/org/briarproject/transport/TransportModule.java b/briar-core/src/org/briarproject/transport/TransportModule.java index 4c614ddc69d7505dd2e19879eda73942e96f7fb9..c66865bdbd6bb3b37b189d69b9f13d9edc05451f 100644 --- a/briar-core/src/org/briarproject/transport/TransportModule.java +++ b/briar-core/src/org/briarproject/transport/TransportModule.java @@ -7,6 +7,7 @@ import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.transport.KeyManager; import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamWriterFactory; +import org.briarproject.api.transport.TransportKeyManagerFactory; import javax.inject.Inject; import javax.inject.Singleton; @@ -34,6 +35,12 @@ public class TransportModule { return new StreamWriterFactoryImpl(streamEncrypterFactory); } + @Provides + TransportKeyManagerFactory provideTransportKeyManagerFactory( + TransportKeyManagerFactoryImpl transportKeyManagerFactory) { + return transportKeyManagerFactory; + } + @Provides @Singleton KeyManager provideKeyManager(LifecycleManager lifecycleManager, diff --git a/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java b/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java index d55771fcfd00d09f4e5ae479ce022947d80af830..d7ff0f0ceab76d5c602f6272a21c9eeb4414b957 100644 --- a/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java +++ b/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java @@ -1,14 +1,192 @@ package org.briarproject.transport; import org.briarproject.BriarTestCase; +import org.briarproject.api.TransportId; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.event.ContactRemovedEvent; +import org.briarproject.api.event.ContactStatusChangedEvent; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorId; +import org.briarproject.api.plugins.PluginConfig; +import org.briarproject.api.plugins.simplex.SimplexPluginFactory; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TransportKeyManager; +import org.briarproject.api.transport.TransportKeyManagerFactory; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.lib.concurrent.DeterministicExecutor; +import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.fail; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import static org.briarproject.TestUtils.getRandomBytes; +import static org.briarproject.TestUtils.getRandomId; +import static org.briarproject.TestUtils.getSecretKey; +import static org.junit.Assert.assertEquals; public class KeyManagerImplTest extends BriarTestCase { + private final Mockery context = new Mockery(); + private final KeyManagerImpl keyManager; + private final DatabaseComponent db = context.mock(DatabaseComponent.class); + private final PluginConfig pluginConfig = context.mock(PluginConfig.class); + private final TransportKeyManagerFactory transportKeyManagerFactory = + context.mock(TransportKeyManagerFactory.class); + private final TransportKeyManager transportKeyManager = + context.mock(TransportKeyManager.class); + private final DeterministicExecutor executor = new DeterministicExecutor(); + private final Transaction txn = new Transaction(null, false); + private final ContactId contactId = new ContactId(42); + private final ContactId inactiveContactId = new ContactId(43); + private final TransportId transportId = new TransportId("tId"); + private final StreamContext streamContext = + new StreamContext(contactId, transportId, getSecretKey(), + getSecretKey(), 1); + private final byte[] tag = getRandomBytes(42); + + public KeyManagerImplTest() { + keyManager = new KeyManagerImpl(db, executor, pluginConfig, + transportKeyManagerFactory); + } + + @Before + public void testStartService() throws Exception { + final Transaction txn = new Transaction(null, false); + AuthorId remoteAuthorId = new AuthorId(getRandomId()); + Author remoteAuthor = new Author(remoteAuthorId, "author", + getRandomBytes(42)); + AuthorId localAuthorId = new AuthorId(getRandomId()); + final Collection<Contact> contacts = new ArrayList<>(); + contacts.add(new Contact(contactId, remoteAuthor, localAuthorId, true, + true)); + contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId, + true, false)); + final SimplexPluginFactory pluginFactory = + context.mock(SimplexPluginFactory.class); + final Collection<SimplexPluginFactory> factories = Collections + .singletonList(pluginFactory); + final int maxLatency = 1337; + + context.checking(new Expectations() {{ + oneOf(pluginConfig).getSimplexFactories(); + will(returnValue(factories)); + oneOf(pluginFactory).getId(); + will(returnValue(transportId)); + oneOf(pluginFactory).getMaxLatency(); + will(returnValue(maxLatency)); + oneOf(db).addTransport(txn, transportId, maxLatency); + oneOf(transportKeyManagerFactory) + .createTransportKeyManager(transportId, maxLatency); + will(returnValue(transportKeyManager)); + oneOf(pluginConfig).getDuplexFactories(); + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(db).getContacts(txn); + will(returnValue(contacts)); + oneOf(transportKeyManager).start(txn); + oneOf(db).commitTransaction(txn); + oneOf(db).endTransaction(txn); + }}); + + keyManager.startService(); + } + @Test - public void testUnitTestsExist() { - fail(); // FIXME: Write tests + public void testAddContact() throws Exception { + final SecretKey secretKey = getSecretKey(); + final long timestamp = 42L; + final boolean alice = true; + + context.checking(new Expectations() {{ + oneOf(transportKeyManager) + .addContact(txn, contactId, secretKey, timestamp, alice); + }}); + + keyManager.addContact(txn, contactId, secretKey, timestamp, alice); + context.assertIsSatisfied(); + } + + @Test + public void testGetStreamContextForContact() throws Exception { + assertEquals(null, + keyManager.getStreamContext(inactiveContactId, transportId)); + assertEquals(null, keyManager + .getStreamContext(inactiveContactId, new TransportId("id"))); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(transportKeyManager).getStreamContext(txn, contactId); + will(returnValue(streamContext)); + oneOf(db).commitTransaction(txn); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(streamContext, + keyManager.getStreamContext(contactId, transportId)); + context.assertIsSatisfied(); } + + @Test + public void testGetStreamContextForTag() throws Exception { + assertEquals(null, keyManager + .getStreamContext(new TransportId("id"), tag)); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(transportKeyManager).getStreamContext(txn, tag); + will(returnValue(streamContext)); + oneOf(db).commitTransaction(txn); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(streamContext, + keyManager.getStreamContext(transportId, tag)); + context.assertIsSatisfied(); + } + + @Test + public void testContactRemovedEvent() throws Exception { + ContactRemovedEvent event = new ContactRemovedEvent(contactId); + + context.checking(new Expectations() {{ + oneOf(transportKeyManager).removeContact(contactId); + }}); + + keyManager.eventOccurred(event); + executor.runUntilIdle(); + assertEquals(null, keyManager.getStreamContext(contactId, transportId)); + + context.assertIsSatisfied(); + } + + @Test + public void testContactStatusChangedEvent() throws Exception { + ContactStatusChangedEvent event = + new ContactStatusChangedEvent(inactiveContactId, true); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(transportKeyManager).getStreamContext(txn, inactiveContactId); + will(returnValue(streamContext)); + oneOf(db).commitTransaction(txn); + oneOf(db).endTransaction(txn); + }}); + + keyManager.eventOccurred(event); + assertEquals(streamContext, + keyManager.getStreamContext(inactiveContactId, transportId)); + + context.assertIsSatisfied(); + } + } diff --git a/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java b/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java index b97d1786c3ea0f2d47b76c405626be4cdddb527d..d1bc128f6971c15f32b285937a43608531fa5e40 100644 --- a/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java +++ b/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java @@ -13,6 +13,7 @@ import org.briarproject.api.system.Clock; import org.briarproject.api.transport.IncomingKeys; import org.briarproject.api.transport.OutgoingKeys; import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TransportKeyManager; import org.briarproject.api.transport.TransportKeys; import org.hamcrest.Description; import org.jmock.Expectations; @@ -96,7 +97,8 @@ public class TransportKeyManagerTest extends BriarTestCase { with(rotationPeriodLength - 1), with(MILLISECONDS)); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); transportKeyManager.start(txn); @@ -138,7 +140,8 @@ public class TransportKeyManagerTest extends BriarTestCase { oneOf(db).addTransportKeys(txn, contactId, rotated); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); // The timestamp is 1 ms before the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000 - 1; @@ -161,7 +164,8 @@ public class TransportKeyManagerTest extends BriarTestCase { final Transaction txn = new Transaction(null, false); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); assertNull(transportKeyManager.getStreamContext(txn, contactId)); @@ -205,7 +209,8 @@ public class TransportKeyManagerTest extends BriarTestCase { oneOf(db).addTransportKeys(txn, contactId, transportKeys); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; @@ -254,7 +259,8 @@ public class TransportKeyManagerTest extends BriarTestCase { oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; @@ -310,7 +316,8 @@ public class TransportKeyManagerTest extends BriarTestCase { oneOf(db).addTransportKeys(txn, contactId, transportKeys); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; @@ -365,7 +372,8 @@ public class TransportKeyManagerTest extends BriarTestCase { 1, new byte[REORDERING_WINDOW_SIZE / 8]); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); // The timestamp is at the start of rotation period 1000 long timestamp = rotationPeriodLength * 1000; @@ -456,7 +464,8 @@ public class TransportKeyManagerTest extends BriarTestCase { oneOf(db).endTransaction(txn1); }}); - TransportKeyManager transportKeyManager = new TransportKeyManager(db, + TransportKeyManager + transportKeyManager = new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler, clock, transportId, maxLatency); transportKeyManager.start(txn);