From 89a4d1922b74d912a04f21cdeeeae4b1f367805e Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Thu, 26 Apr 2018 23:28:13 +0100
Subject: [PATCH] Remove support for unbound transport keys.

---
 .../bramble/api/contact/ContactManager.java   |   6 +-
 .../bramble/api/db/DatabaseComponent.java     |  12 +-
 .../bramble/api/transport/KeyManager.java     |  34 +---
 .../bramble/api/transport/KeySet.java         |   8 +-
 .../bramble/contact/ContactManagerImpl.java   |   2 +-
 .../org/briarproject/bramble/db/Database.java |  12 +-
 .../bramble/db/DatabaseComponentImpl.java     |  18 +-
 .../briarproject/bramble/db/JdbcDatabase.java |  40 +----
 .../bramble/transport/KeyManagerImpl.java     |  43 +----
 .../bramble/transport/MutableKeySet.java      |  14 +-
 .../transport/TransportKeyManager.java        |  11 +-
 .../transport/TransportKeyManagerImpl.java    |  66 +------
 .../contact/ContactManagerImplTest.java       |   4 +-
 .../bramble/db/DatabaseComponentImplTest.java |  34 +---
 .../bramble/db/JdbcDatabaseTest.java          | 116 ++----------
 .../bramble/transport/KeyManagerImplTest.java |  26 +--
 .../TransportKeyManagerImplTest.java          | 169 +++++-------------
 .../IntroduceeProtocolEngine.java             |   7 +-
 18 files changed, 121 insertions(+), 501 deletions(-)

diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
index cc1f6da04a..1f9daf48af 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
@@ -45,9 +45,9 @@ public interface ContactManager {
 	 *
 	 * @param alice true if the local party is Alice
 	 */
-	ContactId addContact(Author remote, AuthorId local,
-			SecretKey master, long timestamp, boolean alice, boolean verified,
-			boolean active) throws DbException;
+	ContactId addContact(Author remote, AuthorId local, SecretKey master,
+			long timestamp, boolean alice, boolean verified, boolean active)
+			throws DbException;
 
 	/**
 	 * Returns the contact with the given ID.
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 680ddcad96..914b88a2e2 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
@@ -104,18 +104,12 @@ public interface DatabaseComponent {
 			throws DbException;
 
 	/**
-	 * Stores the given transport keys, optionally binding them to the given
-	 * contact, and returns a key set ID.
+	 * Stores the given transport keys for the given contact and returns a
+	 * key set ID.
 	 */
-	KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
+	KeySetId addTransportKeys(Transaction txn, 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 8424c9325b..7071a5e472 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
@@ -19,48 +19,24 @@ public interface KeyManager {
 	/**
 	 * Informs the key manager that a new contact has been added. Derives and
 	 * stores a set of transport keys for communicating with the contact over
-	 * each transport.
+	 * each transport and returns the key set IDs.
 	 * <p/>
 	 * {@link StreamContext StreamContexts} for the contact can be created
 	 * after this method has returned.
 	 *
 	 * @param alice true if the local party is Alice
+	 * @param active whether the derived keys can be used for outgoing streams
 	 */
-	void addContact(Transaction txn, ContactId c, SecretKey master,
-			long timestamp, boolean alice) throws DbException;
-
-	/**
-	 * Derives and stores a set of unbound transport keys for each transport
-	 * and returns the key set IDs.
-	 * <p/>
-	 * The keys must be bound before they can be used for incoming streams,
-	 * and also activated before they can be used for outgoing streams.
-	 *
-	 * @param alice true if the local party is Alice
-	 */
-	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)
+	Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
+			SecretKey master, long timestamp, boolean alice, boolean active)
 			throws DbException;
 
 	/**
-	 * Marks the given transport keys as usable for outgoing streams. Keys must
-	 * be bound before they are activated.
+	 * Marks the given transport keys as usable for outgoing streams.
 	 */
 	void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
 			throws DbException;
 
-	/**
-	 * Removes the given transport keys, which must not have been bound, from
-	 * the manager and the database.
-	 */
-	void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
-			throws DbException;
-
 	/**
 	 * Returns true if we have keys that can be used for outgoing streams to
 	 * the given contact over the given transport.
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySet.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySet.java
index 9cc8f63c29..47fc65288a 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySet.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySet.java
@@ -3,23 +3,20 @@ package org.briarproject.bramble.api.transport;
 import org.briarproject.bramble.api.contact.ContactId;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 
-import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 
 /**
- * A set of transport keys for communicating with a contact. If the keys have
- * not yet been bound to a contact, {@link #getContactId()}} returns null.
+ * A set of transport keys for communicating with a contact.
  */
 @Immutable
 @NotNullByDefault
 public class KeySet {
 
 	private final KeySetId keySetId;
-	@Nullable
 	private final ContactId contactId;
 	private final TransportKeys transportKeys;
 
-	public KeySet(KeySetId keySetId, @Nullable ContactId contactId,
+	public KeySet(KeySetId keySetId, ContactId contactId,
 			TransportKeys transportKeys) {
 		this.keySetId = keySetId;
 		this.contactId = contactId;
@@ -30,7 +27,6 @@ public class KeySet {
 		return keySetId;
 	}
 
-	@Nullable
 	public ContactId getContactId() {
 		return contactId;
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
index afa68c1800..02c94fc750 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
@@ -46,7 +46,7 @@ class ContactManagerImpl implements ContactManager {
 			SecretKey master, long timestamp, boolean alice, boolean verified,
 			boolean active) throws DbException {
 		ContactId c = db.addContact(txn, remote, local, verified, active);
-		keyManager.addContact(txn, c, master, timestamp, alice);
+		keyManager.addContact(txn, c, master, timestamp, alice, active);
 		Contact contact = db.getContact(txn, c);
 		for (ContactHook hook : hooks) hook.addingContact(txn, contact);
 		return c;
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 6d9fb54fbe..66e5cfcf6d 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
@@ -125,16 +125,10 @@ interface Database<T> {
 			throws DbException;
 
 	/**
-	 * Stores the given transport keys, optionally binding them to the given
-	 * contact, and returns a key set ID.
+	 * Stores the given transport keys for the given contact and returns a
+	 * key set ID.
 	 */
-	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)
+	KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
 			throws DbException;
 
 	/**
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 ad83111180..6f54b32199 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
@@ -234,29 +234,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	}
 
 	@Override
-	public KeySetId addTransportKeys(Transaction transaction,
-			@Nullable ContactId c, TransportKeys k) throws DbException {
+	public KeySetId addTransportKeys(Transaction transaction, ContactId c,
+			TransportKeys k) throws DbException {
 		if (transaction.isReadOnly()) throw new IllegalArgumentException();
 		T txn = unbox(transaction);
-		if (c != null && !db.containsContact(txn, c))
+		if (!db.containsContact(txn, c))
 			throw new NoSuchContactException();
 		if (!db.containsTransport(txn, k.getTransportId()))
 			throw new NoSuchTransportException();
 		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 21103c2d43..9e38a717bb 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
@@ -236,7 +236,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " (transportId _STRING NOT NULL,"
 					+ " keySetId _COUNTER,"
 					+ " rotationPeriod BIGINT NOT NULL,"
-					+ " contactId INT," // Null if keys are not bound
+					+ " contactId INT NOT NULL,"
 					+ " tagKey _SECRET NOT NULL,"
 					+ " headerKey _SECRET NOT NULL,"
 					+ " stream BIGINT NOT NULL,"
@@ -255,7 +255,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " (transportId _STRING NOT NULL,"
 					+ " keySetId INT NOT NULL,"
 					+ " rotationPeriod BIGINT NOT NULL,"
-					+ " contactId INT," // Null if keys are not bound
+					+ " contactId INT NOT NULL,"
 					+ " tagKey _SECRET NOT NULL,"
 					+ " headerKey _SECRET NOT NULL,"
 					+ " base BIGINT NOT NULL,"
@@ -883,7 +883,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 	}
 
 	@Override
-	public KeySetId addTransportKeys(Connection txn, @Nullable ContactId c,
+	public KeySetId addTransportKeys(Connection txn, ContactId c,
 			TransportKeys k) throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
@@ -893,8 +893,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " rotationPeriod, tagKey, headerKey, stream, active)"
 					+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
 			ps = txn.prepareStatement(sql);
-			if (c == null) ps.setNull(1, INTEGER);
-			else ps.setInt(1, c.getInt());
+			ps.setInt(1, c.getInt());
 			ps.setString(2, k.getTransportId().getString());
 			OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
 			ps.setLong(3, outCurr.getRotationPeriod());
@@ -922,8 +921,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, keySetId.getInt());
-			if (c == null) ps.setNull(2, INTEGER);
-			else ps.setInt(2, c.getInt());
+			ps.setInt(2, c.getInt());
 			ps.setString(3, k.getTransportId().getString());
 			// Previous rotation period
 			IncomingKeys inPrev = k.getPreviousIncomingKeys();
@@ -965,33 +963,6 @@ 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 {
@@ -2172,7 +2143,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 				if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
 				KeySetId keySetId = new KeySetId(rs.getInt(1));
 				ContactId contactId = new ContactId(rs.getInt(2));
-				if (rs.wasNull()) contactId = null;
 				long rotationPeriod = rs.getLong(3);
 				SecretKey tagKey = new SecretKey(rs.getBytes(4));
 				SecretKey headerKey = new SecretKey(rs.getBytes(5));
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 bbd5e5ec15..ffbe8c0ebf 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
@@ -99,39 +99,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
 	}
 
 	@Override
-	public void addContact(Transaction txn, ContactId c, SecretKey master,
-			long timestamp, boolean alice) throws DbException {
-		for (TransportKeyManager m : managers.values())
-			m.addContact(txn, c, master, timestamp, alice);
-	}
-
-	@Override
-	public Map<TransportId, KeySetId> addUnboundKeys(Transaction txn,
-			SecretKey master, long timestamp, boolean alice)
+	public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
+			SecretKey master, long timestamp, boolean alice, boolean active)
 			throws DbException {
 		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));
+			ids.put(t, m.addContact(txn, c, master, timestamp, alice, active));
 		}
 		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 void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
 			throws DbException {
@@ -146,24 +125,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
 		}
 	}
 
-	@Override
-	public void removeKeys(Transaction txn, 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.removeKeys(txn, e.getValue());
-			}
-		}
-	}
-
 	@Override
 	public boolean canSendOutgoingStreams(ContactId c, TransportId t) {
 		TransportKeyManager m = managers.get(t);
-		return m == null ? false : m.canSendOutgoingStreams(c);
+		return m != null && m.canSendOutgoingStreams(c);
 	}
 
 	@Override
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableKeySet.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableKeySet.java
index b55c5aef4f..63ce3134ff 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableKeySet.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableKeySet.java
@@ -3,32 +3,28 @@ package org.briarproject.bramble.transport;
 import org.briarproject.bramble.api.contact.ContactId;
 import org.briarproject.bramble.api.transport.KeySetId;
 
-import javax.annotation.Nullable;
-
-public class MutableKeySet {
+class MutableKeySet {
 
 	private final KeySetId keySetId;
-	@Nullable
 	private final ContactId contactId;
 	private final MutableTransportKeys transportKeys;
 
-	public MutableKeySet(KeySetId keySetId, @Nullable ContactId contactId,
+	MutableKeySet(KeySetId keySetId, ContactId contactId,
 			MutableTransportKeys transportKeys) {
 		this.keySetId = keySetId;
 		this.contactId = contactId;
 		this.transportKeys = transportKeys;
 	}
 
-	public KeySetId getKeySetId() {
+	KeySetId getKeySetId() {
 		return keySetId;
 	}
 
-	@Nullable
-	public ContactId getContactId() {
+	ContactId getContactId() {
 		return contactId;
 	}
 
-	public MutableTransportKeys getTransportKeys() {
+	MutableTransportKeys getTransportKeys() {
 		return transportKeys;
 	}
 }
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 5ca159a425..b407db16c8 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
@@ -15,18 +15,11 @@ interface TransportKeyManager {
 
 	void start(Transaction txn) throws DbException;
 
-	void addContact(Transaction txn, ContactId c, SecretKey master,
-			long timestamp, boolean alice) throws DbException;
-
-	KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
-			boolean alice) throws DbException;
-
-	void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException;
+	KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
+			long timestamp, boolean alice, boolean active) throws DbException;
 
 	void activateKeys(Transaction txn, KeySetId k) throws DbException;
 
-	void removeKeys(Transaction txn, KeySetId k) throws DbException;
-
 	void removeContact(ContactId c);
 
 	boolean canSendOutgoingStreams(ContactId c);
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 63bcdbc778..05dffce527 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
@@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Logger;
 
-import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -119,16 +118,14 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 	}
 
 	// Locking: lock
-	private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
+	private void addKeys(KeySetId keySetId, ContactId contactId,
 			MutableTransportKeys m) {
 		MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
 		keys.put(keySetId, ks);
-		if (contactId != null) {
-			encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
-			encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
-			encodeTags(keySetId, contactId, m.getNextIncomingKeys());
-			considerReplacingOutgoingKeys(ks);
-		}
+		encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
+		encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
+		encodeTags(keySetId, contactId, m.getNextIncomingKeys());
+		considerReplacingOutgoingKeys(ks);
 	}
 
 	// Locking: lock
@@ -150,8 +147,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 		if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
 			MutableKeySet old = outContexts.get(ks.getContactId());
 			if (old == null ||
-					old.getKeySetId().getInt() < ks.getKeySetId().getInt())
+					old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
 				outContexts.put(ks.getContactId(), ks);
+			}
 		}
 	}
 
@@ -177,20 +175,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 	}
 
 	@Override
-	public void addContact(Transaction txn, ContactId c, SecretKey master,
-			long timestamp, boolean alice) throws DbException {
-		deriveAndAddKeys(txn, c, master, timestamp, alice, true);
-	}
-
-	@Override
-	public KeySetId addUnboundKeys(Transaction txn, SecretKey master,
-			long timestamp, boolean alice) throws DbException {
-		return deriveAndAddKeys(txn, null, master, timestamp, alice, false);
-	}
-
-	private KeySetId deriveAndAddKeys(Transaction txn, @Nullable ContactId c,
-			SecretKey master, long timestamp, boolean alice, boolean active)
-			throws DbException {
+	public KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
+			long timestamp, boolean alice, boolean active) throws DbException {
 		lock.lock();
 		try {
 			// Work out what rotation period the timestamp belongs to
@@ -211,31 +197,12 @@ 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();
-			// Check that the keys haven't already been bound
-			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 activateKeys(Transaction txn, KeySetId k) throws DbException {
 		lock.lock();
 		try {
 			MutableKeySet ks = keys.get(k);
 			if (ks == null) throw new IllegalArgumentException();
-			// Check that the keys have been bound
-			if (ks.getContactId() == null) throw new IllegalArgumentException();
 			MutableTransportKeys m = ks.getTransportKeys();
 			m.getCurrentOutgoingKeys().activate();
 			considerReplacingOutgoingKeys(ks);
@@ -245,21 +212,6 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 		}
 	}
 
-	@Override
-	public void removeKeys(Transaction txn, KeySetId k) throws DbException {
-		lock.lock();
-		try {
-			MutableKeySet ks = keys.remove(k);
-			if (ks == null) throw new IllegalArgumentException();
-			// Check that the keys haven't been bound
-			if (ks.getContactId() != null) throw new IllegalArgumentException();
-			TransportId t = ks.getTransportKeys().getTransportId();
-			db.removeTransportKeys(txn, t, k);
-		} finally {
-			lock.unlock();
-		}
-	}
-
 	@Override
 	public void removeContact(ContactId c) {
 		lock.lock();
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
index 50c8305e90..a90d685b18 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
@@ -55,8 +55,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 			will(returnValue(txn));
 			oneOf(db).addContact(txn, remote, local, verified, active);
 			will(returnValue(contactId));
-			oneOf(keyManager)
-					.addContact(txn, contactId, master, timestamp, alice);
+			oneOf(keyManager).addContact(txn, contactId, master, timestamp,
+					alice, active);
 			oneOf(db).getContact(txn, contactId);
 			will(returnValue(contact));
 			oneOf(db).commitTransaction(txn);
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 1fec34ce9d..e16e03da0d 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
@@ -289,11 +289,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(16).of(database).startTransaction();
 			will(returnValue(txn));
-			exactly(17).of(database).containsContact(txn, contactId);
+			exactly(16).of(database).containsContact(txn, contactId);
 			will(returnValue(false));
-			exactly(17).of(database).abortTransaction(txn);
+			exactly(16).of(database).abortTransaction(txn);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
@@ -308,16 +308,6 @@ 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);
@@ -773,13 +763,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
 			// endTransaction()
 			oneOf(database).commitTransaction(txn);
 			// Check whether the transport is in the DB (which it's not)
-			exactly(6).of(database).startTransaction();
+			exactly(5).of(database).startTransaction();
 			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			exactly(6).of(database).containsTransport(txn, transportId);
+			exactly(5).of(database).containsTransport(txn, transportId);
 			will(returnValue(false));
-			exactly(6).of(database).abortTransaction(txn);
+			exactly(5).of(database).abortTransaction(txn);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
@@ -794,16 +782,6 @@ 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/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index c8dc8dccc6..2655e9ef7f 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -94,6 +94,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	private final TransportId transportId;
 	private final ContactId contactId;
 	private final KeySetId keySetId, keySetId1;
+	private final Random random = new Random();
 
 	JdbcDatabaseTest() throws Exception {
 		clientId = getClientId();
@@ -670,8 +671,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	@Test
 	public void testTransportKeys() throws Exception {
 		long rotationPeriod = 123, rotationPeriod1 = 234;
-		TransportKeys keys = createTransportKeys(rotationPeriod);
-		TransportKeys keys1 = createTransportKeys(rotationPeriod1);
+		boolean active = random.nextBoolean();
+		TransportKeys keys = createTransportKeys(rotationPeriod, active);
+		TransportKeys keys1 = createTransportKeys(rotationPeriod1, active);
 
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
@@ -682,7 +684,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		// Add the contact, the transport and the transport keys
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
-				true, true));
+				true, active));
 		db.addTransport(txn, transportId, 123);
 		assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
 		assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
@@ -701,8 +703,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		}
 
 		// Rotate the transport keys
-		TransportKeys rotated = createTransportKeys(rotationPeriod + 1);
-		TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1);
+		TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active);
+		TransportKeys rotated1 =
+				createTransportKeys(rotationPeriod1 + 1, active);
 		db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
 		db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1));
 
@@ -727,95 +730,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		db.close();
 	}
 
-	@Test
-	public void testUnboundTransportKeys() throws Exception {
-		long rotationPeriod = 123, rotationPeriod1 = 234;
-		TransportKeys keys = createTransportKeys(rotationPeriod);
-		TransportKeys keys1 = createTransportKeys(rotationPeriod1);
-
-		Database<Connection> db = open(false);
-		Connection txn = db.startTransaction();
-
-		// Initially there should be no transport keys in the database
-		assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
-
-		// Add the contact, the transport and the unbound transport keys
-		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
-				true, true));
-		db.addTransport(txn, transportId, 123);
-		assertEquals(keySetId, db.addTransportKeys(txn, null, keys));
-		assertEquals(keySetId1, db.addTransportKeys(txn, null, keys1));
-
-		// Retrieve the transport keys
-		Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId);
-		assertEquals(2, allKeys.size());
-		for (KeySet ks : allKeys) {
-			assertNull(ks.getContactId());
-			if (ks.getKeySetId().equals(keySetId)) {
-				assertKeysEquals(keys, ks.getTransportKeys());
-			} else {
-				assertEquals(keySetId1, ks.getKeySetId());
-				assertKeysEquals(keys1, ks.getTransportKeys());
-			}
-		}
-
-		// Bind the first set of transport keys
-		db.bindTransportKeys(txn, contactId, transportId, keySetId);
-
-		// Retrieve the keys again - the first set should be bound
-		allKeys = db.getTransportKeys(txn, transportId);
-		assertEquals(2, allKeys.size());
-		for (KeySet ks : allKeys) {
-			if (ks.getKeySetId().equals(keySetId)) {
-				assertEquals(contactId, ks.getContactId());
-				assertKeysEquals(keys, ks.getTransportKeys());
-			} else {
-				assertEquals(keySetId1, ks.getKeySetId());
-				assertNull(ks.getContactId());
-				assertKeysEquals(keys1, ks.getTransportKeys());
-			}
-		}
-
-		// Rotate the transport keys
-		TransportKeys rotated = createTransportKeys(rotationPeriod + 1);
-		TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1);
-		db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
-		db.updateTransportKeys(txn, new KeySet(keySetId1, null, rotated1));
-
-		// Retrieve the transport keys again
-		allKeys = db.getTransportKeys(txn, transportId);
-		assertEquals(2, allKeys.size());
-		for (KeySet ks : allKeys) {
-			if (ks.getKeySetId().equals(keySetId)) {
-				assertEquals(contactId, ks.getContactId());
-				assertKeysEquals(rotated, ks.getTransportKeys());
-			} else {
-				assertEquals(keySetId1, ks.getKeySetId());
-				assertNull(ks.getContactId());
-				assertKeysEquals(rotated1, ks.getTransportKeys());
-			}
-		}
-
-		// Remove the unbound transport keys
-		db.removeTransportKeys(txn, transportId, keySetId1);
-
-		// Retrieve the keys again - the second set should be gone
-		allKeys = db.getTransportKeys(txn, transportId);
-		assertEquals(1, allKeys.size());
-		KeySet ks = allKeys.iterator().next();
-		assertEquals(keySetId, ks.getKeySetId());
-		assertEquals(contactId, ks.getContactId());
-		assertKeysEquals(rotated, ks.getTransportKeys());
-
-		// Removing the transport should remove the remaining transport keys
-		db.removeTransport(txn, transportId);
-		assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
-
-		db.commitTransaction(txn);
-		db.close();
-	}
-
 	private void assertKeysEquals(TransportKeys expected,
 			TransportKeys actual) {
 		assertEquals(expected.getTransportId(), actual.getTransportId());
@@ -853,7 +767,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	@Test
 	public void testIncrementStreamCounter() throws Exception {
 		long rotationPeriod = 123;
-		TransportKeys keys = createTransportKeys(rotationPeriod);
+		TransportKeys keys = createTransportKeys(rotationPeriod, true);
 		long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
 
 		Database<Connection> db = open(false);
@@ -893,8 +807,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 
 	@Test
 	public void testSetReorderingWindow() throws Exception {
+		boolean active = random.nextBoolean();
 		long rotationPeriod = 123;
-		TransportKeys keys = createTransportKeys(rotationPeriod);
+		TransportKeys keys = createTransportKeys(rotationPeriod, active);
 		long base = keys.getCurrentIncomingKeys().getWindowBase();
 		byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
 
@@ -904,12 +819,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		// Add the contact, transport and transport keys
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
-				true, true));
+				true, active));
 		db.addTransport(txn, transportId, 123);
 		assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
 
 		// Update the reordering window and retrieve the transport keys
-		new Random().nextBytes(bitmap);
+		random.nextBytes(bitmap);
 		db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod,
 				base + 1, bitmap);
 		Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId);
@@ -1908,7 +1823,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		return db;
 	}
 
-	private TransportKeys createTransportKeys(long rotationPeriod) {
+	private TransportKeys createTransportKeys(long rotationPeriod,
+			boolean active) {
 		SecretKey inPrevTagKey = getSecretKey();
 		SecretKey inPrevHeaderKey = getSecretKey();
 		IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
@@ -1924,7 +1840,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		SecretKey outCurrTagKey = getSecretKey();
 		SecretKey outCurrHeaderKey = getSecretKey();
 		OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
-				rotationPeriod, 456, true);
+				rotationPeriod, 456, active);
 		return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
 	}
 
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 cc3b466b64..dd91eb2379 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
@@ -22,6 +22,7 @@ import org.junit.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Random;
 
 import static java.util.Collections.singletonList;
@@ -54,6 +55,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
 			new StreamContext(contactId, transportId, getSecretKey(),
 					getSecretKey(), 1);
 	private final byte[] tag = getRandomBytes(TAG_LENGTH);
+	private final Random random = new Random();
 
 	private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
 			pluginConfig, transportKeyManagerFactory);
@@ -102,30 +104,18 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
 	public void testAddContact() throws Exception {
 		SecretKey secretKey = getSecretKey();
 		long timestamp = System.currentTimeMillis();
-		boolean alice = new Random().nextBoolean();
+		boolean alice = random.nextBoolean();
+		boolean active = random.nextBoolean();
 
 		context.checking(new Expectations() {{
 			oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
-					timestamp, alice);
-		}});
-
-		keyManager.addContact(txn, contactId, secretKey, timestamp, alice);
-	}
-
-	@Test
-	public void testAddUnboundKeys() throws Exception {
-		SecretKey secretKey = getSecretKey();
-		long timestamp = System.currentTimeMillis();
-		boolean alice = new Random().nextBoolean();
-
-		context.checking(new Expectations() {{
-			oneOf(transportKeyManager).addUnboundKeys(txn, secretKey,
-					timestamp, alice);
+					timestamp, alice, active);
 			will(returnValue(keySetId));
 		}});
 
-		assertEquals(singletonMap(transportId, keySetId),
-				keyManager.addUnboundKeys(txn, secretKey, timestamp, alice));
+		Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
+				secretKey, timestamp, alice, active);
+		assertEquals(singletonMap(transportId, keySetId), ids);
 	}
 
 	@Test
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 8ae42e396c..cdba372a16 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
@@ -61,7 +61,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 	private final ContactId contactId1 = new ContactId(234);
 	private final KeySetId keySetId = new KeySetId(345);
 	private final KeySetId keySetId1 = new KeySetId(456);
-	private final KeySetId keySetId2 = new KeySetId(567);
 	private final SecretKey tagKey = TestUtils.getSecretKey();
 	private final SecretKey headerKey = TestUtils.getSecretKey();
 	private final SecretKey masterKey = TestUtils.getSecretKey();
@@ -71,14 +70,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 	public void testKeysAreRotatedAtStartup() throws Exception {
 		TransportKeys shouldRotate = createTransportKeys(900, 0, true);
 		TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
-		TransportKeys shouldRotate1 = createTransportKeys(999, 0, false);
 		Collection<KeySet> loaded = asList(
 				new KeySet(keySetId, contactId, shouldRotate),
-				new KeySet(keySetId1, contactId1, shouldNotRotate),
-				new KeySet(keySetId2, null, shouldRotate1)
+				new KeySet(keySetId1, contactId1, shouldNotRotate)
 		);
 		TransportKeys rotated = createTransportKeys(1000, 0, true);
-		TransportKeys rotated1 = createTransportKeys(1000, 0, false);
 		Transaction txn = new Transaction(null, false);
 
 		context.checking(new Expectations() {{
@@ -93,8 +89,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 			will(returnValue(rotated));
 			oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
 			will(returnValue(shouldNotRotate));
-			oneOf(transportCrypto).rotateTransportKeys(shouldRotate1, 1000);
-			will(returnValue(rotated1));
 			// Encode the tags (3 sets per contact)
 			for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
 				exactly(6).of(transportCrypto).encodeTag(
@@ -103,10 +97,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				will(new EncodeTagAction());
 			}
 			// Save the keys that were rotated
-			oneOf(db).updateTransportKeys(txn, asList(
-					new KeySet(keySetId, contactId, rotated),
-					new KeySet(keySetId2, null, rotated1))
-			);
+			oneOf(db).updateTransportKeys(txn,
+					singletonList(new KeySet(keySetId, contactId, rotated)));
 			// Schedule key rotation at the start of the next rotation period
 			oneOf(scheduler).schedule(with(any(Runnable.class)),
 					with(rotationPeriodLength - 1), with(MILLISECONDS));
@@ -153,43 +145,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				maxLatency);
 		// The timestamp is 1 ms before the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000 - 1;
-		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
-				alice);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, true));
 		assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
 	}
 
-	@Test
-	public void testKeysAreRotatedWhenAddingUnboundKeys() throws Exception {
-		boolean alice = random.nextBoolean();
-		TransportKeys transportKeys = createTransportKeys(999, 0, false);
-		TransportKeys rotated = createTransportKeys(1000, 0, false);
-		Transaction txn = new Transaction(null, false);
-
-		context.checking(new Expectations() {{
-			oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
-					999, alice, false);
-			will(returnValue(transportKeys));
-			// Get the current time (1 ms after start of rotation period 1000)
-			oneOf(clock).currentTimeMillis();
-			will(returnValue(rotationPeriodLength * 1000 + 1));
-			// Rotate the transport keys
-			oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
-			will(returnValue(rotated));
-			// Save the keys
-			oneOf(db).addTransportKeys(txn, null, rotated);
-			will(returnValue(keySetId));
-		}});
-
-		TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
-				db, transportCrypto, dbExecutor, scheduler, clock, transportId,
-				maxLatency);
-		// The timestamp is 1 ms before the start of rotation period 1000
-		long timestamp = rotationPeriodLength * 1000 - 1;
-		assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
-				masterKey, timestamp, alice));
-		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
-	}
-
 	@Test
 	public void testOutgoingStreamContextIsNullIfContactIsNotFound()
 			throws Exception {
@@ -211,15 +171,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				MAX_32_BIT_UNSIGNED + 1, true);
 		Transaction txn = new Transaction(null, false);
 
-		expectAddContactNoRotation(alice, transportKeys, txn);
+		expectAddContactNoRotation(alice, true, transportKeys, txn);
 
 		TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
 				db, transportCrypto, dbExecutor, scheduler, clock, transportId,
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
-				alice);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, true));
 		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
 		assertNull(transportKeyManager.getStreamContext(txn, contactId));
 	}
@@ -232,7 +192,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				MAX_32_BIT_UNSIGNED, true);
 		Transaction txn = new Transaction(null, false);
 
-		expectAddContactNoRotation(alice, transportKeys, txn);
+		expectAddContactNoRotation(alice, true, transportKeys, txn);
 
 		context.checking(new Expectations() {{
 			// Increment the stream counter
@@ -244,8 +204,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
-				alice);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, true));
 		// The first request should return a stream context
 		assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
 		StreamContext ctx = transportKeyManager.getStreamContext(txn,
@@ -265,19 +225,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 	public void testIncomingStreamContextIsNullIfTagIsNotFound()
 			throws Exception {
 		boolean alice = random.nextBoolean();
-		TransportKeys transportKeys = createTransportKeys(1000, 0, true);
+		boolean active = random.nextBoolean();
+		TransportKeys transportKeys = createTransportKeys(1000, 0, active);
 		Transaction txn = new Transaction(null, false);
 
-		expectAddContactNoRotation(alice, transportKeys, txn);
+		expectAddContactNoRotation(alice, active, transportKeys, txn);
 
 		TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
 				db, transportCrypto, dbExecutor, scheduler, clock, transportId,
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
-				alice);
-		assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, active));
+		assertEquals(active,
+				transportKeyManager.canSendOutgoingStreams(contactId));
 		// The tag should not be recognised
 		assertNull(transportKeyManager.getStreamContext(txn,
 				new byte[TAG_LENGTH]));
@@ -327,8 +289,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
-				alice);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, true));
 		assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
 		// Use the first tag (previous rotation period, stream number 0)
 		assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
@@ -415,23 +377,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 	}
 
 	@Test
-	public void testBindingAndActivatingKeys() throws Exception {
+	public void testActivatingKeys() throws Exception {
 		boolean alice = random.nextBoolean();
 		TransportKeys transportKeys = createTransportKeys(1000, 0, false);
 		Transaction txn = new Transaction(null, false);
 
-		expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
+		expectAddContactNoRotation(alice, false, transportKeys, txn);
 
 		context.checking(new Expectations() {{
-			// When the keys are bound, encode the tags (3 sets)
-			for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
-				exactly(3).of(transportCrypto).encodeTag(
-						with(any(byte[].class)), with(tagKey),
-						with(PROTOCOL_VERSION), with(i));
-				will(new EncodeTagAction());
-			}
-			// Save the key binding
-			oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId);
 			// Activate the keys
 			oneOf(db).setTransportKeysActive(txn, transportId, keySetId);
 			// Increment the stream counter
@@ -443,12 +396,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
-				masterKey, timestamp, alice));
-		// The keys are unbound so no stream context should be returned
-		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
-		assertNull(transportKeyManager.getStreamContext(txn, contactId));
-		transportKeyManager.bindKeys(txn, contactId, keySetId);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, false));
 		// The keys are inactive so no stream context should be returned
 		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
 		assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -474,18 +423,26 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 		// Keep a copy of the tags
 		List<byte[]> tags = new ArrayList<>();
 
-		expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
-
 		context.checking(new Expectations() {{
-			// When the keys are bound, encode the tags (3 sets)
+			oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
+					1000, alice, false);
+			will(returnValue(transportKeys));
+			// Get the current time (the start of rotation period 1000)
+			oneOf(clock).currentTimeMillis();
+			will(returnValue(rotationPeriodLength * 1000));
+			// Encode the tags (3 sets)
 			for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
 				exactly(3).of(transportCrypto).encodeTag(
 						with(any(byte[].class)), with(tagKey),
 						with(PROTOCOL_VERSION), with(i));
 				will(new EncodeTagAction(tags));
 			}
-			// Save the key binding
-			oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId);
+			// Rotate the transport keys (the keys are unaffected)
+			oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
+			will(returnValue(transportKeys));
+			// Save the keys
+			oneOf(db).addTransportKeys(txn, contactId, transportKeys);
+			will(returnValue(keySetId));
 			// Encode a new tag after sliding the window
 			oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
 					with(tagKey), with(PROTOCOL_VERSION),
@@ -505,9 +462,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 				maxLatency);
 		// The timestamp is at the start of rotation period 1000
 		long timestamp = rotationPeriodLength * 1000;
-		assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
-				masterKey, timestamp, alice));
-		transportKeyManager.bindKeys(txn, contactId, keySetId);
+		assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
+				masterKey, timestamp, alice, false));
 		// The keys are inactive so no stream context should be returned
 		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
 		assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -532,36 +488,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 		assertEquals(0L, ctx.getStreamNumber());
 	}
 
-	@Test
-	public void testRemovingUnboundKeys() throws Exception {
-		boolean alice = random.nextBoolean();
-		TransportKeys transportKeys = createTransportKeys(1000, 0, false);
-		Transaction txn = new Transaction(null, false);
-
-		expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
-
-		context.checking(new Expectations() {{
-			// Remove the unbound keys
-			oneOf(db).removeTransportKeys(txn, transportId, keySetId);
-		}});
-
-		TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
-				db, transportCrypto, dbExecutor, scheduler, clock, transportId,
-				maxLatency);
-		// The timestamp is at the start of rotation period 1000
-		long timestamp = rotationPeriodLength * 1000;
-		assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
-				masterKey, timestamp, alice));
-		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
-		transportKeyManager.removeKeys(txn, keySetId);
-		assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
-	}
-
-	private void expectAddContactNoRotation(boolean alice,
+	private void expectAddContactNoRotation(boolean alice, boolean active,
 			TransportKeys transportKeys, Transaction txn) throws Exception {
 		context.checking(new Expectations() {{
 			oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
-					1000, alice, true);
+					1000, alice, active);
 			will(returnValue(transportKeys));
 			// Get the current time (the start of rotation period 1000)
 			oneOf(clock).currentTimeMillis();
@@ -582,24 +513,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
 		}});
 	}
 
-	private void expectAddUnboundKeysNoRotation(boolean alice,
-			TransportKeys transportKeys, Transaction txn) throws Exception {
-		context.checking(new Expectations() {{
-			oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
-					1000, alice, false);
-			will(returnValue(transportKeys));
-			// Get the current time (the start of rotation period 1000)
-			oneOf(clock).currentTimeMillis();
-			will(returnValue(rotationPeriodLength * 1000));
-			// Rotate the transport keys (the keys are unaffected)
-			oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
-			will(returnValue(transportKeys));
-			// Save the unbound keys
-			oneOf(db).addTransportKeys(txn, null, transportKeys);
-			will(returnValue(keySetId));
-		}});
-	}
-
 	private TransportKeys createTransportKeys(long rotationPeriod,
 			long streamCounter, boolean active) {
 		IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index 34578f310c..66c4c985e1 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -453,12 +453,11 @@ class IntroduceeProtocolEngine
 					.getContact(txn, s.getRemote().author.getId(),
 							localAuthor.getId());
 
-			// bind the keys to the new contact
+			// add the keys to the new contact
 			//noinspection ConstantConditions
 			keys = keyManager
-					.addUnboundKeys(txn, new SecretKey(s.getMasterKey()),
-							timestamp, s.getLocal().alice);
-			keyManager.bindKeys(txn, c.getId(), keys);
+					.addContact(txn, c.getId(), new SecretKey(s.getMasterKey()),
+							timestamp, s.getLocal().alice, false);
 
 			// add signed transport properties for the contact
 			//noinspection ConstantConditions
-- 
GitLab