diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java index 45f721f22df00a5160352619a0801955a037fbd0..2a4f262de853052e12b216ec8d008dd1ad81c475 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java @@ -110,6 +110,12 @@ public interface DatabaseComponent { KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c, TransportKeys k) throws DbException; + /** + * Binds the given keys for the given transport to the given contact. + */ + void bindTransportKeys(Transaction txn, ContactId c, TransportId t, + KeySetId k) throws DbException; + /** * Returns true if the database contains the given contact for the given * local pseudonym. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java index ef422d66e560d3e0c5826466f2fc521bd8556f18..50a5cf9480c430d8eb876fdb67642f86a2c30282 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java @@ -6,7 +6,7 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.plugin.TransportId; -import java.util.Collection; +import java.util.Map; import javax.annotation.Nullable; @@ -31,9 +31,15 @@ public interface KeyManager { * Derives and stores a set of unbound transport keys for each transport * and returns the key set IDs. */ - Collection<KeySetId> addUnboundKeys(Transaction txn, SecretKey master, + Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master, long timestamp, boolean alice) throws DbException; + /** + * Binds the given transport keys to the given contact. + */ + void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys) + throws DbException; + /** * Returns a {@link StreamContext} for sending a stream to the given * contact over the given transport, or null if an error occurs or the diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java index e8597eaba7deca87148ede882f806b7369d35f70..b11e1b828118593e3d1052f4c2c512c5c309c384 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java @@ -131,6 +131,12 @@ interface Database<T> { KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k) throws DbException; + /** + * Binds the given keys for the given transport to the given contact. + */ + void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k) + throws DbException; + /** * Returns true if the database contains the given contact for the given * local pseudonym. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index f90f9e3ff18631275a73cfd890bd6f51dd4ad9c6..305b672900fa61c8dc2cb10759f198c95f9c5b0c 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -245,6 +245,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { return db.addTransportKeys(txn, c, k); } + @Override + public void bindTransportKeys(Transaction transaction, ContactId c, + TransportId t, KeySetId k) throws DbException { + if (transaction.isReadOnly()) throw new IllegalArgumentException(); + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.bindTransportKeys(txn, c, t, k); + } + @Override public boolean containsContact(Transaction transaction, AuthorId remote, AuthorId local) throws DbException { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index 327203d63005514b6cb66ef389641c8f49859a99..747145a3442087ffc2d6b22e203b07e666fcdf59 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -950,6 +950,33 @@ abstract class JdbcDatabase implements Database<Connection> { } } + @Override + public void bindTransportKeys(Connection txn, ContactId c, TransportId t, + KeySetId k) throws DbException { + PreparedStatement ps = null; + try { + String sql = "UPDATE outgoingKeys SET contactId = ?" + + " WHERE keySetId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, k.getInt()); + int affected = ps.executeUpdate(); + if (affected < 0) throw new DbStateException(); + ps.close(); + sql = "UPDATE incomingKeys SET contactId = ?" + + " WHERE keySetId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, k.getInt()); + affected = ps.executeUpdate(); + if (affected < 0) throw new DbStateException(); + ps.close(); + } catch (SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + @Override public boolean containsContact(Connection txn, AuthorId remote, AuthorId local) throws DbException { @@ -2882,6 +2909,7 @@ abstract class JdbcDatabase implements Database<Connection> { try { // Delete any existing outgoing keys - this will also remove any // incoming keys with the same key set ID + // TODO: Add an index to speed this up? String sql = "DELETE FROM outgoingKeys WHERE keySetId = ?"; ps = txn.prepareStatement(sql); for (KeySet ks : keys) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java index 993cb5c4c66770c8712bc8f5f6010531d549b638..c07c9e1e72ee6f48ecade12704b2f52e474396ec 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java @@ -22,8 +22,6 @@ import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.StreamContext; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -108,15 +106,32 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { } @Override - public Collection<KeySetId> addUnboundKeys(Transaction txn, + public Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master, long timestamp, boolean alice) throws DbException { - Collection<KeySetId> ids = new ArrayList<>(); - for (TransportKeyManager m : managers.values()) - ids.add(m.addUnboundKeys(txn, master, timestamp, alice)); + Map<TransportId, KeySetId> ids = new HashMap<>(); + for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { + TransportId t = e.getKey(); + TransportKeyManager m = e.getValue(); + ids.put(t, m.addUnboundKeys(txn, master, timestamp, alice)); + } return ids; } + @Override + public void bindKeys(Transaction txn, ContactId c, + Map<TransportId, KeySetId> keys) throws DbException { + for (Entry<TransportId, KeySetId> e : keys.entrySet()) { + TransportId t = e.getKey(); + TransportKeyManager m = managers.get(t); + if (m == null) { + if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t); + } else { + m.bindKeys(txn, c, e.getValue()); + } + } + } + @Override public StreamContext getStreamContext(ContactId c, TransportId t) throws DbException { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java index 5da58633f09fe4c098ff3bcae1701bc0575bb631..41e31fbe62e2abb8727541fd1ffecc25f517fd4a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java @@ -21,6 +21,8 @@ interface TransportKeyManager { KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp, boolean alice) throws DbException; + void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException; + void removeContact(ContactId c); @Nullable diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java index b1dbb6ced97e47c393e52e19494ed0e892d59f3e..6b9a1519ebe7ef4fbba42bb2d0e2500c0dd9c967 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java @@ -22,7 +22,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; @@ -57,7 +56,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { private final ReentrantLock lock = new ReentrantLock(); // The following are locking: lock - private final Collection<MutableKeySet> keys = new ArrayList<>(); + private final Map<KeySetId, MutableKeySet> keys = new HashMap<>(); private final Map<Bytes, TagContext> inContexts = new HashMap<>(); private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>(); @@ -123,7 +122,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { private void addKeys(KeySetId keySetId, @Nullable ContactId contactId, MutableTransportKeys m) { MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); - keys.add(ks); + keys.put(keySetId, ks); if (contactId != null) { encodeTags(keySetId, contactId, m.getPreviousIncomingKeys()); encodeTags(keySetId, contactId, m.getCurrentIncomingKeys()); @@ -204,22 +203,34 @@ class TransportKeyManagerImpl implements TransportKeyManager { } } + @Override + public void bindKeys(Transaction txn, ContactId c, KeySetId k) + throws DbException { + lock.lock(); + try { + MutableKeySet ks = keys.get(k); + if (ks == null) throw new IllegalArgumentException(); + if (ks.getContactId() != null) throw new IllegalArgumentException(); + MutableTransportKeys m = ks.getTransportKeys(); + addKeys(k, c, m); + db.bindTransportKeys(txn, c, m.getTransportId(), k); + } finally { + lock.unlock(); + } + } + @Override public void removeContact(ContactId c) { lock.lock(); try { // Remove mutable state for the contact - Iterator<Entry<Bytes, TagContext>> inContextsIter = - inContexts.entrySet().iterator(); - while (inContextsIter.hasNext()) { - ContactId c1 = inContextsIter.next().getValue().contactId; - if (c1.equals(c)) inContextsIter.remove(); - } + Iterator<TagContext> it = inContexts.values().iterator(); + while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove(); outContexts.remove(c); - Iterator<MutableKeySet> keysIter = keys.iterator(); - while (keysIter.hasNext()) { - ContactId c1 = keysIter.next().getContactId(); - if (c1 != null && c1.equals(c)) keysIter.remove(); + Iterator<MutableKeySet> it1 = keys.values().iterator(); + while (it1.hasNext()) { + ContactId c1 = it1.next().getContactId(); + if (c1 != null && c1.equals(c)) it1.remove(); } } finally { lock.unlock(); @@ -300,7 +311,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { try { // Rotate the keys to the current rotation period Collection<KeySet> snapshot = new ArrayList<>(keys.size()); - for (MutableKeySet ks : keys) { + for (MutableKeySet ks : keys.values()) { snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(), ks.getTransportKeys().snapshot())); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java index eb31247ce074642cd6c9fcc29de75280def9151c..4832d9d8bc027cb2e53c355fdc2fff8494ea1a7a 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java @@ -284,11 +284,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { throws Exception { context.checking(new Expectations() {{ // Check whether the contact is in the DB (which it's not) - exactly(17).of(database).startTransaction(); + exactly(18).of(database).startTransaction(); will(returnValue(txn)); - exactly(17).of(database).containsContact(txn, contactId); + exactly(18).of(database).containsContact(txn, contactId); will(returnValue(false)); - exactly(17).of(database).abortTransaction(txn); + exactly(18).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); @@ -303,6 +303,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { db.endTransaction(transaction); } + transaction = db.startTransaction(false); + try { + db.bindTransportKeys(transaction, contactId, transportId, keySetId); + fail(); + } catch (NoSuchContactException expected) { + // Expected + } finally { + db.endTransaction(transaction); + } + transaction = db.startTransaction(false); try { db.generateAck(transaction, contactId, 123); @@ -768,13 +778,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { // endTransaction() oneOf(database).commitTransaction(txn); // Check whether the transport is in the DB (which it's not) - exactly(4).of(database).startTransaction(); + exactly(5).of(database).startTransaction(); will(returnValue(txn)); - oneOf(database).containsContact(txn, contactId); + exactly(2).of(database).containsContact(txn, contactId); will(returnValue(true)); - exactly(4).of(database).containsTransport(txn, transportId); + exactly(5).of(database).containsTransport(txn, transportId); will(returnValue(false)); - exactly(4).of(database).abortTransaction(txn); + exactly(5).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); @@ -789,6 +799,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { db.endTransaction(transaction); } + transaction = db.startTransaction(false); + try { + db.bindTransportKeys(transaction, contactId, transportId, keySetId); + fail(); + } catch (NoSuchTransportException expected) { + // Expected + } finally { + db.endTransaction(transaction); + } + transaction = db.startTransaction(false); try { db.getTransportKeys(transaction, transportId); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java index a55a92bf78175ff6aba8f0a717f196c4b17c781c..ed7a357ed092f32427496906bc27ddf009ae2697 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Random; import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; @@ -122,7 +123,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase { will(returnValue(keySetId)); }}); - assertEquals(singletonList(keySetId), + assertEquals(singletonMap(transportId, keySetId), keyManager.addUnboundKeys(txn, secretKey, timestamp, alice)); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java index 4a92d4eb35659b0cf6b70a7f1be1a771e2d8a8e1..a57ce4e742355706f48f06b14551f489cfd4b7b5 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/TransportKeyManagerImplTest.java @@ -449,8 +449,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { } // Save the keys that were rotated oneOf(db).updateTransportKeys(txn1, asList( - new KeySet(keySetId, contactId, rotated), - new KeySet(keySetId1, null, rotated1) + new KeySet(keySetId1, null, rotated1), + new KeySet(keySetId, contactId, rotated) )); // Schedule key rotation at the start of the next rotation period oneOf(scheduler).schedule(with(any(Runnable.class)),