From 17fe358fd9efaaa05bd7292007e950390a64092b Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Tue, 27 Mar 2018 17:01:39 +0100 Subject: [PATCH] Add a method for binding transport keys to a contact. --- .../bramble/api/db/DatabaseComponent.java | 6 +++ .../bramble/api/transport/KeyManager.java | 10 ++++- .../org/briarproject/bramble/db/Database.java | 6 +++ .../bramble/db/DatabaseComponentImpl.java | 12 ++++++ .../briarproject/bramble/db/JdbcDatabase.java | 28 +++++++++++++ .../bramble/transport/KeyManagerImpl.java | 27 ++++++++++--- .../transport/TransportKeyManager.java | 2 + .../transport/TransportKeyManagerImpl.java | 39 ++++++++++++------- .../bramble/db/DatabaseComponentImplTest.java | 34 ++++++++++++---- .../bramble/transport/KeyManagerImplTest.java | 3 +- .../TransportKeyManagerImplTest.java | 4 +- 11 files changed, 139 insertions(+), 32 deletions(-) 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 45f721f22d..2a4f262de8 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 ef422d66e5..50a5cf9480 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 e8597eaba7..b11e1b8281 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 f90f9e3ff1..305b672900 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 327203d630..747145a344 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 993cb5c4c6..c07c9e1e72 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 5da58633f0..41e31fbe62 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 b1dbb6ced9..6b9a1519eb 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 eb31247ce0..4832d9d8bc 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 a55a92bf78..ed7a357ed0 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 4a92d4eb35..a57ce4e742 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)), -- GitLab