diff --git a/api/net/sf/briar/api/crypto/KeyManager.java b/api/net/sf/briar/api/crypto/KeyManager.java index ad406eceaed790d70bc4b7a9abb7ed2b8135f611..a5a841687ae2d1b129009d9c29ea22b69635ce3b 100644 --- a/api/net/sf/briar/api/crypto/KeyManager.java +++ b/api/net/sf/briar/api/crypto/KeyManager.java @@ -1,15 +1,15 @@ package net.sf.briar.api.crypto; import net.sf.briar.api.ContactId; +import net.sf.briar.api.db.ContactTransport; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.transport.ConnectionContext; public interface KeyManager { /** - * Starts the key manager and returns true if the manager started - * successfully. This method must be called after the database has been - * opened. + * Starts the key manager and returns true if it started successfully. This + * method must be called after the database has been opened. */ boolean start(); @@ -22,4 +22,10 @@ public interface KeyManager { * support the transport. */ ConnectionContext getConnectionContext(ContactId c, TransportId t); + + /** + * Called whenever a contact transport has been added. The initial secret + * is erased before returning. + */ + void contactTransportAdded(ContactTransport ct, byte[] initialSecret); } diff --git a/api/net/sf/briar/api/db/TemporarySecret.java b/api/net/sf/briar/api/db/TemporarySecret.java index db2aa95dc78a2bfd4c5693f1809f9ff52ebbd9cb..0289b07373ff4a1bdc3393b7280cfa323e717950 100644 --- a/api/net/sf/briar/api/db/TemporarySecret.java +++ b/api/net/sf/briar/api/db/TemporarySecret.java @@ -30,10 +30,10 @@ public class TemporarySecret extends ContactTransport { secret, 0L, 0L, new byte[CONNECTION_WINDOW_SIZE / 8]); } - /** Creates a temporary secret derived from the given temporary secret. */ - public TemporarySecret(TemporarySecret old, long period, byte[] secret) { - this(old.getContactId(), old.getTransportId(), old.getEpoch(), - old.getClockDifference(), old.getLatency(), old.getAlice(), + /** Creates a temporary secret derived from the given contact transport. */ + public TemporarySecret(ContactTransport ct, long period, byte[] secret) { + this(ct.getContactId(), ct.getTransportId(), ct.getEpoch(), + ct.getClockDifference(), ct.getLatency(), ct.getAlice(), period, secret); } diff --git a/components/net/sf/briar/transport/KeyManagerImpl.java b/components/net/sf/briar/transport/KeyManagerImpl.java index 97aa506f3c6badfe2d88e0e998b6618f31759a1c..c1cd7741d6dc6f80379ccfdb64609a4622f0da1d 100644 --- a/components/net/sf/briar/transport/KeyManagerImpl.java +++ b/components/net/sf/briar/transport/KeyManagerImpl.java @@ -1,6 +1,7 @@ package net.sf.briar.transport; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -13,6 +14,7 @@ import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.KeyManager; +import net.sf.briar.api.db.ContactTransport; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.TemporarySecret; @@ -26,8 +28,6 @@ import net.sf.briar.util.ByteUtils; import com.google.inject.Inject; -// FIXME: When a contact transport is added we need to load its secrets - class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { private static final int MS_BETWEEN_CHECKS = 60 * 1000; @@ -94,9 +94,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { Collection<TemporarySecret> secrets) { Collection<TemporarySecret> dead = new ArrayList<TemporarySecret>(); for(TemporarySecret s : secrets) { - ContactId c = s.getContactId(); - TransportId t = s.getTransportId(); - ContactTransportKey k = new ContactTransportKey(c, t); + ContactTransportKey k = new ContactTransportKey(s); long rotationPeriod = getRotationPeriod(s); long creationTime = getCreationTime(s); long activationTime = creationTime + s.getClockDifference(); @@ -128,9 +126,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { Collection<TemporarySecret> dead) { Collection<TemporarySecret> created = new ArrayList<TemporarySecret>(); for(TemporarySecret s : dead) { - ContactId c = s.getContactId(); - TransportId t = s.getTransportId(); - ContactTransportKey k = new ContactTransportKey(c, t); + ContactTransportKey k = new ContactTransportKey(s); if(incomingNew.containsKey(k)) throw new IllegalStateException(); byte[] secret = s.getSecret(); long period = s.getPeriod(); @@ -153,8 +149,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { // Derive the two current incoming secrets byte[] secret1, secret2; secret1 = secret; - for(long l = period; l < currentPeriod; l++) { - byte[] temp = crypto.deriveNextSecret(secret1, l); + for(long p = period; p < currentPeriod; p++) { + byte[] temp = crypto.deriveNextSecret(secret1, p); ByteUtils.erase(secret1); secret1 = temp; } @@ -181,7 +177,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { return created; } - private long getRotationPeriod(TemporarySecret s) { + private long getRotationPeriod(ContactTransport s) { return 2 * s.getClockDifference() + s.getLatency(); } @@ -219,6 +215,49 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { return new ConnectionContext(c, t, secret, connection, s.getAlice()); } + public synchronized void contactTransportAdded(ContactTransport ct, + byte[] initialSecret) { + long now = System.currentTimeMillis(); + long rotationPeriod = getRotationPeriod(ct); + long elapsed = now - ct.getEpoch(); + long currentPeriod = elapsed / rotationPeriod; + if(currentPeriod < 1) throw new IllegalArgumentException(); + // Derive the two current incoming secrets + byte[] secret1, secret2; + secret1 = initialSecret; + for(long p = 0; p < currentPeriod; p++) { + byte[] temp = crypto.deriveNextSecret(secret1, p); + ByteUtils.erase(secret1); + secret1 = temp; + } + secret2 = crypto.deriveNextSecret(secret1, currentPeriod); + // One of the incoming secrets is the current outgoing secret + ContactTransportKey k = new ContactTransportKey(ct); + TemporarySecret s1, s2; + s1 = new TemporarySecret(ct, currentPeriod - 1, secret1); + incomingOld.put(k, s1); + s2 = new TemporarySecret(ct, currentPeriod, secret2); + incomingNew.put(k, s2); + if(elapsed % rotationPeriod < ct.getClockDifference()) { + // The outgoing secret is the newer incoming secret + outgoing.put(k, s2); + } else { + // The outgoing secret is the older incoming secret + outgoing.put(k, s1); + } + // Store the new secrets + try { + db.addSecrets(Arrays.asList(s1, s2)); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + } + // Pass the new secrets to the recogniser + recogniser.addSecret(s1); + recogniser.addSecret(s2); + // Erase the initial secret + ByteUtils.erase(initialSecret); + } + @Override public synchronized void run() { // Rebuild the maps because we may be running a whole period late @@ -287,6 +326,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { this.transportId = transportId; } + private ContactTransportKey(ContactTransport ct) { + this(ct.getContactId(), ct.getTransportId()); + } + @Override public int hashCode() { return contactId.hashCode() + transportId.hashCode(); diff --git a/test/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java b/test/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java index 5caec2ff58dcda71df781c2020a25ecdfb4e5324..8448a0934a012221c49400f20d4cb4e4bd4fa2c1 100644 --- a/test/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java +++ b/test/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java @@ -14,8 +14,8 @@ import net.sf.briar.TestDatabaseModule; import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; import net.sf.briar.api.crypto.KeyManager; +import net.sf.briar.api.db.ContactTransport; import net.sf.briar.api.db.DatabaseComponent; -import net.sf.briar.api.db.TemporarySecret; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.MessagesAddedEvent; @@ -57,7 +57,7 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { private final File aliceDir = new File(testDir, "alice"); private final File bobDir = new File(testDir, "bob"); private final TransportId transportId; - private final byte[] secret; + private final byte[] initialSecret; private final long epoch; private Injector alice, bob; @@ -66,8 +66,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { super(); transportId = new TransportId(TestUtils.getRandomId()); // Create matching secrets for Alice and Bob - secret = new byte[32]; - new Random().nextBytes(secret); + initialSecret = new byte[32]; + new Random().nextBytes(initialSecret); long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY; epoch = System.currentTimeMillis() - 2 * rotationPeriod; } @@ -103,15 +103,15 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { // Open Alice's database DatabaseComponent db = alice.getInstance(DatabaseComponent.class); db.open(false); - // Add Bob as a contact - ContactId contactId = db.addContact(); - TemporarySecret s = new TemporarySecret(contactId, transportId, epoch, - CLOCK_DIFFERENCE, LATENCY, true, 0L, secret); - db.addContactTransport(s); - db.addSecrets(Collections.singletonList(s)); // Start Alice's key manager KeyManager km = alice.getInstance(KeyManager.class); km.start(); + // Add Bob as a contact + ContactId contactId = db.addContact(); + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, CLOCK_DIFFERENCE, LATENCY, true); + db.addContactTransport(ct); + km.contactTransportAdded(ct, initialSecret.clone()); // Send Bob a message String subject = "Hello"; byte[] body = "Hi Bob!".getBytes("UTF-8"); @@ -147,18 +147,18 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { // Open Bob's database DatabaseComponent db = bob.getInstance(DatabaseComponent.class); db.open(false); - // Set up a database listener - MessageListener listener = new MessageListener(); - db.addListener(listener); - // Add Alice as a contact - ContactId contactId = db.addContact(); - TemporarySecret s = new TemporarySecret(contactId, transportId, epoch, - CLOCK_DIFFERENCE, LATENCY, false, 0L, secret); - db.addContactTransport(s); - db.addSecrets(Collections.singletonList(s)); // Start Bob's key manager KeyManager km = bob.getInstance(KeyManager.class); km.start(); + // Add Alice as a contact + ContactId contactId = db.addContact(); + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, CLOCK_DIFFERENCE, LATENCY, false); + db.addContactTransport(ct); + km.contactTransportAdded(ct, initialSecret.clone()); + // Set up a database listener + MessageListener listener = new MessageListener(); + db.addListener(listener); // Fake a transport update from Alice TransportUpdate transportUpdate = new TransportUpdate() { @@ -216,7 +216,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { private boolean messagesAdded = false; public void eventOccurred(DatabaseEvent e) { - if(e instanceof MessagesAddedEvent) messagesAdded = true; + if(e instanceof MessagesAddedEvent) + messagesAdded = true; } } }