Add key manager methods for pending contacts.

This commit is contained in:
akwizgran
2019-05-09 13:07:15 +01:00
parent f42fc5213e
commit dd50f4bcd4
9 changed files with 294 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -112,6 +113,19 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ids;
}
@Override
public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice)
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.addPendingContact(txn, p, rootKey, alice));
}
return ids;
}
@Override
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException {
@@ -132,6 +146,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return m != null && m.canSendOutgoingStreams(c);
}
@Override
public boolean canSendOutgoingStreams(PendingContactId p, TransportId t) {
TransportKeyManager m = managers.get(t);
return m != null && m.canSendOutgoingStreams(p);
}
@Override
public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException {
@@ -144,6 +164,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
m.getStreamContext(txn, c));
}
@Override
public StreamContext getStreamContext(PendingContactId p, TransportId t)
throws DbException {
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
return db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, p));
}
@Override
public StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException {

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -21,16 +22,27 @@ interface TransportKeyManager {
KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
boolean alice) throws DbException;
KeySetId addPendingContact(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException;
void removeContact(ContactId c);
void removePendingContact(PendingContactId p);
boolean canSendOutgoingStreams(ContactId c);
boolean canSendOutgoingStreams(PendingContactId p);
@Nullable
StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException;
@Nullable
StreamContext getStreamContext(Transaction txn, PendingContactId p)
throws DbException;
@Nullable
StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException;

View File

@@ -64,8 +64,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@GuardedBy("lock")
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
@GuardedBy("lock")
private final Map<ContactId, MutableTransportKeySet> outContexts =
new HashMap<>();
private final Map<ContactId, MutableTransportKeySet>
contactOutContexts = new HashMap<>();
@GuardedBy("lock")
private final Map<PendingContactId, MutableTransportKeySet>
pendingContactOutContexts = new HashMap<>();
TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, Executor dbExecutor,
@@ -162,19 +165,29 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@GuardedBy("lock")
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
// Use the active outgoing keys with the highest key set ID
ContactId c = ks.getContactId();
if (c != null && ks.getKeys().getCurrentOutgoingKeys().isActive()) {
MutableTransportKeySet old = outContexts.get(c);
if (old == null ||
(old.getKeys().isHandshakeMode() &&
!ks.getKeys().isHandshakeMode()) ||
// Use the active outgoing keys with the highest key set ID, preferring
// rotation keys to handshake keys
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) {
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(),
ks.getPendingContactId());
if (old == null || (old.getKeys().isHandshakeMode() &&
!ks.getKeys().isHandshakeMode()) ||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
outContexts.put(c, ks);
if (ks.getContactId() == null)
pendingContactOutContexts.put(ks.getPendingContactId(), ks);
else contactOutContexts.put(ks.getContactId(), ks);
}
}
}
@GuardedBy("lock")
@Nullable
private MutableTransportKeySet getOutgoingKeySet(@Nullable ContactId c,
@Nullable PendingContactId p) {
if (c == null) return pendingContactOutContexts.get(p);
else return contactOutContexts.get(c);
}
private void scheduleKeyUpdate(long now) {
long delay = timePeriodLength - now % timePeriodLength;
scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS);
@@ -233,6 +246,26 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
}
@Override
public KeySetId addPendingContact(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException {
lock.lock();
try {
// Work out what time period we're in
long timePeriod = clock.currentTimeMillis() / timePeriodLength;
// Derive the transport keys
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, timePeriod, alice);
// Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, p, k);
// Initialise mutable state for the pending contact
addKeys(keySetId, null, p, new MutableTransportKeys(k));
return keySetId;
} finally {
lock.unlock();
}
}
@Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
lock.lock();
@@ -254,8 +287,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
try {
// Remove mutable state for the contact
Iterator<TagContext> it = inContexts.values().iterator();
while (it.hasNext()) if (c.equals(it.next().contactId)) it.remove();
outContexts.remove(c);
while (it.hasNext())
if (c.equals(it.next().contactId)) it.remove();
contactOutContexts.remove(c);
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
while (it1.hasNext())
if (c.equals(it1.next().getContactId())) it1.remove();
@@ -265,13 +299,39 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
@Override
public boolean canSendOutgoingStreams(ContactId c) {
public void removePendingContact(PendingContactId p) {
lock.lock();
try {
MutableTransportKeySet ks = outContexts.get(c);
// Remove mutable state for the pending contact
Iterator<TagContext> it = inContexts.values().iterator();
while (it.hasNext())
if (p.equals(it.next().pendingContactId)) it.remove();
pendingContactOutContexts.remove(p);
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
while (it1.hasNext())
if (p.equals(it1.next().getPendingContactId())) it1.remove();
} finally {
lock.unlock();
}
}
@Override
public boolean canSendOutgoingStreams(ContactId c) {
return canSendOutgoingStreams(c, null);
}
@Override
public boolean canSendOutgoingStreams(PendingContactId p) {
return canSendOutgoingStreams(null, p);
}
private boolean canSendOutgoingStreams(@Nullable ContactId c,
@Nullable PendingContactId p) {
lock.lock();
try {
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
if (ks == null) return false;
MutableOutgoingKeys outKeys =
ks.getKeys().getCurrentOutgoingKeys();
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError();
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
} finally {
@@ -282,17 +342,30 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override
public StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException {
return getStreamContext(txn, c, null);
}
@Override
public StreamContext getStreamContext(Transaction txn, PendingContactId p)
throws DbException {
return getStreamContext(txn, null, p);
}
@Nullable
private StreamContext getStreamContext(Transaction txn,
@Nullable ContactId c, @Nullable PendingContactId p)
throws DbException {
lock.lock();
try {
// Look up the outgoing keys for the contact
MutableTransportKeySet ks = outContexts.get(c);
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
if (ks == null) return null;
MutableTransportKeys keys = ks.getKeys();
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError();
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
// Create a stream context
StreamContext ctx = new StreamContext(c, null, transportId,
StreamContext ctx = new StreamContext(c, p, transportId,
outKeys.getTagKey(), outKeys.getHeaderKey(),
outKeys.getStreamCounter(), keys.isHandshakeMode());
// Increment the stream counter and write it back to the DB
@@ -373,7 +446,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
UpdateResult updateResult = updateKeys(snapshot, now);
// Rebuild the mutable state for all contacts
inContexts.clear();
outContexts.clear();
contactOutContexts.clear();
pendingContactOutContexts.clear();
keys.clear();
addKeys(updateResult.current);
// Write any updated keys back to the DB