mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 21:29:54 +01:00
Add key manager methods for pending contacts.
This commit is contained in:
@@ -6,6 +6,7 @@ import org.briarproject.bramble.util.StringUtils;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +39,7 @@ public class Bytes implements Comparable<Bytes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
|
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +29,7 @@ public class ContactId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof ContactId && id == ((ContactId) o).id;
|
return o instanceof ContactId && id == ((ContactId) o).id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ public class PendingContactId extends UniqueId {
|
|||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof PendingContactId && super.equals(o);
|
return o instanceof PendingContactId && super.equals(o);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.transport;
|
package org.briarproject.bramble.api.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -39,11 +40,26 @@ public interface KeyManager {
|
|||||||
* {@link StreamContext StreamContexts} for the contact can be created
|
* {@link StreamContext StreamContexts} for the contact can be created
|
||||||
* after this method has returned.
|
* after this method has returned.
|
||||||
*
|
*
|
||||||
* @param alice True if the local party is ALice
|
* @param alice True if the local party is Alice
|
||||||
*/
|
*/
|
||||||
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||||
SecretKey rootKey, boolean alice) throws DbException;
|
SecretKey rootKey, boolean alice) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the key manager that a new pending contact has been added.
|
||||||
|
* Derives and stores a set of handshake mode transport keys for
|
||||||
|
* communicating with the pending contact over each transport and returns
|
||||||
|
* the key set IDs.
|
||||||
|
* <p/>
|
||||||
|
* {@link StreamContext StreamContexts} for the pending contact can be
|
||||||
|
* created after this method has returned.
|
||||||
|
*
|
||||||
|
* @param alice True if the local party is Alice
|
||||||
|
*/
|
||||||
|
Map<TransportId, KeySetId> addPendingContact(Transaction txn,
|
||||||
|
PendingContactId p, SecretKey rootKey, boolean alice)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
@@ -56,15 +72,28 @@ public interface KeyManager {
|
|||||||
*/
|
*/
|
||||||
boolean canSendOutgoingStreams(ContactId c, TransportId t);
|
boolean canSendOutgoingStreams(ContactId c, TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if we have keys that can be used for outgoing streams to
|
||||||
|
* the given pending contact over the given transport.
|
||||||
|
*/
|
||||||
|
boolean canSendOutgoingStreams(PendingContactId p, TransportId t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link StreamContext} for sending a stream to the given
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
* contact over the given transport, or null if an error occurs or the
|
* contact over the given transport, or null if an error occurs.
|
||||||
* contact does not support the transport.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(ContactId c, TransportId t)
|
StreamContext getStreamContext(ContactId c, TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
|
* pending contact over the given transport, or null if an error occurs.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
StreamContext getStreamContext(PendingContactId p, TransportId t)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the given tag and returns a {@link StreamContext} for reading
|
* Looks up the given tag and returns a {@link StreamContext} for reading
|
||||||
* from the corresponding stream, or null if an error occurs or the tag was
|
* from the corresponding stream, or null if an error occurs or the tag was
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -112,6 +113,19 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
return ids;
|
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
|
@Override
|
||||||
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -132,6 +146,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
return m != null && m.canSendOutgoingStreams(c);
|
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
|
@Override
|
||||||
public StreamContext getStreamContext(ContactId c, TransportId t)
|
public StreamContext getStreamContext(ContactId c, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -144,6 +164,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
m.getStreamContext(txn, c));
|
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
|
@Override
|
||||||
public StreamContext getStreamContext(TransportId t, byte[] tag)
|
public StreamContext getStreamContext(TransportId t, byte[] tag)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -21,16 +22,27 @@ interface TransportKeyManager {
|
|||||||
KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
|
KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey,
|
||||||
boolean alice) throws DbException;
|
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 activateKeys(Transaction txn, KeySetId k) throws DbException;
|
||||||
|
|
||||||
void removeContact(ContactId c);
|
void removeContact(ContactId c);
|
||||||
|
|
||||||
|
void removePendingContact(PendingContactId p);
|
||||||
|
|
||||||
boolean canSendOutgoingStreams(ContactId c);
|
boolean canSendOutgoingStreams(ContactId c);
|
||||||
|
|
||||||
|
boolean canSendOutgoingStreams(PendingContactId p);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(Transaction txn, ContactId c)
|
StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
StreamContext getStreamContext(Transaction txn, PendingContactId p)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(Transaction txn, byte[] tag)
|
StreamContext getStreamContext(Transaction txn, byte[] tag)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|||||||
@@ -64,8 +64,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final Map<ContactId, MutableTransportKeySet> outContexts =
|
private final Map<ContactId, MutableTransportKeySet>
|
||||||
new HashMap<>();
|
contactOutContexts = new HashMap<>();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<PendingContactId, MutableTransportKeySet>
|
||||||
|
pendingContactOutContexts = new HashMap<>();
|
||||||
|
|
||||||
TransportKeyManagerImpl(DatabaseComponent db,
|
TransportKeyManagerImpl(DatabaseComponent db,
|
||||||
TransportCrypto transportCrypto, Executor dbExecutor,
|
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||||
@@ -162,19 +165,29 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
|
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
|
||||||
// Use the active outgoing keys with the highest key set ID
|
// Use the active outgoing keys with the highest key set ID, preferring
|
||||||
ContactId c = ks.getContactId();
|
// rotation keys to handshake keys
|
||||||
if (c != null && ks.getKeys().getCurrentOutgoingKeys().isActive()) {
|
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) {
|
||||||
MutableTransportKeySet old = outContexts.get(c);
|
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(),
|
||||||
if (old == null ||
|
ks.getPendingContactId());
|
||||||
(old.getKeys().isHandshakeMode() &&
|
if (old == null || (old.getKeys().isHandshakeMode() &&
|
||||||
!ks.getKeys().isHandshakeMode()) ||
|
!ks.getKeys().isHandshakeMode()) ||
|
||||||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
|
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) {
|
private void scheduleKeyUpdate(long now) {
|
||||||
long delay = timePeriodLength - now % timePeriodLength;
|
long delay = timePeriodLength - now % timePeriodLength;
|
||||||
scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS);
|
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
|
@Override
|
||||||
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
|
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@@ -254,8 +287,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
try {
|
try {
|
||||||
// Remove mutable state for the contact
|
// Remove mutable state for the contact
|
||||||
Iterator<TagContext> it = inContexts.values().iterator();
|
Iterator<TagContext> it = inContexts.values().iterator();
|
||||||
while (it.hasNext()) if (c.equals(it.next().contactId)) it.remove();
|
while (it.hasNext())
|
||||||
outContexts.remove(c);
|
if (c.equals(it.next().contactId)) it.remove();
|
||||||
|
contactOutContexts.remove(c);
|
||||||
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
|
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
|
||||||
while (it1.hasNext())
|
while (it1.hasNext())
|
||||||
if (c.equals(it1.next().getContactId())) it1.remove();
|
if (c.equals(it1.next().getContactId())) it1.remove();
|
||||||
@@ -265,13 +299,39 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canSendOutgoingStreams(ContactId c) {
|
public void removePendingContact(PendingContactId p) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
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;
|
if (ks == null) return false;
|
||||||
MutableOutgoingKeys outKeys =
|
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys();
|
||||||
ks.getKeys().getCurrentOutgoingKeys();
|
|
||||||
if (!outKeys.isActive()) throw new AssertionError();
|
if (!outKeys.isActive()) throw new AssertionError();
|
||||||
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
|
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -282,17 +342,30 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
@Override
|
@Override
|
||||||
public StreamContext getStreamContext(Transaction txn, ContactId c)
|
public StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
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();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// Look up the outgoing keys for the contact
|
// Look up the outgoing keys for the contact
|
||||||
MutableTransportKeySet ks = outContexts.get(c);
|
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
|
||||||
if (ks == null) return null;
|
if (ks == null) return null;
|
||||||
MutableTransportKeys keys = ks.getKeys();
|
MutableTransportKeys keys = ks.getKeys();
|
||||||
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
|
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
|
||||||
if (!outKeys.isActive()) throw new AssertionError();
|
if (!outKeys.isActive()) throw new AssertionError();
|
||||||
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
|
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
|
||||||
// Create a stream context
|
// Create a stream context
|
||||||
StreamContext ctx = new StreamContext(c, null, transportId,
|
StreamContext ctx = new StreamContext(c, p, transportId,
|
||||||
outKeys.getTagKey(), outKeys.getHeaderKey(),
|
outKeys.getTagKey(), outKeys.getHeaderKey(),
|
||||||
outKeys.getStreamCounter(), keys.isHandshakeMode());
|
outKeys.getStreamCounter(), keys.isHandshakeMode());
|
||||||
// Increment the stream counter and write it back to the DB
|
// Increment the stream counter and write it back to the DB
|
||||||
@@ -373,7 +446,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
UpdateResult updateResult = updateKeys(snapshot, now);
|
UpdateResult updateResult = updateKeys(snapshot, now);
|
||||||
// Rebuild the mutable state for all contacts
|
// Rebuild the mutable state for all contacts
|
||||||
inContexts.clear();
|
inContexts.clear();
|
||||||
outContexts.clear();
|
contactOutContexts.clear();
|
||||||
|
pendingContactOutContexts.clear();
|
||||||
keys.clear();
|
keys.clear();
|
||||||
addKeys(updateResult.current);
|
addKeys(updateResult.current);
|
||||||
// Write any updated keys back to the DB
|
// Write any updated keys back to the DB
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -26,6 +27,7 @@ import static java.util.Collections.singletonMap;
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -43,11 +45,17 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final DeterministicExecutor executor = new DeterministicExecutor();
|
private final DeterministicExecutor executor = new DeterministicExecutor();
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
private final ContactId contactId = getContactId();
|
private final ContactId contactId = getContactId();
|
||||||
|
private final PendingContactId pendingContactId =
|
||||||
|
new PendingContactId(getRandomId());
|
||||||
private final KeySetId keySetId = new KeySetId(345);
|
private final KeySetId keySetId = new KeySetId(345);
|
||||||
private final TransportId transportId = getTransportId();
|
private final TransportId transportId = getTransportId();
|
||||||
private final TransportId unknownTransportId = getTransportId();
|
private final TransportId unknownTransportId = getTransportId();
|
||||||
private final StreamContext streamContext = new StreamContext(contactId,
|
private final StreamContext contactStreamContext =
|
||||||
null, transportId, getSecretKey(), getSecretKey(), 1, false);
|
new StreamContext(contactId, null, transportId, getSecretKey(),
|
||||||
|
getSecretKey(), 1, false);
|
||||||
|
private final StreamContext pendingContactStreamContext =
|
||||||
|
new StreamContext(null, pendingContactId, transportId,
|
||||||
|
getSecretKey(), getSecretKey(), 1, true);
|
||||||
private final byte[] tag = getRandomBytes(TAG_LENGTH);
|
private final byte[] tag = getRandomBytes(TAG_LENGTH);
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
@@ -117,23 +125,60 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForUnknownTransport() throws Exception {
|
public void testAddPendingContact() throws Exception {
|
||||||
|
SecretKey secretKey = getSecretKey();
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(transportKeyManager).addPendingContact(txn, pendingContactId,
|
||||||
|
secretKey, alice);
|
||||||
|
will(returnValue(keySetId));
|
||||||
|
}});
|
||||||
|
|
||||||
|
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
|
||||||
|
pendingContactId, secretKey, alice);
|
||||||
|
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStreamContextForContactWithUnknownTransport()
|
||||||
|
throws Exception {
|
||||||
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
|
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStreamContextForPendingContactWithUnknownTransport()
|
||||||
|
throws Exception {
|
||||||
|
assertNull(keyManager.getStreamContext(pendingContactId,
|
||||||
|
unknownTransportId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForContact() throws Exception {
|
public void testGetStreamContextForContact() throws Exception {
|
||||||
context.checking(new DbExpectations() {{
|
context.checking(new DbExpectations() {{
|
||||||
oneOf(db).transactionWithNullableResult(with(false),
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
withNullableDbCallable(txn));
|
withNullableDbCallable(txn));
|
||||||
oneOf(transportKeyManager).getStreamContext(txn, contactId);
|
oneOf(transportKeyManager).getStreamContext(txn, contactId);
|
||||||
will(returnValue(streamContext));
|
will(returnValue(contactStreamContext));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(streamContext,
|
assertEquals(contactStreamContext,
|
||||||
keyManager.getStreamContext(contactId, transportId));
|
keyManager.getStreamContext(contactId, transportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStreamContextForPendingContact() throws Exception {
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
|
withNullableDbCallable(txn));
|
||||||
|
oneOf(transportKeyManager).getStreamContext(txn, pendingContactId);
|
||||||
|
will(returnValue(pendingContactStreamContext));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(pendingContactStreamContext,
|
||||||
|
keyManager.getStreamContext(pendingContactId, transportId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForTagAndUnknownTransport()
|
public void testGetStreamContextForTagAndUnknownTransport()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -146,10 +191,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).transactionWithNullableResult(with(false),
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
withNullableDbCallable(txn));
|
withNullableDbCallable(txn));
|
||||||
oneOf(transportKeyManager).getStreamContext(txn, tag);
|
oneOf(transportKeyManager).getStreamContext(txn, tag);
|
||||||
will(returnValue(streamContext));
|
will(returnValue(contactStreamContext));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(streamContext,
|
assertEquals(contactStreamContext,
|
||||||
keyManager.getStreamContext(transportId, tag));
|
keyManager.getStreamContext(transportId, tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -37,6 +38,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
@@ -61,6 +63,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
||||||
private final ContactId contactId = getContactId();
|
private final ContactId contactId = getContactId();
|
||||||
private final ContactId contactId1 = getContactId();
|
private final ContactId contactId1 = getContactId();
|
||||||
|
private final PendingContactId pendingContactId =
|
||||||
|
new PendingContactId(getRandomId());
|
||||||
private final KeySetId keySetId = new KeySetId(345);
|
private final KeySetId keySetId = new KeySetId(345);
|
||||||
private final KeySetId keySetId1 = new KeySetId(456);
|
private final KeySetId keySetId1 = new KeySetId(456);
|
||||||
private final SecretKey tagKey = getSecretKey();
|
private final SecretKey tagKey = getSecretKey();
|
||||||
@@ -162,7 +166,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
public void testHandshakeKeysAreDerivedWhenAddingContact()
|
public void testHandshakeKeysAreDerivedWhenAddingContact()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
TransportKeys transportKeys = createTransportKeys(1000, 0, true);
|
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -193,6 +197,42 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreDerivedWhenAddingPendingContact()
|
||||||
|
throws Exception {
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Get the current time (1 ms after start of time period 1000)
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(timePeriodLength * 1000 + 1));
|
||||||
|
// Derive the transport keys
|
||||||
|
oneOf(transportCrypto).deriveHandshakeKeys(transportId, rootKey,
|
||||||
|
1000, alice);
|
||||||
|
will(returnValue(transportKeys));
|
||||||
|
// 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 keys
|
||||||
|
oneOf(db).addTransportKeys(txn, pendingContactId, transportKeys);
|
||||||
|
will(returnValue(keySetId));
|
||||||
|
}});
|
||||||
|
|
||||||
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
|
maxLatency);
|
||||||
|
assertEquals(keySetId, transportKeyManager.addPendingContact(txn,
|
||||||
|
pendingContactId, rootKey, alice));
|
||||||
|
assertTrue(transportKeyManager.canSendOutgoingStreams(
|
||||||
|
pendingContactId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -205,6 +245,19 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOutgoingStreamContextIsNullIfPendingContactIsNotFound()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
|
maxLatency);
|
||||||
|
assertNull(transportKeyManager.getStreamContext(txn, pendingContactId));
|
||||||
|
assertFalse(transportKeyManager.canSendOutgoingStreams(
|
||||||
|
pendingContactId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -565,6 +618,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
private TransportKeys createHandshakeKeys(long timePeriod,
|
||||||
|
long streamCounter, boolean alice) {
|
||||||
|
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod - 1);
|
||||||
|
IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod);
|
||||||
|
IncomingKeys inNext = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod + 1);
|
||||||
|
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
||||||
|
timePeriod, streamCounter, true);
|
||||||
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
||||||
|
rootKey, alice);
|
||||||
|
}
|
||||||
|
|
||||||
private class EncodeTagAction implements Action {
|
private class EncodeTagAction implements Action {
|
||||||
|
|
||||||
private final Collection<byte[]> tags;
|
private final Collection<byte[]> tags;
|
||||||
|
|||||||
Reference in New Issue
Block a user