mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Derive and store secrets when a contact transport is added.
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
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 {
|
|||||||
* 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 {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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;
|
|||||||
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;
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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;
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
// 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 {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user