From 27e50b84953f7905433033cb94b9013daa3b812e Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Wed, 24 Oct 2012 18:16:17 +0100 Subject: [PATCH] Implemented KeyManager (untested). A test is failing due to key derivation errors - must be fixed! --- api/net/sf/briar/api/crypto/KeyManager.java | 14 +- .../sf/briar/api/db/DatabaseComponent.java | 7 +- api/net/sf/briar/api/db/TemporarySecret.java | 23 +- .../api/transport/ConnectionRecogniser.java | 10 +- .../net/sf/briar/crypto/KeyRotator.java | 24 -- .../net/sf/briar/crypto/KeyRotatorImpl.java | 41 --- components/net/sf/briar/db/Database.java | 4 +- .../sf/briar/db/DatabaseComponentImpl.java | 29 +- components/net/sf/briar/db/JdbcDatabase.java | 49 ++- .../transport/ConnectionRecogniserImpl.java | 24 +- .../ConnectionWriterFactoryImpl.java | 1 + .../briar/transport/ConnectionWriterImpl.java | 1 + .../sf/briar/transport/KeyManagerImpl.java | 305 ++++++++++++++++++ .../transport/OutgoingEncryptionLayer.java | 1 + .../TransportConnectionRecogniser.java | 29 +- .../sf/briar/transport/TransportModule.java | 6 + test/build.xml | 1 - .../net/sf/briar/ProtocolIntegrationTest.java | 2 +- .../sf/briar/crypto/KeyDerivationTest.java | 2 +- .../sf/briar/crypto/KeyRotatorImplTest.java | 54 ---- .../sf/briar/db/DatabaseComponentTest.java | 4 +- test/net/sf/briar/db/H2DatabaseTest.java | 200 ++++++------ .../OutgoingSimplexConnectionTest.java | 8 - .../transport/ConnectionWriterImplTest.java | 1 + .../transport/TransportIntegrationTest.java | 6 + 25 files changed, 540 insertions(+), 306 deletions(-) delete mode 100644 components/net/sf/briar/crypto/KeyRotator.java delete mode 100644 components/net/sf/briar/crypto/KeyRotatorImpl.java create mode 100644 components/net/sf/briar/transport/KeyManagerImpl.java delete mode 100644 test/net/sf/briar/crypto/KeyRotatorImplTest.java diff --git a/api/net/sf/briar/api/crypto/KeyManager.java b/api/net/sf/briar/api/crypto/KeyManager.java index 7d71eb199d..ad406eceae 100644 --- a/api/net/sf/briar/api/crypto/KeyManager.java +++ b/api/net/sf/briar/api/crypto/KeyManager.java @@ -6,10 +6,20 @@ 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. + */ + boolean start(); + + /** Stops the key manager. */ + void stop(); + /** * Returns a connection context for connecting to the given contact over - * the given transport, or null if the contact does not support the - * transport. + * the given transport, or null if an error occurs or the contact does not + * support the transport. */ ConnectionContext getConnectionContext(ContactId c, TransportId t); } diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 6e642578e2..f3d952b401 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -114,9 +114,6 @@ public interface DatabaseComponent { /** Returns the IDs of all contacts. */ Collection<ContactId> getContacts() throws DbException; - /** Returns all contact transports. */ - Collection<ContactTransport> getContactTransports() throws DbException; - /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; @@ -150,9 +147,9 @@ public interface DatabaseComponent { /** * Increments the outgoing connection counter for the given contact - * transport in the given rotation period. + * transport in the given rotation period and returns the old value. */ - void incrementConnectionCounter(ContactId c, TransportId t, long period) + long incrementConnectionCounter(ContactId c, TransportId t, long period) throws DbException; /** Processes an acknowledgement from the given contact. */ diff --git a/api/net/sf/briar/api/db/TemporarySecret.java b/api/net/sf/briar/api/db/TemporarySecret.java index 08455e3252..e3818b14e5 100644 --- a/api/net/sf/briar/api/db/TemporarySecret.java +++ b/api/net/sf/briar/api/db/TemporarySecret.java @@ -1,20 +1,19 @@ package net.sf.briar.api.db; +import static net.sf.briar.api.transport.TransportConstants.CONNECTION_WINDOW_SIZE; import net.sf.briar.api.ContactId; import net.sf.briar.api.protocol.TransportId; -public class TemporarySecret { +public class TemporarySecret extends ContactTransport { - private final ContactId contactId; - private final TransportId transportId; private final long period, outgoing, centre; private final byte[] secret, bitmap; public TemporarySecret(ContactId contactId, TransportId transportId, + long epoch, long clockDiff, long latency, boolean alice, long period, byte[] secret, long outgoing, long centre, byte[] bitmap) { - this.contactId = contactId; - this.transportId = transportId; + super(contactId, transportId, epoch, clockDiff, latency, alice); this.period = period; this.secret = secret; this.outgoing = outgoing; @@ -22,12 +21,14 @@ public class TemporarySecret { this.bitmap = bitmap; } - public ContactId getContactId() { - return contactId; - } - - public TransportId getTransportId() { - return transportId; + public TemporarySecret(TemporarySecret old, long period, byte[] secret) { + super(old.getContactId(), old.getTransportId(), old.getEpoch(), + old.getClockDifference(), old.getLatency(), old.getAlice()); + this.period = period; + this.secret = secret; + outgoing = 0L; + centre = 0L; + bitmap = new byte[CONNECTION_WINDOW_SIZE / 8]; } public long getPeriod() { diff --git a/api/net/sf/briar/api/transport/ConnectionRecogniser.java b/api/net/sf/briar/api/transport/ConnectionRecogniser.java index 61ca17daff..4c7fa20fd8 100644 --- a/api/net/sf/briar/api/transport/ConnectionRecogniser.java +++ b/api/net/sf/briar/api/transport/ConnectionRecogniser.java @@ -2,6 +2,7 @@ package net.sf.briar.api.transport; import net.sf.briar.api.ContactId; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.TemporarySecret; import net.sf.briar.api.protocol.TransportId; /** @@ -17,10 +18,11 @@ public interface ConnectionRecogniser { ConnectionContext acceptConnection(TransportId t, byte[] tag) throws DbException; - void addWindow(ContactId c, TransportId t, long period, boolean alice, - byte[] secret, long centre, byte[] bitmap) throws DbException; + void addSecret(TemporarySecret s) throws DbException; - void removeWindow(ContactId c, TransportId t, long period); + void removeSecret(ContactId c, TransportId t, long period); - void removeWindows(ContactId c); + void removeSecrets(ContactId c); + + void removeSecrets(); } diff --git a/components/net/sf/briar/crypto/KeyRotator.java b/components/net/sf/briar/crypto/KeyRotator.java deleted file mode 100644 index 71dd43d7e2..0000000000 --- a/components/net/sf/briar/crypto/KeyRotator.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.sf.briar.crypto; - -import net.sf.briar.api.db.DbException; - -interface KeyRotator { - - /** - * Starts a new thread to rotate keys periodically. The rotator will pause - * for the given number of milliseconds between rotations. - */ - void startRotating(Callback callback, long msBetweenRotations); - - /** Tells the rotator thread to exit. */ - void stopRotating(); - - interface Callback { - - /** - * Rotates keys, replacing and destroying any keys that have passed the - * ends of their respective retention periods. - */ - void rotateKeys() throws DbException; - } -} diff --git a/components/net/sf/briar/crypto/KeyRotatorImpl.java b/components/net/sf/briar/crypto/KeyRotatorImpl.java deleted file mode 100644 index 0d653561fc..0000000000 --- a/components/net/sf/briar/crypto/KeyRotatorImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.sf.briar.crypto; - -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.sf.briar.api.db.DbException; - -class KeyRotatorImpl extends TimerTask implements KeyRotator { - - private static final Logger LOG = - Logger.getLogger(KeyRotatorImpl.class.getName()); - - private volatile Callback callback = null; - private volatile Timer timer = null; - - public void startRotating(Callback callback, long msBetweenRotations) { - this.callback = callback; - timer = new Timer(); - timer.scheduleAtFixedRate(this, 0L, msBetweenRotations); - } - - public void stopRotating() { - if(timer == null) throw new IllegalStateException(); - timer.cancel(); - } - - public void run() { - if(callback == null) throw new IllegalStateException(); - try { - callback.rotateKeys(); - } catch(DbException e) { - if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); - throw new Error(e); // Kill the application - } catch(RuntimeException e) { - if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); - throw new Error(e); // Kill the application - } - } -} diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index a554c58175..10898756dc 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -468,11 +468,11 @@ interface Database<T> { /** * Increments the outgoing connection counter for the given contact - * transport in the given rotation period. + * transport in the given rotation period and returns the old value; * <p> * Locking: contact read, window write. */ - void incrementConnectionCounter(T txn, ContactId c, TransportId t, + long incrementConnectionCounter(T txn, ContactId c, TransportId t, long period) throws DbException; /** diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index d0854576c5..48bb097cdd 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -758,30 +758,6 @@ DatabaseCleaner.Callback { } } - public Collection<ContactTransport> getContactTransports() - throws DbException { - contactLock.readLock().lock(); - try { - windowLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - Collection<ContactTransport> contactTransports = - db.getContactTransports(txn); - db.commitTransaction(txn); - return contactTransports; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - windowLock.readLock().unlock(); - } - } finally { - contactLock.readLock().unlock(); - } - } - public TransportProperties getLocalProperties(TransportId t) throws DbException { transportLock.readLock().lock(); @@ -1005,7 +981,7 @@ DatabaseCleaner.Callback { } } - public void incrementConnectionCounter(ContactId c, TransportId t, + public long incrementConnectionCounter(ContactId c, TransportId t, long period) throws DbException { contactLock.readLock().lock(); try { @@ -1015,8 +991,9 @@ DatabaseCleaner.Callback { try { if(!db.containsContactTransport(txn, c, t)) throw new NoSuchContactTransportException(); - db.incrementConnectionCounter(txn, c, t, period); + long l = db.incrementConnectionCounter(txn, c, t, period); db.commitTransaction(txn); + return l; } catch(DbException e) { db.abortTransaction(txn); throw e; diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index fd7aa47cc4..c3afa3b951 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -1557,22 +1557,30 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactId, transportId, period, secret," - + " outgoing, centre, bitmap" - + " FROM secrets"; + String sql = "SELECT ct.contactId, ct.transportId, epoch," + + " clockDiff, latency, alice, period, secret, outgoing," + + " centre, bitmap" + + " FROM contactTransports AS ct" + + " JOIN secrets AS s" + + " ON ct.contactId = s.contactId" + + " AND ct.transportId = s.transportId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<TemporarySecret> secrets = new ArrayList<TemporarySecret>(); while(rs.next()) { ContactId c = new ContactId(rs.getInt(1)); TransportId t = new TransportId(rs.getBytes(2)); - long period = rs.getLong(3); - byte[] secret = rs.getBytes(4); - long outgoing = rs.getLong(5); - long centre = rs.getLong(6); - byte[] bitmap = rs.getBytes(7); - secrets.add(new TemporarySecret(c, t, period, secret, outgoing, - centre, bitmap)); + long epoch = rs.getLong(3); + long clockDiff = rs.getLong(4); + long latency = rs.getLong(5); + boolean alice = rs.getBoolean(6); + long period = rs.getLong(7); + byte[] secret = rs.getBytes(8); + long outgoing = rs.getLong(9); + long centre = rs.getLong(10); + byte[] bitmap = rs.getBytes(11); + secrets.add(new TemporarySecret(c, t, epoch, clockDiff, latency, + alice, period, secret, outgoing, centre, bitmap)); } rs.close(); ps.close(); @@ -2021,11 +2029,26 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void incrementConnectionCounter(Connection txn, ContactId c, + public long incrementConnectionCounter(Connection txn, ContactId c, TransportId t, long period) throws DbException { PreparedStatement ps = null; + ResultSet rs = null; try { - String sql = "UPDATE secrets SET outgoing = outgoing + 1" + // Get the current connection counter + String sql = "SELECT outgoing FROM secrets" + + " WHERE contactId = ? AND transportId = ? AND period + ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getBytes()); + ps.setLong(3, period); + rs = ps.executeQuery(); + if(!rs.next()) throw new DbStateException(); + long connection = rs.getLong(1); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + // Increment the connection counter + sql = "UPDATE secrets SET outgoing = outgoing + 1" + " WHERE contactId = ? AND transportId = ? AND period = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); @@ -2034,8 +2057,10 @@ abstract class JdbcDatabase implements Database<Connection> { int affected = ps.executeUpdate(); if(affected > 1) throw new DbStateException(); ps.close(); + return connection; } catch(SQLException e) { tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java index 3917b0c48f..7fb1c9b722 100644 --- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java +++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java @@ -7,6 +7,7 @@ import net.sf.briar.api.ContactId; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.TemporarySecret; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionRecogniser; @@ -37,9 +38,8 @@ class ConnectionRecogniserImpl implements ConnectionRecogniser { return r.acceptConnection(tag); } - public void addWindow(ContactId c, TransportId t, long period, - boolean alice, byte[] secret, long centre, byte[] bitmap) - throws DbException { + public void addSecret(TemporarySecret s) throws DbException { + TransportId t = s.getTransportId(); TransportConnectionRecogniser r; synchronized(this) { r = recognisers.get(t); @@ -48,20 +48,24 @@ class ConnectionRecogniserImpl implements ConnectionRecogniser { recognisers.put(t, r); } } - r.addWindow(c, period, alice, secret, centre, bitmap); + r.addSecret(s); } - public void removeWindow(ContactId c, TransportId t, long period) { + public void removeSecret(ContactId c, TransportId t, long period) { TransportConnectionRecogniser r; synchronized(this) { r = recognisers.get(t); } - if(r != null) r.removeWindow(c, period); + if(r != null) r.removeSecret(c, period); } - public synchronized void removeWindows(ContactId c) { - for(TransportConnectionRecogniser r : recognisers.values()) { - r.removeWindows(c); - } + public synchronized void removeSecrets(ContactId c) { + for(TransportConnectionRecogniser r : recognisers.values()) + r.removeSecrets(c); + } + + public synchronized void removeSecrets() { + for(TransportConnectionRecogniser r : recognisers.values()) + r.removeSecrets(); } } diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java index b0a2b8c848..a56e4becd2 100644 --- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java @@ -13,6 +13,7 @@ import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; + import com.google.inject.Inject; class ConnectionWriterFactoryImpl implements ConnectionWriterFactory { diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java index c4b52cfe4d..d27905938c 100644 --- a/components/net/sf/briar/transport/ConnectionWriterImpl.java +++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java @@ -6,6 +6,7 @@ import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import java.io.IOException; import java.io.OutputStream; + import net.sf.briar.api.transport.ConnectionWriter; /** diff --git a/components/net/sf/briar/transport/KeyManagerImpl.java b/components/net/sf/briar/transport/KeyManagerImpl.java new file mode 100644 index 0000000000..20f8158cbd --- /dev/null +++ b/components/net/sf/briar/transport/KeyManagerImpl.java @@ -0,0 +1,305 @@ +package net.sf.briar.transport; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +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.DatabaseComponent; +import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.TemporarySecret; +import net.sf.briar.api.db.event.ContactRemovedEvent; +import net.sf.briar.api.db.event.DatabaseEvent; +import net.sf.briar.api.db.event.DatabaseListener; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionRecogniser; +import net.sf.briar.util.ByteUtils; + +import com.google.inject.Inject; + +class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener { + + private static final int MS_BETWEEN_CHECKS = 60 * 1000; + + private static final Logger LOG = + Logger.getLogger(KeyManagerImpl.class.getName()); + + private final CryptoComponent crypto; + private final DatabaseComponent db; + private final ConnectionRecogniser recogniser; + private final Timer timer; + // Locking: this + private final Map<ContactTransportKey, TemporarySecret> outgoing; + // Locking: this + private final Map<ContactTransportKey, TemporarySecret> incomingOld; + // Locking: this + private final Map<ContactTransportKey, TemporarySecret> incomingNew; + + // Locking: this + private boolean running = false; + + @Inject + public KeyManagerImpl(CryptoComponent crypto, DatabaseComponent db, + ConnectionRecogniser recogniser) { + this.crypto = crypto; + this.db = db; + this.recogniser = recogniser; + timer = new Timer(); + outgoing = new HashMap<ContactTransportKey, TemporarySecret>(); + incomingOld = new HashMap<ContactTransportKey, TemporarySecret>(); + incomingNew = new HashMap<ContactTransportKey, TemporarySecret>(); + } + + public synchronized boolean start() { + if(running) return false; + Collection<TemporarySecret> secrets; + try { + secrets = db.getSecrets(); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + return false; + } + // Work out what phase of its lifecycle each secret is in + long now = System.currentTimeMillis(); + Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets); + // Replace any dead secrets + Collection<TemporarySecret> created = replaceDeadSecrets(now, dead); + try { + // Store any secrets that have been created + if(!created.isEmpty()) db.addSecrets(created); + // Pass the current incoming secrets to the connection recogniser + // FIXME: This uses a separate database transaction for each secret + for(TemporarySecret s : incomingOld.values()) + recogniser.addSecret(s); + for(TemporarySecret s : incomingNew.values()) + recogniser.addSecret(s); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + return false; + } + // Schedule periodic key rotation + timer.scheduleAtFixedRate(this, MS_BETWEEN_CHECKS, MS_BETWEEN_CHECKS); + running = true; + return true; + } + + // Assigns secrets to the appropriate maps and returns any dead secrets + // FIXME: Check there are no duplicate keys when updating maps + private Collection<TemporarySecret> assignSecretsToMaps(long now, + 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); + long rotationPeriod = getRotationPeriod(s); + long creationTime = getCreationTime(s); + long activationTime = creationTime + s.getClockDifference(); + long successorCreationTime = creationTime + rotationPeriod; + long deactivationTime = activationTime + rotationPeriod; + long destructionTime = successorCreationTime + rotationPeriod; + if(now >= destructionTime) { + dead.add(s); + } else if(now >= deactivationTime) { + incomingOld.put(k, s); + } else if(now >= successorCreationTime) { + incomingOld.put(k, s); + outgoing.put(k, s); + } else if(now >= activationTime) { + incomingNew.put(k, s); + outgoing.put(k, s); + } else if(now >= creationTime) { + incomingNew.put(k, s); + } else { + // FIXME: What should we do if the clock moves backwards? + throw new IllegalStateException(); + } + } + return dead; + } + + // Replaces and erases the given secrets and returns any secrets created + private Collection<TemporarySecret> replaceDeadSecrets(long now, + 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); + if(incomingNew.containsKey(k)) throw new IllegalStateException(); + byte[] secret = s.getSecret(); + long period = s.getPeriod(); + if(incomingOld.containsKey(k)) { + // The dead secret's successor is still alive + byte[] secret1 = crypto.deriveNextSecret(secret, period + 1); + TemporarySecret s1 = new TemporarySecret(s, period + 1, + secret1); + created.add(s1); + incomingNew.put(k, s1); + long creationTime = getCreationTime(s1); + long activationTime = creationTime + s1.getClockDifference(); + if(now >= activationTime) outgoing.put(k, s1); + } else { + // The dead secret has no living successor + long rotationPeriod = getRotationPeriod(s); + long elapsed = now - s.getEpoch(); + long currentPeriod = elapsed / rotationPeriod; + if(currentPeriod <= period) throw new IllegalStateException(); + // Derive the two current incoming secrets + byte[] secret1, secret2; + secret1 = secret; + for(long l = period; l < currentPeriod; l++) { + byte[] temp = crypto.deriveNextSecret(secret1, l); + ByteUtils.erase(secret1); + secret1 = temp; + } + secret2 = crypto.deriveNextSecret(secret1, currentPeriod); + // One of the incoming secrets is the current outgoing secret + TemporarySecret s1, s2; + s1 = new TemporarySecret(s, currentPeriod - 1, secret1); + created.add(s1); + incomingOld.put(k, s1); + s2 = new TemporarySecret(s, currentPeriod, secret2); + created.add(s2); + incomingNew.put(k, s2); + if(elapsed % rotationPeriod < s.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); + } + } + // Erase the dead secret + ByteUtils.erase(secret); + } + return created; + } + + private long getRotationPeriod(TemporarySecret s) { + return 2 * s.getClockDifference() + s.getLatency(); + } + + private long getCreationTime(TemporarySecret s) { + long rotationPeriod = getRotationPeriod(s); + return s.getEpoch() + rotationPeriod * s.getPeriod(); + } + + public synchronized void stop() { + if(!running) return; + recogniser.removeSecrets(); + removeAndEraseSecrets(outgoing); + removeAndEraseSecrets(incomingOld); + removeAndEraseSecrets(incomingNew); + running = false; + } + + // Locking: this + private void removeAndEraseSecrets(Map<?, TemporarySecret> m) { + for(TemporarySecret s : m.values()) ByteUtils.erase(s.getSecret()); + m.clear(); + } + + public synchronized ConnectionContext getConnectionContext(ContactId c, + TransportId t) { + TemporarySecret s = outgoing.get(new ContactTransportKey(c, t)); + if(s == null) return null; + long connection; + try { + connection = db.incrementConnectionCounter(c, t, s.getPeriod()); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + return null; + } + byte[] secret = s.getSecret().clone(); + return new ConnectionContext(c, t, secret, connection, s.getAlice()); + } + + @Override + public synchronized void run() { + // Rebuild the maps because we may be running a whole period late + Collection<TemporarySecret> secrets = new ArrayList<TemporarySecret>(); + secrets.addAll(incomingOld.values()); + secrets.addAll(incomingNew.values()); + outgoing.clear(); + incomingOld.clear(); + incomingNew.clear(); + // Work out what phase of its lifecycle each secret is in + long now = System.currentTimeMillis(); + Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets); + // Replace any dead secrets + Collection<TemporarySecret> created = replaceDeadSecrets(now, dead); + try { + // Store any secrets that have been created + if(!created.isEmpty()) db.addSecrets(created); + // Pass the current incoming secrets to the connection recogniser + // FIXME: This uses a separate database transaction for each secret + for(TemporarySecret s : incomingOld.values()) + recogniser.addSecret(s); + for(TemporarySecret s : incomingNew.values()) + recogniser.addSecret(s); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + } + } + + public void eventOccurred(DatabaseEvent e) { + if(e instanceof ContactRemovedEvent) { + ContactId c = ((ContactRemovedEvent) e).getContactId(); + recogniser.removeSecrets(c); + synchronized(this) { + removeAndEraseSecrets(c, outgoing); + removeAndEraseSecrets(c, incomingOld); + removeAndEraseSecrets(c, incomingNew); + } + } + } + + // Locking: this + private void removeAndEraseSecrets(ContactId c, Map<?, TemporarySecret> m) { + Iterator<TemporarySecret> it = m.values().iterator(); + while(it.hasNext()) { + TemporarySecret s = it.next(); + if(s.getContactId().equals(c)) { + ByteUtils.erase(s.getSecret()); + it.remove(); + } + } + } + + private static class ContactTransportKey { + + private final ContactId contactId; + private final TransportId transportId; + + private ContactTransportKey(ContactId contactId, + TransportId transportId) { + this.contactId = contactId; + this.transportId = transportId; + } + + @Override + public int hashCode() { + return contactId.hashCode() + transportId.hashCode(); + } + + @Override + public boolean equals(Object o) { + if(o instanceof ContactTransportKey) { + ContactTransportKey k = (ContactTransportKey) o; + return contactId.equals(k.contactId) && + transportId.equals(k.transportId); + } + return false; + } + } +} diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java index a9c45fd961..924c09db81 100644 --- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java +++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.OutputStream; import java.security.GeneralSecurityException; + import net.sf.briar.api.crypto.AuthenticatedCipher; import net.sf.briar.api.crypto.ErasableKey; diff --git a/components/net/sf/briar/transport/TransportConnectionRecogniser.java b/components/net/sf/briar/transport/TransportConnectionRecogniser.java index 2eb5089672..c0525df306 100644 --- a/components/net/sf/briar/transport/TransportConnectionRecogniser.java +++ b/components/net/sf/briar/transport/TransportConnectionRecogniser.java @@ -15,6 +15,7 @@ import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.TemporarySecret; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.util.ByteUtils; @@ -74,8 +75,13 @@ class TransportConnectionRecogniser { return ctx; } - synchronized void addWindow(ContactId contactId, long period, boolean alice, - byte[] secret, long centre, byte[] bitmap) throws DbException { + synchronized void addSecret(TemporarySecret s) throws DbException { + ContactId contactId = s.getContactId(); + long period = s.getPeriod(); + byte[] secret = s.getSecret(); + boolean alice = s.getAlice(); + long centre = s.getWindowCentre(); + byte[] bitmap = s.getWindowBitmap(); // Create the connection window and the expected tags Cipher cipher = crypto.getTagCipher(); ErasableKey key = crypto.deriveTagKey(secret, alice); @@ -96,10 +102,15 @@ class TransportConnectionRecogniser { removalMap.put(new RemovalKey(contactId, period), rctx); } - synchronized void removeWindow(ContactId contactId, long period) { + synchronized void removeSecret(ContactId contactId, long period) { RemovalKey rk = new RemovalKey(contactId, period); RemovalContext rctx = removalMap.remove(rk); if(rctx == null) throw new IllegalArgumentException(); + removeSecret(rctx); + } + + // Locking: this + private void removeSecret(RemovalContext rctx) { // Remove the expected tags Cipher cipher = crypto.getTagCipher(); ErasableKey key = crypto.deriveTagKey(rctx.secret, rctx.alice); @@ -114,12 +125,18 @@ class TransportConnectionRecogniser { ByteUtils.erase(rctx.secret); } - synchronized void removeWindows(ContactId c) { + synchronized void removeSecrets(ContactId c) { Collection<RemovalKey> keysToRemove = new ArrayList<RemovalKey>(); for(RemovalKey k : removalMap.keySet()) { if(k.contactId.equals(c)) keysToRemove.add(k); } - for(RemovalKey k : keysToRemove) removeWindow(k.contactId, k.period); + for(RemovalKey k : keysToRemove) removeSecret(k.contactId, k.period); + } + + synchronized void removeSecrets() { + for(RemovalContext rctx : removalMap.values()) removeSecret(rctx); + assert tagMap.isEmpty(); + removalMap.clear(); } private static class WindowContext { @@ -148,7 +165,7 @@ class TransportConnectionRecogniser { @Override public int hashCode() { - return contactId.hashCode()+ (int) period; + return contactId.hashCode() + (int) period; } @Override diff --git a/components/net/sf/briar/transport/TransportModule.java b/components/net/sf/briar/transport/TransportModule.java index daceddd3b1..931a5554bb 100644 --- a/components/net/sf/briar/transport/TransportModule.java +++ b/components/net/sf/briar/transport/TransportModule.java @@ -3,13 +3,16 @@ package net.sf.briar.transport; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import net.sf.briar.api.crypto.KeyManager; import net.sf.briar.api.transport.ConnectionDispatcher; import net.sf.briar.api.transport.ConnectionReaderFactory; +import net.sf.briar.api.transport.ConnectionRecogniser; import net.sf.briar.api.transport.ConnectionRegistry; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.IncomingConnectionExecutor; import com.google.inject.AbstractModule; +import com.google.inject.Singleton; public class TransportModule extends AbstractModule { @@ -18,6 +21,8 @@ public class TransportModule extends AbstractModule { bind(ConnectionDispatcher.class).to(ConnectionDispatcherImpl.class); bind(ConnectionReaderFactory.class).to( ConnectionReaderFactoryImpl.class); + bind(ConnectionRecogniser.class).to(ConnectionRecogniserImpl.class).in( + Singleton.class); bind(ConnectionRegistry.class).toInstance(new ConnectionRegistryImpl()); bind(ConnectionWriterFactory.class).to( ConnectionWriterFactoryImpl.class); @@ -25,5 +30,6 @@ public class TransportModule extends AbstractModule { bind(Executor.class).annotatedWith( IncomingConnectionExecutor.class).toInstance( Executors.newCachedThreadPool()); + bind(KeyManager.class).to(KeyManagerImpl.class).in(Singleton.class); } } diff --git a/test/build.xml b/test/build.xml index a83fab2ee6..bcb9ea9678 100644 --- a/test/build.xml +++ b/test/build.xml @@ -20,7 +20,6 @@ <test name='net.sf.briar.crypto.ErasableKeyTest'/> <test name='net.sf.briar.crypto.KeyAgreementTest'/> <test name='net.sf.briar.crypto.KeyDerivationTest'/> - <test name='net.sf.briar.crypto.KeyRotatorImplTest'/> <test name='net.sf.briar.db.BasicH2Test'/> <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> <test name='net.sf.briar.db.DatabaseComponentImplTest'/> diff --git a/test/net/sf/briar/ProtocolIntegrationTest.java b/test/net/sf/briar/ProtocolIntegrationTest.java index ebe526173b..f1bddbeaf7 100644 --- a/test/net/sf/briar/ProtocolIntegrationTest.java +++ b/test/net/sf/briar/ProtocolIntegrationTest.java @@ -189,7 +189,7 @@ public class ProtocolIntegrationTest extends BriarTestCase { InputStream in = new ByteArrayInputStream(connectionData); byte[] tag = new byte[TAG_LENGTH]; assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH)); - assertArrayEquals(new byte[TAG_LENGTH], tag); + // FIXME: Check that the expected tag was received ConnectionContext ctx = new ConnectionContext(contactId, transportId, secret.clone(), 0L, true); ConnectionReader conn = connectionReaderFactory.createConnectionReader( diff --git a/test/net/sf/briar/crypto/KeyDerivationTest.java b/test/net/sf/briar/crypto/KeyDerivationTest.java index 49dda92c8e..b05f536a45 100644 --- a/test/net/sf/briar/crypto/KeyDerivationTest.java +++ b/test/net/sf/briar/crypto/KeyDerivationTest.java @@ -63,7 +63,7 @@ public class KeyDerivationTest extends BriarTestCase { public void testConnectionNumberAffectsDerivation() { List<byte[]> secrets = new ArrayList<byte[]>(); for(int i = 0; i < 20; i++) { - secrets.add(crypto.deriveNextSecret(secret, i)); + secrets.add(crypto.deriveNextSecret(secret.clone(), i)); } for(int i = 0; i < 20; i++) { byte[] secretI = secrets.get(i); diff --git a/test/net/sf/briar/crypto/KeyRotatorImplTest.java b/test/net/sf/briar/crypto/KeyRotatorImplTest.java deleted file mode 100644 index 3e124b2e8e..0000000000 --- a/test/net/sf/briar/crypto/KeyRotatorImplTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.sf.briar.crypto; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import net.sf.briar.BriarTestCase; -import net.sf.briar.api.db.DbException; -import net.sf.briar.crypto.KeyRotatorImpl; -import net.sf.briar.crypto.KeyRotator.Callback; - -import org.junit.Test; - -public class KeyRotatorImplTest extends BriarTestCase { - - @Test - public void testCleanerRunsPeriodically() throws Exception { - final CountDownLatch latch = new CountDownLatch(5); - Callback callback = new Callback() { - - public void rotateKeys() throws DbException { - latch.countDown(); - } - }; - KeyRotatorImpl cleaner = new KeyRotatorImpl(); - // Start the rotator - cleaner.startRotating(callback, 10L); - // The keys should be rotated five times (allow 5 secs for system load) - assertTrue(latch.await(5, TimeUnit.SECONDS)); - // Stop the rotator - cleaner.stopRotating(); - } - - @Test - public void testStoppingCleanerWakesItUp() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - Callback callback = new Callback() { - - public void rotateKeys() throws DbException { - latch.countDown(); - } - }; - KeyRotatorImpl cleaner = new KeyRotatorImpl(); - long start = System.currentTimeMillis(); - // Start the rotator - cleaner.startRotating(callback, 10L * 1000L); - // The keys should be rotated once at startup - assertTrue(latch.await(5, TimeUnit.SECONDS)); - // Stop the rotator (it should be waiting between rotations) - cleaner.stopRotating(); - long end = System.currentTimeMillis(); - // Check that much less than 10 seconds expired - assertTrue(end - start < 10L * 1000L); - } -} diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 0f14ed31f1..c30fe34787 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -88,8 +88,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { transports = Collections.singletonList(transport); contactTransport = new ContactTransport(contactId, transportId, 123L, 234L, 345L, true); - temporarySecret = new TemporarySecret(contactId, transportId, 0L, - new byte[32], 0L, 0L, new byte[4]); + temporarySecret = new TemporarySecret(contactId, transportId, 1L, 2L, + 3L, false, 4L, new byte[32], 5L, 6L, new byte[4]); } protected abstract <T> DatabaseComponent createDatabaseComponent( diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index a2646bb97b..d8ff0fc5dd 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -1728,18 +1728,18 @@ public class H2DatabaseTest extends BriarTestCase { byte[] secret1 = new byte[32], bitmap1 = new byte[4]; random.nextBytes(secret1); random.nextBytes(bitmap1); - TemporarySecret ts1 = new TemporarySecret(contactId, transportId, 0L, - secret1, 123L, 234L, bitmap1); + TemporarySecret s1 = new TemporarySecret(contactId, transportId, 123L, + 234L, 345L, false, 0L, secret1, 456L, 567L, bitmap1); byte[] secret2 = new byte[32], bitmap2 = new byte[4]; random.nextBytes(secret2); random.nextBytes(bitmap2); - TemporarySecret ts2 = new TemporarySecret(contactId, transportId, 1L, - secret2, 1234L, 2345L, bitmap2); + TemporarySecret s2 = new TemporarySecret(contactId, transportId, 1234L, + 2345L, 3456L, false, 1L, secret2, 4567L, 5678L, bitmap2); byte[] secret3 = new byte[32], bitmap3 = new byte[4]; random.nextBytes(secret3); random.nextBytes(bitmap3); - TemporarySecret ts3 = new TemporarySecret(contactId, transportId, 2L, - secret3, 12345L, 23456L, bitmap3); + TemporarySecret s3 = new TemporarySecret(contactId, transportId, 12345L, + 23456L, 34567L, false, 0L, secret3, 45678L, 56789L, bitmap3); Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -1749,26 +1749,36 @@ public class H2DatabaseTest extends BriarTestCase { // Add a contact and the first two secrets assertEquals(contactId, db.addContact(txn)); - db.addSecrets(txn, Arrays.asList(ts1, ts2)); + db.addSecrets(txn, Arrays.asList(s1, s2)); // Retrieve the first two secrets Collection<TemporarySecret> secrets = db.getSecrets(txn); assertEquals(2, secrets.size()); boolean foundFirst = false, foundSecond = false; - for(TemporarySecret ts : secrets) { - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - if(ts.getPeriod() == 0L) { - assertArrayEquals(secret1, ts.getSecret()); - assertEquals(123L, ts.getOutgoingConnectionCounter()); - assertEquals(234L, ts.getWindowCentre()); - assertArrayEquals(bitmap1, ts.getWindowBitmap()); + for(TemporarySecret s : secrets) { + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + if(s.getPeriod() == 0L) { + assertEquals(s1.getEpoch(), s.getEpoch()); + assertEquals(s1.getClockDifference(), s.getClockDifference()); + assertEquals(s1.getLatency(), s.getLatency()); + assertEquals(s1.getAlice(), s.getAlice()); + assertArrayEquals(s1.getSecret(), s.getSecret()); + assertEquals(s1.getOutgoingConnectionCounter(), + s.getOutgoingConnectionCounter()); + assertEquals(s1.getWindowCentre(), s.getWindowCentre()); + assertArrayEquals(s1.getWindowBitmap(), s.getWindowBitmap()); foundFirst = true; - } else if(ts.getPeriod() == 1L) { - assertArrayEquals(secret2, ts.getSecret()); - assertEquals(1234L, ts.getOutgoingConnectionCounter()); - assertEquals(2345L, ts.getWindowCentre()); - assertArrayEquals(bitmap2, ts.getWindowBitmap()); + } else if(s.getPeriod() == 1L) { + assertEquals(s2.getEpoch(), s.getEpoch()); + assertEquals(s2.getClockDifference(), s.getClockDifference()); + assertEquals(s2.getLatency(), s.getLatency()); + assertEquals(s2.getAlice(), s.getAlice()); + assertArrayEquals(s2.getSecret(), s.getSecret()); + assertEquals(s2.getOutgoingConnectionCounter(), + s.getOutgoingConnectionCounter()); + assertEquals(s2.getWindowCentre(), s.getWindowCentre()); + assertArrayEquals(s2.getWindowBitmap(), s.getWindowBitmap()); foundSecond = true; } else { fail(); @@ -1778,25 +1788,35 @@ public class H2DatabaseTest extends BriarTestCase { assertTrue(foundSecond); // Adding the third secret (period 2) should delete the first (period 0) - db.addSecrets(txn, Arrays.asList(ts3)); + db.addSecrets(txn, Arrays.asList(s3)); secrets = db.getSecrets(txn); assertEquals(2, secrets.size()); foundSecond = false; boolean foundThird = false; - for(TemporarySecret ts : secrets) { - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - if(ts.getPeriod() == 1L) { - assertArrayEquals(secret2, ts.getSecret()); - assertEquals(1234L, ts.getOutgoingConnectionCounter()); - assertEquals(2345L, ts.getWindowCentre()); - assertArrayEquals(bitmap2, ts.getWindowBitmap()); + for(TemporarySecret s : secrets) { + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + if(s.getPeriod() == 1L) { + assertEquals(s2.getEpoch(), s.getEpoch()); + assertEquals(s2.getClockDifference(), s.getClockDifference()); + assertEquals(s2.getLatency(), s.getLatency()); + assertEquals(s2.getAlice(), s.getAlice()); + assertArrayEquals(s2.getSecret(), s.getSecret()); + assertEquals(s2.getOutgoingConnectionCounter(), + s.getOutgoingConnectionCounter()); + assertEquals(s2.getWindowCentre(), s.getWindowCentre()); + assertArrayEquals(s2.getWindowBitmap(), s.getWindowBitmap()); foundSecond = true; - } else if(ts.getPeriod() == 2L) { - assertArrayEquals(secret3, ts.getSecret()); - assertEquals(12345L, ts.getOutgoingConnectionCounter()); - assertEquals(23456L, ts.getWindowCentre()); - assertArrayEquals(bitmap3, ts.getWindowBitmap()); + } else if(s.getPeriod() == 2L) { + assertEquals(s3.getEpoch(), s.getEpoch()); + assertEquals(s3.getClockDifference(), s.getClockDifference()); + assertEquals(s3.getLatency(), s.getLatency()); + assertEquals(s3.getAlice(), s.getAlice()); + assertArrayEquals(s3.getSecret(), s.getSecret()); + assertEquals(s3.getOutgoingConnectionCounter(), + s.getOutgoingConnectionCounter()); + assertEquals(s3.getWindowCentre(), s.getWindowCentre()); + assertArrayEquals(s3.getWindowBitmap(), s.getWindowBitmap()); foundThird = true; } else { fail(); @@ -1819,55 +1839,43 @@ public class H2DatabaseTest extends BriarTestCase { Random random = new Random(); byte[] secret = new byte[32], bitmap = new byte[4]; random.nextBytes(secret); - TemporarySecret ts = new TemporarySecret(contactId, transportId, 0L, - secret, 0L, 0L, bitmap); + TemporarySecret s = new TemporarySecret(contactId, transportId, 0L, + 0L, 0L, false, 0L, secret, 0L, 0L, bitmap); Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and the temporary secret assertEquals(contactId, db.addContact(txn)); - db.addSecrets(txn, Arrays.asList(ts)); + db.addSecrets(txn, Arrays.asList(s)); // Retrieve the secret Collection<TemporarySecret> secrets = db.getSecrets(txn); assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(0L, ts.getOutgoingConnectionCounter()); - assertEquals(0L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(0L, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(0L, s.getOutgoingConnectionCounter()); + assertEquals(0L, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); // Increment the connection counter twice and retrieve the secret again - db.incrementConnectionCounter(txn, contactId, transportId, 0L); - db.incrementConnectionCounter(txn, contactId, transportId, 0L); + assertEquals(0L, db.incrementConnectionCounter(txn, contactId, + transportId, 0L)); + assertEquals(1L, db.incrementConnectionCounter(txn, contactId, + transportId, 0L)); secrets = db.getSecrets(txn); assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(2L, ts.getOutgoingConnectionCounter()); - assertEquals(0L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); - - // Incrementing a nonexistent counter should not throw an exception - db.incrementConnectionCounter(txn, contactId, transportId, 1L); - // The nonexistent counter should not have been created - secrets = db.getSecrets(txn); - assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(2L, ts.getOutgoingConnectionCounter()); - assertEquals(0L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(0L, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(2L, s.getOutgoingConnectionCounter()); + assertEquals(0L, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); db.commitTransaction(txn); db.close(); @@ -1879,27 +1887,27 @@ public class H2DatabaseTest extends BriarTestCase { Random random = new Random(); byte[] secret = new byte[32], bitmap = new byte[4]; random.nextBytes(secret); - TemporarySecret ts = new TemporarySecret(contactId, transportId, 0L, - secret, 0L, 0L, bitmap); + TemporarySecret s = new TemporarySecret(contactId, transportId, 0L, + 0L, 0L, false, 0L, secret, 0L, 0L, bitmap); Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and the temporary secret assertEquals(contactId, db.addContact(txn)); - db.addSecrets(txn, Arrays.asList(ts)); + db.addSecrets(txn, Arrays.asList(s)); // Retrieve the secret Collection<TemporarySecret> secrets = db.getSecrets(txn); assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(0L, ts.getOutgoingConnectionCounter()); - assertEquals(0L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(0L, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(0L, s.getOutgoingConnectionCounter()); + assertEquals(0L, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); // Update the connection window and retrieve the secret again db.setConnectionWindow(txn, contactId, transportId, 0L, 1L, bitmap); @@ -1907,28 +1915,28 @@ public class H2DatabaseTest extends BriarTestCase { db.setConnectionWindow(txn, contactId, transportId, 0L, 1L, bitmap); secrets = db.getSecrets(txn); assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(0L, ts.getOutgoingConnectionCounter()); - assertEquals(1L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(0L, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(0L, s.getOutgoingConnectionCounter()); + assertEquals(1L, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); // Updating a nonexistent window should not throw an exception db.setConnectionWindow(txn, contactId, transportId, 1L, 1L, bitmap); // The nonexistent window should not have been created secrets = db.getSecrets(txn); assertEquals(1, secrets.size()); - ts = secrets.iterator().next(); - assertEquals(contactId, ts.getContactId()); - assertEquals(transportId, ts.getTransportId()); - assertEquals(0L, ts.getPeriod()); - assertArrayEquals(secret, ts.getSecret()); - assertEquals(0L, ts.getOutgoingConnectionCounter()); - assertEquals(1L, ts.getWindowCentre()); - assertArrayEquals(bitmap, ts.getWindowBitmap()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(0L, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(0L, s.getOutgoingConnectionCounter()); + assertEquals(1L, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); db.commitTransaction(txn); db.close(); diff --git a/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java b/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java index 853b5ffbe5..62463294b3 100644 --- a/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java +++ b/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java @@ -14,7 +14,6 @@ import java.util.concurrent.Executors; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; -import net.sf.briar.api.crypto.KeyManager; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.protocol.Ack; @@ -24,7 +23,6 @@ import net.sf.briar.api.protocol.RawBatch; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.transport.ConnectionContext; -import net.sf.briar.api.transport.ConnectionRecogniser; import net.sf.briar.api.transport.ConnectionRegistry; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.crypto.CryptoModule; @@ -48,8 +46,6 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { private final Mockery context; private final DatabaseComponent db; - private final KeyManager keyManager; - private final ConnectionRecogniser connRecogniser; private final ConnectionRegistry connRegistry; private final ConnectionWriterFactory connFactory; private final ProtocolWriterFactory protoFactory; @@ -61,14 +57,10 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { super(); context = new Mockery(); db = context.mock(DatabaseComponent.class); - keyManager = context.mock(KeyManager.class); - connRecogniser = context.mock(ConnectionRecogniser.class); Module testModule = new AbstractModule() { @Override public void configure() { bind(DatabaseComponent.class).toInstance(db); - bind(KeyManager.class).toInstance(keyManager); - bind(ConnectionRecogniser.class).toInstance(connRecogniser); bind(Executor.class).annotatedWith( DatabaseExecutor.class).toInstance( Executors.newCachedThreadPool()); diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java index c0c7437816..663700326d 100644 --- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java @@ -8,6 +8,7 @@ import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; + public class ConnectionWriterImplTest extends BriarTestCase { private static final int FRAME_LENGTH = 1024; diff --git a/test/net/sf/briar/transport/TransportIntegrationTest.java b/test/net/sf/briar/transport/TransportIntegrationTest.java index 7f6247aaed..22ff5d7dce 100644 --- a/test/net/sf/briar/transport/TransportIntegrationTest.java +++ b/test/net/sf/briar/transport/TransportIntegrationTest.java @@ -22,9 +22,15 @@ import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.transport.ConnectionReaderImpl; +import net.sf.briar.transport.ConnectionWriterFactoryImpl; +import net.sf.briar.transport.ConnectionWriterImpl; +import net.sf.briar.transport.IncomingEncryptionLayer; +import net.sf.briar.transport.OutgoingEncryptionLayer; import org.junit.Test; + import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; -- GitLab