Skip to content
Snippets Groups Projects
Commit 1b8478df authored by akwizgran's avatar akwizgran
Browse files

Derive and store secrets when a contact transport is added.

parent 79a8d1c1
No related branches found
No related tags found
No related merge requests found
package net.sf.briar.api.crypto; package net.sf.briar.api.crypto;
import net.sf.briar.api.ContactId; 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.protocol.TransportId;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
public interface KeyManager { public interface KeyManager {
/** /**
* Starts the key manager and returns true if the manager started * Starts the key manager and returns true if it started successfully. This
* successfully. This method must be called after the database has been * method must be called after the database has been opened.
* opened.
*/ */
boolean start(); boolean start();
...@@ -22,4 +22,10 @@ public interface KeyManager { ...@@ -22,4 +22,10 @@ public interface KeyManager {
* support the transport. * support the transport.
*/ */
ConnectionContext getConnectionContext(ContactId c, TransportId t); 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);
} }
...@@ -30,10 +30,10 @@ public class TemporarySecret extends ContactTransport { ...@@ -30,10 +30,10 @@ public class TemporarySecret extends ContactTransport {
secret, 0L, 0L, new byte[CONNECTION_WINDOW_SIZE / 8]); secret, 0L, 0L, new byte[CONNECTION_WINDOW_SIZE / 8]);
} }
/** Creates a temporary secret derived from the given temporary secret. */ /** Creates a temporary secret derived from the given contact transport. */
public TemporarySecret(TemporarySecret old, long period, byte[] secret) { public TemporarySecret(ContactTransport ct, long period, byte[] secret) {
this(old.getContactId(), old.getTransportId(), old.getEpoch(), this(ct.getContactId(), ct.getTransportId(), ct.getEpoch(),
old.getClockDifference(), old.getLatency(), old.getAlice(), ct.getClockDifference(), ct.getLatency(), ct.getAlice(),
period, secret); period, secret);
} }
......
package net.sf.briar.transport; package net.sf.briar.transport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
...@@ -13,6 +14,7 @@ import java.util.logging.Logger; ...@@ -13,6 +14,7 @@ import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.KeyManager; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.TemporarySecret; import net.sf.briar.api.db.TemporarySecret;
...@@ -26,8 +28,6 @@ import net.sf.briar.util.ByteUtils; ...@@ -26,8 +28,6 @@ import net.sf.briar.util.ByteUtils;
import com.google.inject.Inject; 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 { class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
private static final int MS_BETWEEN_CHECKS = 60 * 1000; private static final int MS_BETWEEN_CHECKS = 60 * 1000;
...@@ -94,9 +94,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -94,9 +94,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
Collection<TemporarySecret> secrets) { Collection<TemporarySecret> secrets) {
Collection<TemporarySecret> dead = new ArrayList<TemporarySecret>(); Collection<TemporarySecret> dead = new ArrayList<TemporarySecret>();
for(TemporarySecret s : secrets) { for(TemporarySecret s : secrets) {
ContactId c = s.getContactId(); ContactTransportKey k = new ContactTransportKey(s);
TransportId t = s.getTransportId();
ContactTransportKey k = new ContactTransportKey(c, t);
long rotationPeriod = getRotationPeriod(s); long rotationPeriod = getRotationPeriod(s);
long creationTime = getCreationTime(s); long creationTime = getCreationTime(s);
long activationTime = creationTime + s.getClockDifference(); long activationTime = creationTime + s.getClockDifference();
...@@ -128,9 +126,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -128,9 +126,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
Collection<TemporarySecret> dead) { Collection<TemporarySecret> dead) {
Collection<TemporarySecret> created = new ArrayList<TemporarySecret>(); Collection<TemporarySecret> created = new ArrayList<TemporarySecret>();
for(TemporarySecret s : dead) { for(TemporarySecret s : dead) {
ContactId c = s.getContactId(); ContactTransportKey k = new ContactTransportKey(s);
TransportId t = s.getTransportId();
ContactTransportKey k = new ContactTransportKey(c, t);
if(incomingNew.containsKey(k)) throw new IllegalStateException(); if(incomingNew.containsKey(k)) throw new IllegalStateException();
byte[] secret = s.getSecret(); byte[] secret = s.getSecret();
long period = s.getPeriod(); long period = s.getPeriod();
...@@ -153,8 +149,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -153,8 +149,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
// Derive the two current incoming secrets // Derive the two current incoming secrets
byte[] secret1, secret2; byte[] secret1, secret2;
secret1 = secret; secret1 = secret;
for(long l = period; l < currentPeriod; l++) { for(long p = period; p < currentPeriod; p++) {
byte[] temp = crypto.deriveNextSecret(secret1, l); byte[] temp = crypto.deriveNextSecret(secret1, p);
ByteUtils.erase(secret1); ByteUtils.erase(secret1);
secret1 = temp; secret1 = temp;
} }
...@@ -181,7 +177,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -181,7 +177,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
return created; return created;
} }
private long getRotationPeriod(TemporarySecret s) { private long getRotationPeriod(ContactTransport s) {
return 2 * s.getClockDifference() + s.getLatency(); return 2 * s.getClockDifference() + s.getLatency();
} }
...@@ -219,6 +215,49 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -219,6 +215,49 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
return new ConnectionContext(c, t, secret, connection, s.getAlice()); 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 @Override
public synchronized void run() { public synchronized void run() {
// Rebuild the maps because we may be running a whole period late // Rebuild the maps because we may be running a whole period late
...@@ -287,6 +326,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { ...@@ -287,6 +326,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
this.transportId = transportId; this.transportId = transportId;
} }
private ContactTransportKey(ContactTransport ct) {
this(ct.getContactId(), ct.getTransportId());
}
@Override @Override
public int hashCode() { public int hashCode() {
return contactId.hashCode() + transportId.hashCode(); return contactId.hashCode() + transportId.hashCode();
......
...@@ -14,8 +14,8 @@ import net.sf.briar.TestDatabaseModule; ...@@ -14,8 +14,8 @@ import net.sf.briar.TestDatabaseModule;
import net.sf.briar.TestUtils; import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.crypto.KeyManager; 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.DatabaseComponent;
import net.sf.briar.api.db.TemporarySecret;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessagesAddedEvent;
...@@ -57,7 +57,7 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { ...@@ -57,7 +57,7 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
private final File aliceDir = new File(testDir, "alice"); private final File aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob"); private final File bobDir = new File(testDir, "bob");
private final TransportId transportId; private final TransportId transportId;
private final byte[] secret; private final byte[] initialSecret;
private final long epoch; private final long epoch;
private Injector alice, bob; private Injector alice, bob;
...@@ -66,8 +66,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { ...@@ -66,8 +66,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
super(); super();
transportId = new TransportId(TestUtils.getRandomId()); transportId = new TransportId(TestUtils.getRandomId());
// Create matching secrets for Alice and Bob // Create matching secrets for Alice and Bob
secret = new byte[32]; initialSecret = new byte[32];
new Random().nextBytes(secret); new Random().nextBytes(initialSecret);
long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY; long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY;
epoch = System.currentTimeMillis() - 2 * rotationPeriod; epoch = System.currentTimeMillis() - 2 * rotationPeriod;
} }
...@@ -103,15 +103,15 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { ...@@ -103,15 +103,15 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
// Open Alice's database // Open Alice's database
DatabaseComponent db = alice.getInstance(DatabaseComponent.class); DatabaseComponent db = alice.getInstance(DatabaseComponent.class);
db.open(false); 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 // Start Alice's key manager
KeyManager km = alice.getInstance(KeyManager.class); KeyManager km = alice.getInstance(KeyManager.class);
km.start(); 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 // Send Bob a message
String subject = "Hello"; String subject = "Hello";
byte[] body = "Hi Bob!".getBytes("UTF-8"); byte[] body = "Hi Bob!".getBytes("UTF-8");
...@@ -147,18 +147,18 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { ...@@ -147,18 +147,18 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
// Open Bob's database // Open Bob's database
DatabaseComponent db = bob.getInstance(DatabaseComponent.class); DatabaseComponent db = bob.getInstance(DatabaseComponent.class);
db.open(false); 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 // Start Bob's key manager
KeyManager km = bob.getInstance(KeyManager.class); KeyManager km = bob.getInstance(KeyManager.class);
km.start(); 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 // Fake a transport update from Alice
TransportUpdate transportUpdate = new TransportUpdate() { TransportUpdate transportUpdate = new TransportUpdate() {
...@@ -216,7 +216,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { ...@@ -216,7 +216,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
private boolean messagesAdded = false; private boolean messagesAdded = false;
public void eventOccurred(DatabaseEvent e) { public void eventOccurred(DatabaseEvent e) {
if(e instanceof MessagesAddedEvent) messagesAdded = true; if(e instanceof MessagesAddedEvent)
messagesAdded = true;
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment