diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java index a77f46157..6db16f7d5 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java @@ -1,7 +1,6 @@ package org.briarproject.bramble.api.crypto; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.transport.HandshakeKeys; import org.briarproject.bramble.api.transport.TransportKeys; /** @@ -11,35 +10,29 @@ import org.briarproject.bramble.api.transport.TransportKeys; public interface TransportCrypto { /** - * Derives initial transport keys for the given transport in the given - * time period from the given root key. + * Derives initial rotation mode transport keys for the given transport in + * the given time period from the given root key. * - * @param alice whether the keys are for use by Alice or Bob. - * @param active whether the keys are usable for outgoing streams. + * @param alice Whether the keys are for use by Alice or Bob + * @param active Whether the keys are usable for outgoing streams */ - TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey, + TransportKeys deriveRotationKeys(TransportId t, SecretKey rootKey, long timePeriod, boolean alice, boolean active); - /** - * Rotates the given transport keys to the given time period. If the keys - * are for the given period or any later period they are not rotated. - */ - TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod); - /** * Derives handshake keys for the given transport in the given time period * from the given root key. * - * @param alice whether the keys are for use by Alice or Bob. + * @param alice Whether the keys are for use by Alice or Bob */ - HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, + TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, long timePeriod, boolean alice); /** - * Updates the given handshake keys to the given time period. If the keys + * Updates the given transport keys to the given time period. If the keys * are for the given period or any later period they are not updated. */ - HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod); + TransportKeys updateTransportKeys(TransportKeys k, long timePeriod); /** * Encodes the pseudo-random tag that is used to recognise a stream. 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 5a2eaa719..1f97269d2 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 @@ -24,11 +24,8 @@ import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.validation.MessageState; -import org.briarproject.bramble.api.transport.HandshakeKeySet; -import org.briarproject.bramble.api.transport.HandshakeKeySetId; -import org.briarproject.bramble.api.transport.HandshakeKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import java.util.Collection; @@ -115,20 +112,6 @@ public interface DatabaseComponent { */ void addGroup(Transaction txn, Group g) throws DbException; - /** - * Stores the given handshake keys for the given contact and returns a - * key set ID. - */ - HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c, - HandshakeKeys k) throws DbException; - - /** - * Stores the given handshake keys for the given pending contact and - * returns a key set ID. - */ - HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p, - HandshakeKeys k) throws DbException; - /** * Stores an identity. */ @@ -156,7 +139,14 @@ public interface DatabaseComponent { * Stores the given transport keys for the given contact and returns a * key set ID. */ - TransportKeySetId addTransportKeys(Transaction txn, ContactId c, + KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeys k) + throws DbException; + + /** + * Stores the given transport keys for the given pending contact and + * returns a key set ID. + */ + KeySetId addTransportKeys(Transaction txn, PendingContactId p, TransportKeys k) throws DbException; /** @@ -276,7 +266,7 @@ public interface DatabaseComponent { *

* Read-only. */ - Collection getContacts(Transaction txn, AuthorId a) + Collection getContacts(Transaction txn, AuthorId local) throws DbException; /** @@ -310,14 +300,6 @@ public interface DatabaseComponent { Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g) throws DbException; - /** - * Returns all handshake keys for the given transport. - *

- * Read-only. - */ - Collection getHandshakeKeys(Transaction txn, TransportId t) - throws DbException; - /** * Returns the identity for the local pseudonym with the given ID. *

@@ -489,17 +471,11 @@ public interface DatabaseComponent { Collection getTransportKeys(Transaction txn, TransportId t) throws DbException; - /** - * Increments the outgoing stream counter for the given handshake keys. - */ - void incrementStreamCounter(Transaction txn, TransportId t, - HandshakeKeySetId k) throws DbException; - /** * Increments the outgoing stream counter for the given transport keys. */ - void incrementStreamCounter(Transaction txn, TransportId t, - TransportKeySetId k) throws DbException; + void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k) + throws DbException; /** * Merges the given metadata with the existing metadata for the given @@ -554,12 +530,6 @@ public interface DatabaseComponent { */ void removeGroup(Transaction txn, Group g) throws DbException; - /** - * Removes the given handshake keys from the database. - */ - void removeHandshakeKeys(Transaction txn, TransportId t, - HandshakeKeySetId k) throws DbException; - /** * Removes an identity (and all associated state) from the database. */ @@ -584,8 +554,8 @@ public interface DatabaseComponent { /** * Removes the given transport keys from the database. */ - void removeTransportKeys(Transaction txn, TransportId t, - TransportKeySetId k) throws DbException; + void removeTransportKeys(Transaction txn, TransportId t, KeySetId k) + throws DbException; /** * Marks the given contact as verified. @@ -628,31 +598,16 @@ public interface DatabaseComponent { PublicKey publicKey, PrivateKey privateKey) throws DbException; /** - * Sets the reordering window for the given transport key set in the given + * Sets the reordering window for the given transport keys in the given * time period. */ - void setReorderingWindow(Transaction txn, TransportKeySetId k, - TransportId t, long timePeriod, long base, byte[] bitmap) - throws DbException; - - /** - * Sets the reordering window for the given handshake key set in the given - * time period. - */ - void setReorderingWindow(Transaction txn, HandshakeKeySetId k, - TransportId t, long timePeriod, long base, byte[] bitmap) - throws DbException; + void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, + long timePeriod, long base, byte[] bitmap) throws DbException; /** * Marks the given transport keys as usable for outgoing streams. */ - void setTransportKeysActive(Transaction txn, TransportId t, - TransportKeySetId k) throws DbException; - - /** - * Stores the given handshake keys, deleting any keys they have replaced. - */ - void updateHandshakeKeys(Transaction txn, Collection keys) + void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k) throws DbException; /** diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/AbstractTransportKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/AbstractTransportKeys.java deleted file mode 100644 index d054983d2..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/AbstractTransportKeys.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.briarproject.bramble.api.transport; - -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.TransportId; - -import javax.annotation.concurrent.Immutable; - -/** - * Abstract superclass for {@link TransportKeys} and {@link HandshakeKeys}. - */ -@Immutable -@NotNullByDefault -public abstract class AbstractTransportKeys { - - private final TransportId transportId; - private final IncomingKeys inPrev, inCurr, inNext; - private final OutgoingKeys outCurr; - - AbstractTransportKeys(TransportId transportId, IncomingKeys inPrev, - IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { - if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1) - throw new IllegalArgumentException(); - if (inCurr.getTimePeriod() != outCurr.getTimePeriod()) - throw new IllegalArgumentException(); - if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1) - throw new IllegalArgumentException(); - this.transportId = transportId; - this.inPrev = inPrev; - this.inCurr = inCurr; - this.inNext = inNext; - this.outCurr = outCurr; - } - - public TransportId getTransportId() { - return transportId; - } - - public IncomingKeys getPreviousIncomingKeys() { - return inPrev; - } - - public IncomingKeys getCurrentIncomingKeys() { - return inCurr; - } - - public IncomingKeys getNextIncomingKeys() { - return inNext; - } - - public OutgoingKeys getCurrentOutgoingKeys() { - return outCurr; - } - - public long getTimePeriod() { - return outCurr.getTimePeriod(); - } -} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySet.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySet.java deleted file mode 100644 index cb40da1da..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySet.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.briarproject.bramble.api.transport; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.contact.PendingContactId; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * A set of keys for handshaking with a given contact or pending contact over a - * given transport. Unlike a {@link TransportKeySet} these keys do not provide - * forward secrecy. - */ -@Immutable -@NotNullByDefault -public class HandshakeKeySet { - - private final HandshakeKeySetId keySetId; - @Nullable - private final ContactId contactId; - @Nullable - private final PendingContactId pendingContactId; - private final HandshakeKeys keys; - - public HandshakeKeySet(HandshakeKeySetId keySetId, ContactId contactId, - HandshakeKeys keys) { - this.keySetId = keySetId; - this.contactId = contactId; - this.keys = keys; - pendingContactId = null; - } - - public HandshakeKeySet(HandshakeKeySetId keySetId, - PendingContactId pendingContactId, HandshakeKeys keys) { - this.keySetId = keySetId; - this.pendingContactId = pendingContactId; - this.keys = keys; - contactId = null; - } - - public HandshakeKeySetId getKeySetId() { - return keySetId; - } - - @Nullable - public ContactId getContactId() { - return contactId; - } - - @Nullable - public PendingContactId getPendingContactId() { - return pendingContactId; - } - - public HandshakeKeys getKeys() { - return keys; - } - - @Override - public int hashCode() { - return keySetId.hashCode(); - } - - @Override - public boolean equals(Object o) { - return o instanceof HandshakeKeySet && - keySetId.equals(((HandshakeKeySet) o).keySetId); - } -} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeys.java deleted file mode 100644 index 4a27e9adc..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeys.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.briarproject.bramble.api.transport; - -import org.briarproject.bramble.api.crypto.SecretKey; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.TransportId; - -import javax.annotation.concurrent.Immutable; - -/** - * Keys for handshaking with a given contact or pending contact over a given - * transport. Unlike {@link TransportKeys} these keys do not provide forward - * secrecy. - */ -@Immutable -@NotNullByDefault -public class HandshakeKeys extends AbstractTransportKeys { - - private final SecretKey rootKey; - private final boolean alice; - - public HandshakeKeys(TransportId transportId, IncomingKeys inPrev, - IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr, - SecretKey rootKey, boolean alice) { - super(transportId, inPrev, inCurr, inNext, outCurr); - this.rootKey = rootKey; - this.alice = alice; - } - - public SecretKey getRootKey() { - return rootKey; - } - - public boolean isAlice() { - return alice; - } -} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/IncomingKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/IncomingKeys.java index 46a17887e..465dec55a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/IncomingKeys.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/IncomingKeys.java @@ -8,8 +8,8 @@ import javax.annotation.concurrent.Immutable; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; /** - * Contains transport keys for receiving streams from a given contact over a - * given transport in a given time period. + * Contains transport keys for receiving streams from a given contact or + * pending contact over a given transport in a given time period. */ @Immutable @NotNullByDefault 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 e5c1ac541..827c94fb7 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 @@ -27,14 +27,14 @@ public interface KeyManager { * @param alice true if the local party is Alice * @param active whether the derived keys can be used for outgoing streams */ - Map addContact(Transaction txn, ContactId c, + Map addContact(Transaction txn, ContactId c, SecretKey rootKey, long timestamp, boolean alice, boolean active) throws DbException; /** * Marks the given transport keys as usable for outgoing streams. */ - void activateKeys(Transaction txn, Map keys) + void activateKeys(Transaction txn, Map keys) throws DbException; /** diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySetId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySetId.java similarity index 56% rename from bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySetId.java rename to bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySetId.java index f54c3bb7a..fb6a929df 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/HandshakeKeySetId.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeySetId.java @@ -5,17 +5,16 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import javax.annotation.concurrent.Immutable; /** - * Type-safe wrapper for an integer that uniquely identifies a - * {@link HandshakeKeySet set of handshake keys} within the scope of the local - * device. + * Type-safe wrapper for an integer that uniquely identifies a set of + * {@link TransportKeySet transport keys} within the scope of the local device. */ @Immutable @NotNullByDefault -public class HandshakeKeySetId { +public class KeySetId { private final int id; - public HandshakeKeySetId(int id) { + public KeySetId(int id) { this.id = id; } @@ -30,7 +29,6 @@ public class HandshakeKeySetId { @Override public boolean equals(Object o) { - return o instanceof HandshakeKeySetId && - id == ((HandshakeKeySetId) o).id; + return o instanceof KeySetId && id == ((KeySetId) o).id; } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/OutgoingKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/OutgoingKeys.java index 5e06e9b40..9fa6245d0 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/OutgoingKeys.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/OutgoingKeys.java @@ -6,8 +6,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import javax.annotation.concurrent.Immutable; /** - * Contains transport keys for sending streams to a given contact over a given - * transport in a given time period. + * Contains transport keys for sending streams to a given contact or pending + * contact over a given transport in a given time period. */ @Immutable @NotNullByDefault diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StreamContext.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StreamContext.java index ba90e6f29..0290d1eb8 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StreamContext.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StreamContext.java @@ -1,34 +1,52 @@ package org.briarproject.bramble.api.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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @Immutable @NotNullByDefault public class StreamContext { + @Nullable private final ContactId contactId; + @Nullable + private final PendingContactId pendingContactId; private final TransportId transportId; private final SecretKey tagKey, headerKey; private final long streamNumber; + private final boolean handshakeMode; - public StreamContext(ContactId contactId, TransportId transportId, - SecretKey tagKey, SecretKey headerKey, long streamNumber) { + public StreamContext(@Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, + TransportId transportId, SecretKey tagKey, SecretKey headerKey, + long streamNumber, boolean handshakeMode) { + if ((contactId == null) == (pendingContactId == null)) + throw new IllegalArgumentException(); this.contactId = contactId; + this.pendingContactId = pendingContactId; this.transportId = transportId; this.tagKey = tagKey; this.headerKey = headerKey; this.streamNumber = streamNumber; + this.handshakeMode = handshakeMode; } + @Nullable public ContactId getContactId() { return contactId; } + @Nullable + public PendingContactId getPendingContactId() { + return pendingContactId; + } + public TransportId getTransportId() { return transportId; } @@ -44,4 +62,8 @@ public class StreamContext { public long getStreamNumber() { return streamNumber; } + + public boolean isHandshakeMode() { + return handshakeMode; + } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySet.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySet.java index 4b3831bc3..b5a22775f 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySet.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySet.java @@ -1,37 +1,51 @@ package org.briarproject.bramble.api.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** - * A set of keys for communicating with a given contact over a given transport. - * Unlike a {@link HandshakeKeySet} these keys provide forward secrecy. + * A set of keys for communicating with a given contact or pending contact + * over a given transport. */ @Immutable @NotNullByDefault public class TransportKeySet { - private final TransportKeySetId keySetId; + private final KeySetId keySetId; + @Nullable private final ContactId contactId; + @Nullable + private final PendingContactId pendingContactId; private final TransportKeys keys; - public TransportKeySet(TransportKeySetId keySetId, ContactId contactId, - TransportKeys keys) { + public TransportKeySet(KeySetId keySetId, @Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, TransportKeys keys) { + if ((contactId == null) == (pendingContactId == null)) + throw new IllegalArgumentException(); this.keySetId = keySetId; this.contactId = contactId; + this.pendingContactId = pendingContactId; this.keys = keys; } - public TransportKeySetId getKeySetId() { + public KeySetId getKeySetId() { return keySetId; } + @Nullable public ContactId getContactId() { return contactId; } + @Nullable + public PendingContactId getPendingContactId() { + return pendingContactId; + } + public TransportKeys getKeys() { return keys; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySetId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySetId.java deleted file mode 100644 index c4b1ae088..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeySetId.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.briarproject.bramble.api.transport; - -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; - -import javax.annotation.concurrent.Immutable; - -/** - * Type-safe wrapper for an integer that uniquely identifies a - * {@link TransportKeySet set of transport keys} within the scope of the local - * device. - *

- * Key sets created on a given device must have increasing identifiers. - */ -@Immutable -@NotNullByDefault -public class TransportKeySetId { - - private final int id; - - public TransportKeySetId(int id) { - this.id = id; - } - - public int getInt() { - return id; - } - - @Override - public int hashCode() { - return id; - } - - @Override - public boolean equals(Object o) { - return o instanceof TransportKeySetId && - id == ((TransportKeySetId) o).id; - } -} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeys.java index 39a27d45b..200f05bd1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeys.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportKeys.java @@ -1,20 +1,108 @@ package org.briarproject.bramble.api.transport; +import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** - * Keys for communicating with a given contact over a given transport. Unlike - * {@link HandshakeKeys} these keys provide forward secrecy. + * Keys for communicating with a given contact or pending contact over a given + * transport. */ @Immutable @NotNullByDefault -public class TransportKeys extends AbstractTransportKeys { +public class TransportKeys { + private final TransportId transportId; + private final IncomingKeys inPrev, inCurr, inNext; + private final OutgoingKeys outCurr; + @Nullable + private final SecretKey rootKey; + private final boolean alice; + + /** + * Constructor for rotation mode. + */ public TransportKeys(TransportId transportId, IncomingKeys inPrev, IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { - super(transportId, inPrev, inCurr, inNext, outCurr); + this(transportId, inPrev, inCurr, inNext, outCurr, null, false); + } + + /** + * Constructor for handshake mode. + */ + public TransportKeys(TransportId transportId, IncomingKeys inPrev, + IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr, + @Nullable SecretKey rootKey, boolean alice) { + if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1) + throw new IllegalArgumentException(); + if (inCurr.getTimePeriod() != outCurr.getTimePeriod()) + throw new IllegalArgumentException(); + if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1) + throw new IllegalArgumentException(); + this.transportId = transportId; + this.inPrev = inPrev; + this.inCurr = inCurr; + this.inNext = inNext; + this.outCurr = outCurr; + this.rootKey = rootKey; + this.alice = alice; + } + + public TransportId getTransportId() { + return transportId; + } + + public IncomingKeys getPreviousIncomingKeys() { + return inPrev; + } + + public IncomingKeys getCurrentIncomingKeys() { + return inCurr; + } + + public IncomingKeys getNextIncomingKeys() { + return inNext; + } + + public OutgoingKeys getCurrentOutgoingKeys() { + return outCurr; + } + + public long getTimePeriod() { + return outCurr.getTimePeriod(); + } + + /** + * Returns true if these keys are for use in handshake mode or false if + * they're for use in rotation mode. + */ + public boolean isHandshakeMode() { + return rootKey != null; + } + + /** + * If these keys are for use in handshake mode, returns the root key. + * + * @throws UnsupportedOperationException If these keys are for use in + * rotation mode + */ + public SecretKey getRootKey() { + if (rootKey == null) throw new UnsupportedOperationException(); + return rootKey; + } + + /** + * If these keys are for use in handshake mode, returns true if the local + * party is Alice. + * + * @throws UnsupportedOperationException If these keys are for use in + * rotation mode + */ + public boolean isAlice() { + if (rootKey == null) throw new UnsupportedOperationException(); + return alice; } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java index 5ee6b87ee..fab0c8fcd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java @@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.transport.HandshakeKeys; import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.TransportKeys; @@ -42,7 +41,7 @@ class TransportCryptoImpl implements TransportCrypto { } @Override - public TransportKeys deriveTransportKeys(TransportId t, + public TransportKeys deriveRotationKeys(TransportId t, SecretKey rootKey, long timePeriod, boolean weAreAlice, boolean active) { // Keys for the previous period are derived from the root key @@ -70,31 +69,6 @@ class TransportCryptoImpl implements TransportCrypto { return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); } - @Override - public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) { - if (k.getTimePeriod() >= timePeriod) return k; - IncomingKeys inPrev = k.getPreviousIncomingKeys(); - IncomingKeys inCurr = k.getCurrentIncomingKeys(); - IncomingKeys inNext = k.getNextIncomingKeys(); - OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); - long startPeriod = outCurr.getTimePeriod(); - boolean active = outCurr.isActive(); - // Rotate the keys - for (long p = startPeriod + 1; p <= timePeriod; p++) { - inPrev = inCurr; - inCurr = inNext; - SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); - SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1); - inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1); - SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p); - SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p); - outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active); - } - // Collect and return the keys - return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext, - outCurr); - } - private SecretKey rotateKey(SecretKey k, long timePeriod) { byte[] period = new byte[INT_64_BYTES]; writeUint64(timePeriod, period, 0); @@ -117,7 +91,7 @@ class TransportCryptoImpl implements TransportCrypto { } @Override - public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, + public TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, long timePeriod, boolean weAreAlice) { if (timePeriod < 1) throw new IllegalArgumentException(); IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey, @@ -128,7 +102,7 @@ class TransportCryptoImpl implements TransportCrypto { weAreAlice, timePeriod + 1); OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, weAreAlice, timePeriod); - return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey, + return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey, weAreAlice); } @@ -171,7 +145,13 @@ class TransportCryptoImpl implements TransportCrypto { } @Override - public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) { + public TransportKeys updateTransportKeys(TransportKeys k, long timePeriod) { + if (k.isHandshakeMode()) return updateHandshakeKeys(k, timePeriod); + else return updateRotationKeys(k, timePeriod); + } + + private TransportKeys updateHandshakeKeys(TransportKeys k, + long timePeriod) { long elapsed = timePeriod - k.getTimePeriod(); TransportId t = k.getTransportId(); SecretKey rootKey = k.getRootKey(); @@ -188,7 +168,7 @@ class TransportCryptoImpl implements TransportCrypto { weAreAlice, timePeriod + 1); OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, weAreAlice, timePeriod); - return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, + return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey, weAreAlice); } else if (elapsed == 2) { // The keys are two periods old - shift by two periods, keeping @@ -200,7 +180,7 @@ class TransportCryptoImpl implements TransportCrypto { weAreAlice, timePeriod + 1); OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, weAreAlice, timePeriod); - return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, + return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey, weAreAlice); } else { // The keys are more than two periods old - derive fresh keys @@ -208,6 +188,30 @@ class TransportCryptoImpl implements TransportCrypto { } } + private TransportKeys updateRotationKeys(TransportKeys k, long timePeriod) { + if (k.getTimePeriod() >= timePeriod) return k; + IncomingKeys inPrev = k.getPreviousIncomingKeys(); + IncomingKeys inCurr = k.getCurrentIncomingKeys(); + IncomingKeys inNext = k.getNextIncomingKeys(); + OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); + long startPeriod = outCurr.getTimePeriod(); + boolean active = outCurr.isActive(); + // Rotate the keys + for (long p = startPeriod + 1; p <= timePeriod; p++) { + inPrev = inCurr; + inCurr = inNext; + SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); + SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1); + inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1); + SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p); + SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p); + outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active); + } + // Collect and return the keys + return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext, + outCurr); + } + @Override public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, long streamNumber) { 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 a4a836d3b..d93aed223 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 @@ -29,11 +29,8 @@ import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.validation.MessageState; -import org.briarproject.bramble.api.transport.HandshakeKeySet; -import org.briarproject.bramble.api.transport.HandshakeKeySetId; -import org.briarproject.bramble.api.transport.HandshakeKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import java.util.Collection; @@ -107,20 +104,6 @@ interface Database { void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) throws DbException; - /** - * Stores the given handshake keys for the given contact and returns a - * key set ID. - */ - HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k) - throws DbException; - - /** - * Stores the given handshake keys for the given pending contact and - * returns a key set ID. - */ - HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p, - HandshakeKeys k) throws DbException; - /** * Stores an identity. */ @@ -162,7 +145,14 @@ interface Database { * Stores the given transport keys for the given contact and returns a * key set ID. */ - TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) + KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) + throws DbException; + + /** + * Stores the given transport keys for the given pending contact and + * returns a key set ID. + */ + KeySetId addTransportKeys(T txn, PendingContactId p, TransportKeys k) throws DbException; /** @@ -275,7 +265,7 @@ interface Database { *

* Read-only. */ - Collection getContacts(T txn, AuthorId a) throws DbException; + Collection getContacts(T txn, AuthorId local) throws DbException; /** * Returns the group with the given ID. @@ -317,14 +307,6 @@ interface Database { Map getGroupVisibility(T txn, GroupId g) throws DbException; - /** - * Returns all handshake keys for the given transport. - *

- * Read-only. - */ - Collection getHandshakeKeys(T txn, TransportId t) - throws DbException; - /** * Returns the identity for local pseudonym with the given ID. *

@@ -547,16 +529,10 @@ interface Database { Collection getTransportKeys(T txn, TransportId t) throws DbException; - /** - * Increments the outgoing stream counter for the given handshake keys. - */ - void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k) - throws DbException; - /** * Increments the outgoing stream counter for the given transport keys. */ - void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k) + void incrementStreamCounter(T txn, TransportId t, KeySetId k) throws DbException; /** @@ -625,12 +601,6 @@ interface Database { void removeGroupVisibility(T txn, ContactId c, GroupId g) throws DbException; - /** - * Removes the given handshake keys from the database. - */ - void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k) - throws DbException; - /** * Removes an identity (and all associated state) from the database. */ @@ -661,8 +631,7 @@ interface Database { /** * Removes the given transport keys from the database. */ - void removeTransportKeys(T txn, TransportId t, TransportKeySetId k) - throws DbException; + void removeTransportKeys(T txn, TransportId t, KeySetId k) throws DbException; /** * Resets the transmission count and expiry time of the given message with @@ -712,23 +681,16 @@ interface Database { PendingContactState state) throws DbException; /** - * Sets the reordering window for the given transport key set in the given + * Sets the reordering window for the given transport keys in the given * time period. */ - void setReorderingWindow(T txn, TransportKeySetId k, TransportId t, - long timePeriod, long base, byte[] bitmap) throws DbException; - - /** - * Sets the reordering window for the given handshake key set in the given - * time period. - */ - void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t, + void setReorderingWindow(T txn, KeySetId k, TransportId t, long timePeriod, long base, byte[] bitmap) throws DbException; /** * Marks the given transport keys as usable for outgoing streams. */ - void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k) + void setTransportKeysActive(T txn, TransportId t, KeySetId k) throws DbException; /** @@ -740,12 +702,7 @@ interface Database { throws DbException; /** - * Updates the given handshake keys. - */ - void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException; - - /** - * Updates the given transport keys following key rotation. + * Stores the given transport keys, deleting any keys they have replaced. */ void updateTransportKeys(T txn, TransportKeySet ks) 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 c11aaa7de..d72fd994f 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 @@ -64,11 +64,8 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.validation.MessageState; -import org.briarproject.bramble.api.transport.HandshakeKeySet; -import org.briarproject.bramble.api.transport.HandshakeKeySetId; -import org.briarproject.bramble.api.transport.HandshakeKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import java.util.ArrayList; @@ -260,30 +257,6 @@ class DatabaseComponentImpl implements DatabaseComponent { } } - @Override - public HandshakeKeySetId addHandshakeKeys(Transaction transaction, - ContactId c, HandshakeKeys 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, k.getTransportId())) - throw new NoSuchTransportException(); - return db.addHandshakeKeys(txn, c, k); - } - - @Override - public HandshakeKeySetId addHandshakeKeys(Transaction transaction, - PendingContactId p, HandshakeKeys k) throws DbException { - if (transaction.isReadOnly()) throw new IllegalArgumentException(); - T txn = unbox(transaction); - if (!db.containsPendingContact(txn, p)) - throw new NoSuchPendingContactException(); - if (!db.containsTransport(txn, k.getTransportId())) - throw new NoSuchTransportException(); - return db.addHandshakeKeys(txn, p, k); - } - @Override public void addIdentity(Transaction transaction, Identity i) throws DbException { @@ -332,8 +305,8 @@ class DatabaseComponentImpl implements DatabaseComponent { } @Override - public TransportKeySetId addTransportKeys(Transaction transaction, - 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 (!db.containsContact(txn, c)) @@ -343,6 +316,18 @@ class DatabaseComponentImpl implements DatabaseComponent { return db.addTransportKeys(txn, c, k); } + @Override + public KeySetId addTransportKeys(Transaction transaction, + PendingContactId p, TransportKeys k) throws DbException { + if (transaction.isReadOnly()) throw new IllegalArgumentException(); + T txn = unbox(transaction); + if (!db.containsPendingContact(txn, p)) + throw new NoSuchPendingContactException(); + if (!db.containsTransport(txn, k.getTransportId())) + throw new NoSuchTransportException(); + return db.addTransportKeys(txn, p, k); + } + @Override public boolean containsContact(Transaction transaction, AuthorId remote, AuthorId local) throws DbException { @@ -505,11 +490,11 @@ class DatabaseComponentImpl implements DatabaseComponent { @Override public Collection getContacts(Transaction transaction, - AuthorId a) throws DbException { + AuthorId local) throws DbException { T txn = unbox(transaction); - if (!db.containsIdentity(txn, a)) + if (!db.containsIdentity(txn, local)) throw new NoSuchIdentityException(); - return db.getContacts(txn, a); + return db.getContacts(txn, local); } @Override @@ -546,15 +531,6 @@ class DatabaseComponentImpl implements DatabaseComponent { return db.getGroupVisibility(txn, c, g); } - @Override - public Collection getHandshakeKeys(Transaction transaction, - TransportId t) throws DbException { - T txn = unbox(transaction); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - return db.getHandshakeKeys(txn, t); - } - @Override public Identity getIdentity(Transaction transaction, AuthorId a) throws DbException { @@ -737,17 +713,7 @@ class DatabaseComponentImpl implements DatabaseComponent { @Override public void incrementStreamCounter(Transaction transaction, TransportId t, - HandshakeKeySetId k) throws DbException { - if (transaction.isReadOnly()) throw new IllegalArgumentException(); - T txn = unbox(transaction); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.incrementStreamCounter(txn, t, k); - } - - @Override - public void incrementStreamCounter(Transaction transaction, TransportId t, - TransportKeySetId k) throws DbException { + KeySetId k) throws DbException { if (transaction.isReadOnly()) throw new IllegalArgumentException(); T txn = unbox(transaction); if (!db.containsTransport(txn, t)) @@ -896,16 +862,6 @@ class DatabaseComponentImpl implements DatabaseComponent { transaction.attach(new GroupVisibilityUpdatedEvent(affected)); } - @Override - public void removeHandshakeKeys(Transaction transaction, - TransportId t, HandshakeKeySetId k) throws DbException { - if (transaction.isReadOnly()) throw new IllegalArgumentException(); - T txn = unbox(transaction); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.removeHandshakeKeys(txn, t, k); - } - @Override public void removeIdentity(Transaction transaction, AuthorId a) throws DbException { @@ -949,8 +905,8 @@ class DatabaseComponentImpl implements DatabaseComponent { } @Override - public void removeTransportKeys(Transaction transaction, - TransportId t, TransportKeySetId k) throws DbException { + public void removeTransportKeys(Transaction transaction, TransportId t, + KeySetId k) throws DbException { if (transaction.isReadOnly()) throw new IllegalArgumentException(); T txn = unbox(transaction); if (!db.containsTransport(txn, t)) @@ -1048,20 +1004,9 @@ class DatabaseComponentImpl implements DatabaseComponent { } @Override - public void setReorderingWindow(Transaction transaction, - TransportKeySetId k, TransportId t, long timePeriod, long base, - byte[] bitmap) throws DbException { - if (transaction.isReadOnly()) throw new IllegalArgumentException(); - T txn = unbox(transaction); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap); - } - - @Override - public void setReorderingWindow(Transaction transaction, - HandshakeKeySetId k, TransportId t, long timePeriod, long base, - byte[] bitmap) throws DbException { + public void setReorderingWindow(Transaction transaction, KeySetId k, + TransportId t, long timePeriod, long base, byte[] bitmap) + throws DbException { if (transaction.isReadOnly()) throw new IllegalArgumentException(); T txn = unbox(transaction); if (!db.containsTransport(txn, t)) @@ -1071,7 +1016,7 @@ class DatabaseComponentImpl implements DatabaseComponent { @Override public void setTransportKeysActive(Transaction transaction, TransportId t, - TransportKeySetId k) throws DbException { + KeySetId k) throws DbException { if (transaction.isReadOnly()) throw new IllegalArgumentException(); T txn = unbox(transaction); if (!db.containsTransport(txn, t)) @@ -1079,18 +1024,6 @@ class DatabaseComponentImpl implements DatabaseComponent { db.setTransportKeysActive(txn, t, k); } - @Override - public void updateHandshakeKeys(Transaction transaction, - Collection keys) throws DbException { - if (transaction.isReadOnly()) throw new IllegalArgumentException(); - T txn = unbox(transaction); - for (HandshakeKeySet ks : keys) { - TransportId t = ks.getKeys().getTransportId(); - if (db.containsTransport(txn, t)) - db.updateHandshakeKeys(txn, ks); - } - } - @Override public void updateTransportKeys(Transaction transaction, Collection keys) 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 caecf111e..77d7fba11 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 @@ -36,13 +36,10 @@ import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.system.Clock; -import org.briarproject.bramble.api.transport.HandshakeKeySet; -import org.briarproject.bramble.api.transport.HandshakeKeySetId; -import org.briarproject.bramble.api.transport.HandshakeKeys; import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import java.sql.Connection; @@ -68,11 +65,13 @@ import java.util.logging.Logger; import javax.annotation.Nullable; import static java.sql.Types.BINARY; +import static java.sql.Types.BOOLEAN; import static java.sql.Types.INTEGER; import static java.sql.Types.VARCHAR; import static java.util.Arrays.asList; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; @@ -99,7 +98,7 @@ import static org.briarproject.bramble.util.LogUtils.now; abstract class JdbcDatabase implements Database { // Package access for testing - static final int CODE_SCHEMA_VERSION = 43; + static final int CODE_SCHEMA_VERSION = 44; // Time period offsets for incoming transport keys private static final int OFFSET_PREV = -1; @@ -260,16 +259,28 @@ abstract class JdbcDatabase implements Database { + " maxLatency INT NOT NULL," + " PRIMARY KEY (transportId))"; + private static final String CREATE_PENDING_CONTACTS = + "CREATE TABLE pendingContacts" + + " (pendingContactId _HASH NOT NULL," + + " publicKey _BINARY NOT NULL," + + " alias _STRING NOT NULL," + + " state INT NOT NULL," + + " timestamp BIGINT NOT NULL," + + " PRIMARY KEY (pendingContactId))"; + private static final String CREATE_OUTGOING_KEYS = "CREATE TABLE outgoingKeys" + " (transportId _STRING NOT NULL," + " keySetId _COUNTER," + " timePeriod BIGINT NOT NULL," - + " contactId INT NOT NULL," + + " contactId INT," // Null if contact is pending + + " pendingContactId _HASH," // Null if not pending + " tagKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL," + " stream BIGINT NOT NULL," + " active BOOLEAN NOT NULL," + + " rootKey _SECRET," // Null for rotation keys + + " alice BOOLEAN," // Null for rotation keys + " PRIMARY KEY (transportId, keySetId)," + " FOREIGN KEY (transportId)" + " REFERENCES transports (transportId)" @@ -277,6 +288,9 @@ abstract class JdbcDatabase implements Database { + " UNIQUE (keySetId)," + " FOREIGN KEY (contactId)" + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (pendingContactId)" + + " REFERENCES pendingContacts (pendingContactId)" + " ON DELETE CASCADE)"; private static final String CREATE_INCOMING_KEYS = @@ -297,57 +311,6 @@ abstract class JdbcDatabase implements Database { + " REFERENCES outgoingKeys (keySetId)" + " ON DELETE CASCADE)"; - private static final String CREATE_PENDING_CONTACTS = - "CREATE TABLE pendingContacts" - + " (pendingContactId _HASH NOT NULL," - + " publicKey _BINARY NOT NULL," - + " alias _STRING NOT NULL," - + " state INT NOT NULL," - + " timestamp BIGINT NOT NULL," - + " PRIMARY KEY (pendingContactId))"; - - private static final String CREATE_OUTGOING_HANDSHAKE_KEYS = - "CREATE TABLE outgoingHandshakeKeys" - + " (transportId _STRING NOT NULL," - + " keySetId _COUNTER," - + " timePeriod BIGINT NOT NULL," - + " contactId INT," // Null if contact is pending - + " pendingContactId _HASH," // Null if not pending - + " rootKey _SECRET NOT NULL," - + " alice BOOLEAN NOT NULL," - + " tagKey _SECRET NOT NULL," - + " headerKey _SECRET NOT NULL," - + " stream BIGINT NOT NULL," - + " PRIMARY KEY (transportId, keySetId)," - + " FOREIGN KEY (transportId)" - + " REFERENCES transports (transportId)" - + " ON DELETE CASCADE," - + " UNIQUE (keySetId)," - + " FOREIGN KEY (contactId)" - + " REFERENCES contacts (contactId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (pendingContactId)" - + " REFERENCES pendingContacts (pendingContactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_INCOMING_HANDSHAKE_KEYS = - "CREATE TABLE incomingHandshakeKeys" - + " (transportId _STRING NOT NULL," - + " keySetId INT NOT NULL," - + " timePeriod BIGINT NOT NULL," - + " tagKey _SECRET NOT NULL," - + " headerKey _SECRET NOT NULL," - + " base BIGINT NOT NULL," - + " bitmap _BINARY NOT NULL," - + " periodOffset INT NOT NULL," - + " PRIMARY KEY (transportId, keySetId, periodOffset)," - + " FOREIGN KEY (transportId)" - + " REFERENCES transports (transportId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (keySetId)" - + " REFERENCES outgoingHandshakeKeys (keySetId)" - + " ON DELETE CASCADE)"; - private static final String INDEX_CONTACTS_BY_AUTHOR_ID = "CREATE INDEX IF NOT EXISTS contactsByAuthorId" + " ON contacts (authorId)"; @@ -373,7 +336,7 @@ abstract class JdbcDatabase implements Database { + " ON statuses (contactId, timestamp)"; private static final Logger LOG = - Logger.getLogger(JdbcDatabase.class.getName()); + getLogger(JdbcDatabase.class.getName()); // Different database libraries use different names for certain types private final MessageFactory messageFactory; @@ -493,7 +456,8 @@ abstract class JdbcDatabase implements Database { new Migration39_40(), new Migration40_41(dbTypes), new Migration41_42(dbTypes), - new Migration42_43(dbTypes) + new Migration42_43(dbTypes), + new Migration43_44(dbTypes) ); } @@ -541,13 +505,9 @@ abstract class JdbcDatabase implements Database { s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS)); + s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS)); - s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS)); - s.executeUpdate(dbTypes.replaceTypes( - CREATE_OUTGOING_HANDSHAKE_KEYS)); - s.executeUpdate(dbTypes.replaceTypes( - CREATE_INCOMING_HANDSHAKE_KEYS)); s.close(); } catch (SQLException e) { tryToClose(s, LOG, WARNING); @@ -783,103 +743,6 @@ abstract class JdbcDatabase implements Database { } } - @Override - public HandshakeKeySetId addHandshakeKeys(Connection txn, ContactId c, - HandshakeKeys k) throws DbException { - return addHandshakeKeys(txn, c, null, k); - } - - @Override - public HandshakeKeySetId addHandshakeKeys(Connection txn, - PendingContactId p, HandshakeKeys k) throws DbException { - return addHandshakeKeys(txn, null, p, k); - } - - private HandshakeKeySetId addHandshakeKeys(Connection txn, - @Nullable ContactId c, @Nullable PendingContactId p, - HandshakeKeys k) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - // Store the outgoing keys - String sql = "INSERT INTO outgoingHandshakeKeys (contactId," - + " pendingContactId, transportId, rootKey, alice," - + " timePeriod, tagKey, headerKey, stream)" - + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; - ps = txn.prepareStatement(sql); - if (c == null) ps.setNull(1, INTEGER); - else ps.setInt(1, c.getInt()); - if (p == null) ps.setNull(2, BINARY); - else ps.setBytes(2, p.getBytes()); - ps.setString(3, k.getTransportId().getString()); - ps.setBytes(4, k.getRootKey().getBytes()); - ps.setBoolean(5, k.isAlice()); - OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); - ps.setLong(6, outCurr.getTimePeriod()); - ps.setBytes(7, outCurr.getTagKey().getBytes()); - ps.setBytes(8, outCurr.getHeaderKey().getBytes()); - ps.setLong(9, outCurr.getStreamCounter()); - int affected = ps.executeUpdate(); - if (affected != 1) throw new DbStateException(); - ps.close(); - // Get the new (highest) key set ID - sql = "SELECT keySetId FROM outgoingHandshakeKeys" - + " ORDER BY keySetId DESC LIMIT 1"; - ps = txn.prepareStatement(sql); - rs = ps.executeQuery(); - if (!rs.next()) throw new DbStateException(); - HandshakeKeySetId keySetId = new HandshakeKeySetId(rs.getInt(1)); - if (rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - // Store the incoming keys - sql = "INSERT INTO incomingHandshakeKeys (keySetId, transportId," - + " timePeriod, tagKey, headerKey, base, bitmap," - + " periodOffset)" - + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, keySetId.getInt()); - ps.setString(2, k.getTransportId().getString()); - // Previous time period - IncomingKeys inPrev = k.getPreviousIncomingKeys(); - ps.setLong(3, inPrev.getTimePeriod()); - ps.setBytes(4, inPrev.getTagKey().getBytes()); - ps.setBytes(5, inPrev.getHeaderKey().getBytes()); - ps.setLong(6, inPrev.getWindowBase()); - ps.setBytes(7, inPrev.getWindowBitmap()); - ps.setInt(8, OFFSET_PREV); - ps.addBatch(); - // Current time period - IncomingKeys inCurr = k.getCurrentIncomingKeys(); - ps.setLong(3, inCurr.getTimePeriod()); - ps.setBytes(4, inCurr.getTagKey().getBytes()); - ps.setBytes(5, inCurr.getHeaderKey().getBytes()); - ps.setLong(6, inCurr.getWindowBase()); - ps.setBytes(7, inCurr.getWindowBitmap()); - ps.setInt(8, OFFSET_CURR); - ps.addBatch(); - // Next time period - IncomingKeys inNext = k.getNextIncomingKeys(); - ps.setLong(3, inNext.getTimePeriod()); - ps.setBytes(4, inNext.getTagKey().getBytes()); - ps.setBytes(5, inNext.getHeaderKey().getBytes()); - ps.setLong(6, inNext.getWindowBase()); - ps.setBytes(7, inNext.getWindowBitmap()); - ps.setInt(8, OFFSET_NEXT); - ps.addBatch(); - int[] batchAffected = ps.executeBatch(); - if (batchAffected.length != 3) throw new DbStateException(); - for (int rows : batchAffected) - if (rows != 1) throw new DbStateException(); - ps.close(); - return keySetId; - } catch (SQLException e) { - tryToClose(rs, LOG, WARNING); - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - @Override public void addIdentity(Connection txn, Identity i) throws DbException { PreparedStatement ps = null; @@ -1107,24 +970,47 @@ abstract class JdbcDatabase implements Database { } @Override - public TransportKeySetId addTransportKeys(Connection txn, ContactId c, + public KeySetId addTransportKeys(Connection txn, ContactId c, + TransportKeys k) throws DbException { + return addTransportKeys(txn, c, null, k); + } + + @Override + public KeySetId addTransportKeys(Connection txn, + PendingContactId p, TransportKeys k) throws DbException { + return addTransportKeys(txn, null, p, k); + } + + private KeySetId addTransportKeys(Connection txn, + @Nullable ContactId c, @Nullable PendingContactId p, TransportKeys k) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { // Store the outgoing keys - String sql = "INSERT INTO outgoingKeys (contactId, transportId," - + " timePeriod, tagKey, headerKey, stream, active)" - + " VALUES (?, ?, ?, ?, ?, ?, ?)"; + String sql = "INSERT INTO outgoingKeys (transportId, timePeriod," + + " contactId, pendingContactId, tagKey, headerKey," + + " stream, active, rootKey, alice)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setString(2, k.getTransportId().getString()); + ps.setString(1, k.getTransportId().getString()); + ps.setLong(2, k.getTimePeriod()); + if (c == null) ps.setNull(3, INTEGER); + else ps.setInt(3, c.getInt()); + if (p == null) ps.setNull(4, BINARY); + else ps.setBytes(4, p.getBytes()); OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); - ps.setLong(3, outCurr.getTimePeriod()); - ps.setBytes(4, outCurr.getTagKey().getBytes()); - ps.setBytes(5, outCurr.getHeaderKey().getBytes()); - ps.setLong(6, outCurr.getStreamCounter()); - ps.setBoolean(7, outCurr.isActive()); + ps.setBytes(5, outCurr.getTagKey().getBytes()); + ps.setBytes(6, outCurr.getHeaderKey().getBytes()); + ps.setLong(7, outCurr.getStreamCounter()); + ps.setBoolean(8, outCurr.isActive()); + if (k.isHandshakeMode()) { + ps.setBytes(9, k.getRootKey().getBytes()); + ps.setBoolean(10, k.isAlice()); + } else { + ps.setNull(9, BINARY); + ps.setNull(10, BOOLEAN); + } int affected = ps.executeUpdate(); if (affected != 1) throw new DbStateException(); ps.close(); @@ -1134,18 +1020,18 @@ abstract class JdbcDatabase implements Database { ps = txn.prepareStatement(sql); rs = ps.executeQuery(); if (!rs.next()) throw new DbStateException(); - TransportKeySetId keySetId = new TransportKeySetId(rs.getInt(1)); + KeySetId keySetId = new KeySetId(rs.getInt(1)); if (rs.next()) throw new DbStateException(); rs.close(); ps.close(); // Store the incoming keys - sql = "INSERT INTO incomingKeys (keySetId, transportId," + sql = "INSERT INTO incomingKeys (transportId, keySetId," + " timePeriod, tagKey, headerKey, base, bitmap," + " periodOffset)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, keySetId.getInt()); - ps.setString(2, k.getTransportId().getString()); + ps.setString(1, k.getTransportId().getString()); + ps.setInt(2, keySetId.getInt()); // Previous time period IncomingKeys inPrev = k.getPreviousIncomingKeys(); ps.setLong(3, inPrev.getTimePeriod()); @@ -1673,86 +1559,6 @@ abstract class JdbcDatabase implements Database { } } - @Override - public Collection getHandshakeKeys(Connection txn, - TransportId t) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - // Retrieve the incoming keys - String sql = "SELECT timePeriod, tagKey, headerKey, base, bitmap" - + " FROM incomingHandshakeKeys" - + " WHERE transportId = ?" - + " ORDER BY keySetId, periodOffset"; - ps = txn.prepareStatement(sql); - ps.setString(1, t.getString()); - rs = ps.executeQuery(); - List inKeys = new ArrayList<>(); - while (rs.next()) { - long timePeriod = rs.getLong(1); - SecretKey tagKey = new SecretKey(rs.getBytes(2)); - SecretKey headerKey = new SecretKey(rs.getBytes(3)); - long windowBase = rs.getLong(4); - byte[] windowBitmap = rs.getBytes(5); - inKeys.add(new IncomingKeys(tagKey, headerKey, timePeriod, - windowBase, windowBitmap)); - } - rs.close(); - ps.close(); - // Retrieve the outgoing keys in the same order - sql = "SELECT keySetId, contactId, pendingContactId, timePeriod," - + " tagKey, headerKey, rootKey, alice, stream" - + " FROM outgoingHandshakeKeys" - + " WHERE transportId = ?" - + " ORDER BY keySetId"; - ps = txn.prepareStatement(sql); - ps.setString(1, t.getString()); - rs = ps.executeQuery(); - Collection keys = new ArrayList<>(); - for (int i = 0; rs.next(); i++) { - // There should be three times as many incoming keys - if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); - HandshakeKeySetId keySetId = - new HandshakeKeySetId(rs.getInt(1)); - ContactId contactId = null; - int cId = rs.getInt(2); - if (!rs.wasNull()) contactId = new ContactId(cId); - PendingContactId pendingContactId = null; - byte[] pId = rs.getBytes(3); - if (!rs.wasNull()) pendingContactId = new PendingContactId(pId); - long timePeriod = rs.getLong(4); - SecretKey tagKey = new SecretKey(rs.getBytes(5)); - SecretKey headerKey = new SecretKey(rs.getBytes(6)); - SecretKey rootKey = new SecretKey(rs.getBytes(7)); - boolean alice = rs.getBoolean(8); - long streamCounter = rs.getLong(9); - OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, - timePeriod, streamCounter, true); - IncomingKeys inPrev = inKeys.get(i * 3); - IncomingKeys inCurr = inKeys.get(i * 3 + 1); - IncomingKeys inNext = inKeys.get(i * 3 + 2); - HandshakeKeys handshakeKeys = new HandshakeKeys(t, inPrev, - inCurr, inNext, outCurr, rootKey, alice); - if (contactId == null) { - if (pendingContactId == null) throw new DbStateException(); - keys.add(new HandshakeKeySet(keySetId, pendingContactId, - handshakeKeys)); - } else { - if (pendingContactId != null) throw new DbStateException(); - keys.add(new HandshakeKeySet(keySetId, contactId, - handshakeKeys)); - } - } - rs.close(); - ps.close(); - return keys; - } catch (SQLException e) { - tryToClose(rs, LOG, WARNING); - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - @Override public Identity getIdentity(Connection txn, AuthorId a) throws DbException { PreparedStatement ps = null; @@ -2522,8 +2328,8 @@ abstract class JdbcDatabase implements Database { rs.close(); ps.close(); // Retrieve the outgoing keys in the same order - sql = "SELECT keySetId, contactId, timePeriod," - + " tagKey, headerKey, stream, active" + sql = "SELECT keySetId, timePeriod, contactId, pendingContactId," + + " tagKey, headerKey, stream, active, rootKey, alice" + " FROM outgoingKeys" + " WHERE transportId = ?" + " ORDER BY keySetId"; @@ -2534,23 +2340,34 @@ abstract class JdbcDatabase implements Database { for (int i = 0; rs.next(); i++) { // There should be three times as many incoming keys if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); - TransportKeySetId keySetId = - new TransportKeySetId(rs.getInt(1)); - ContactId contactId = new ContactId(rs.getInt(2)); - long timePeriod = rs.getLong(3); - SecretKey tagKey = new SecretKey(rs.getBytes(4)); - SecretKey headerKey = new SecretKey(rs.getBytes(5)); - long streamCounter = rs.getLong(6); - boolean active = rs.getBoolean(7); + KeySetId keySetId = new KeySetId(rs.getInt(1)); + long timePeriod = rs.getLong(2); + int cId = rs.getInt(3); + ContactId contactId = rs.wasNull() ? null : new ContactId(cId); + byte[] pId = rs.getBytes(4); + PendingContactId pendingContactId = pId == null ? + null : new PendingContactId(pId); + SecretKey tagKey = new SecretKey(rs.getBytes(5)); + SecretKey headerKey = new SecretKey(rs.getBytes(6)); + long streamCounter = rs.getLong(7); + boolean active = rs.getBoolean(8); + byte[] rootKey = rs.getBytes(9); + boolean alice = rs.getBoolean(10); OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, timePeriod, streamCounter, active); IncomingKeys inPrev = inKeys.get(i * 3); IncomingKeys inCurr = inKeys.get(i * 3 + 1); IncomingKeys inNext = inKeys.get(i * 3 + 2); - TransportKeys transportKeys = new TransportKeys(t, inPrev, - inCurr, inNext, outCurr); + TransportKeys transportKeys; + if (rootKey == null) { + transportKeys = new TransportKeys(t, inPrev, inCurr, + inNext, outCurr); + } else { + transportKeys = new TransportKeys(t, inPrev, inCurr, + inNext, outCurr, new SecretKey(rootKey), alice); + } keys.add(new TransportKeySet(keySetId, contactId, - transportKeys)); + pendingContactId, transportKeys)); } rs.close(); ps.close(); @@ -2564,26 +2381,7 @@ abstract class JdbcDatabase implements Database { @Override public void incrementStreamCounter(Connection txn, TransportId t, - HandshakeKeySetId k) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE outgoingHandshakeKeys SET stream = stream + 1" - + " WHERE transportId = ? AND keySetId = ?"; - ps = txn.prepareStatement(sql); - ps.setString(1, t.getString()); - ps.setInt(2, k.getInt()); - int affected = ps.executeUpdate(); - if (affected != 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - - @Override - public void incrementStreamCounter(Connection txn, TransportId t, - TransportKeySetId k) throws DbException { + KeySetId k) throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE outgoingKeys SET stream = stream + 1" @@ -2961,27 +2759,6 @@ abstract class JdbcDatabase implements Database { } } - @Override - public void removeHandshakeKeys(Connection txn, TransportId t, - HandshakeKeySetId k) throws DbException { - PreparedStatement ps = null; - try { - // Delete any existing outgoing keys - this will also remove any - // incoming keys with the same key set ID - String sql = "DELETE FROM outgoingHandshakeKeys" - + " WHERE transportId = ? AND keySetId = ?"; - ps = txn.prepareStatement(sql); - ps.setString(1, t.getString()); - ps.setInt(2, k.getInt()); - int affected = ps.executeUpdate(); - if (affected < 0) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - @Override public void removeIdentity(Connection txn, AuthorId a) throws DbException { PreparedStatement ps = null; @@ -3094,8 +2871,8 @@ abstract class JdbcDatabase implements Database { } @Override - public void removeTransportKeys(Connection txn, TransportId t, - TransportKeySetId k) throws DbException { + public void removeTransportKeys(Connection txn, TransportId t, KeySetId k) + throws DbException { PreparedStatement ps = null; try { // Delete any existing outgoing keys - this will also remove any @@ -3311,7 +3088,7 @@ abstract class JdbcDatabase implements Database { ps.setInt(1, state.getValue()); ps.setBytes(2, p.getBytes()); int affected = ps.executeUpdate(); - if (affected != 1) throw new DbStateException(); + if (affected < 0 || affected > 1) throw new DbStateException(); ps.close(); } catch (SQLException e) { tryToClose(ps, LOG, WARNING); @@ -3320,7 +3097,7 @@ abstract class JdbcDatabase implements Database { } @Override - public void setReorderingWindow(Connection txn, TransportKeySetId k, + public void setReorderingWindow(Connection txn, KeySetId k, TransportId t, long timePeriod, long base, byte[] bitmap) throws DbException { PreparedStatement ps = null; @@ -3343,33 +3120,9 @@ abstract class JdbcDatabase implements Database { } } - @Override - public void setReorderingWindow(Connection txn, HandshakeKeySetId k, - TransportId t, long timePeriod, long base, byte[] bitmap) - throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE incomingHandshakeKeys SET base = ?, bitmap = ?" - + " WHERE transportId = ? AND keySetId = ?" - + " AND timePeriod = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, base); - ps.setBytes(2, bitmap); - ps.setString(3, t.getString()); - ps.setInt(4, k.getInt()); - ps.setLong(5, timePeriod); - int affected = ps.executeUpdate(); - if (affected < 0 || affected > 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - @Override public void setTransportKeysActive(Connection txn, TransportId t, - TransportKeySetId k) throws DbException { + KeySetId k) throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE outgoingKeys SET active = true" @@ -3489,71 +3242,4 @@ abstract class JdbcDatabase implements Database { throw new DbException(e); } } - - @Override - public void updateHandshakeKeys(Connection txn, HandshakeKeySet ks) - throws DbException { - PreparedStatement ps = null; - try { - // Update the outgoing keys - String sql = "UPDATE outgoingHandshakeKeys SET timePeriod = ?," - + " tagKey = ?, headerKey = ?, stream = ?" - + " WHERE transportId = ? AND keySetId = ?"; - ps = txn.prepareStatement(sql); - HandshakeKeys k = ks.getKeys(); - OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); - ps.setLong(1, outCurr.getTimePeriod()); - ps.setBytes(2, outCurr.getTagKey().getBytes()); - ps.setBytes(3, outCurr.getHeaderKey().getBytes()); - ps.setLong(4, outCurr.getStreamCounter()); - ps.setString(5, k.getTransportId().getString()); - ps.setInt(6, ks.getKeySetId().getInt()); - int affected = ps.executeUpdate(); - if (affected < 0 || affected > 1) throw new DbStateException(); - ps.close(); - // Update the incoming keys - sql = "UPDATE incomingHandshakeKeys SET timePeriod = ?," - + " tagKey = ?, headerKey = ?, base = ?, bitmap = ?" - + " WHERE transportId = ? AND keySetId = ?" - + " AND periodOffset = ?"; - ps = txn.prepareStatement(sql); - ps.setString(6, k.getTransportId().getString()); - ps.setInt(7, ks.getKeySetId().getInt()); - // Previous time period - IncomingKeys inPrev = k.getPreviousIncomingKeys(); - ps.setLong(1, inPrev.getTimePeriod()); - ps.setBytes(2, inPrev.getTagKey().getBytes()); - ps.setBytes(3, inPrev.getHeaderKey().getBytes()); - ps.setLong(4, inPrev.getWindowBase()); - ps.setBytes(5, inPrev.getWindowBitmap()); - ps.setInt(8, OFFSET_PREV); - ps.addBatch(); - // Current time period - IncomingKeys inCurr = k.getCurrentIncomingKeys(); - ps.setLong(1, inCurr.getTimePeriod()); - ps.setBytes(2, inCurr.getTagKey().getBytes()); - ps.setBytes(3, inCurr.getHeaderKey().getBytes()); - ps.setLong(4, inCurr.getWindowBase()); - ps.setBytes(5, inCurr.getWindowBitmap()); - ps.setInt(8, OFFSET_CURR); - ps.addBatch(); - // Next time period - IncomingKeys inNext = k.getNextIncomingKeys(); - ps.setLong(1, inNext.getTimePeriod()); - ps.setBytes(2, inNext.getTagKey().getBytes()); - ps.setBytes(3, inNext.getHeaderKey().getBytes()); - ps.setLong(4, inNext.getWindowBase()); - ps.setBytes(5, inNext.getWindowBitmap()); - ps.setInt(8, OFFSET_NEXT); - ps.addBatch(); - int[] batchAffected = ps.executeBatch(); - if (batchAffected.length != 3) throw new DbStateException(); - for (int rows : batchAffected) - if (rows < 0 || rows > 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration43_44.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration43_44.java new file mode 100644 index 000000000..530cbe0e9 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration43_44.java @@ -0,0 +1,58 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DbException; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.db.JdbcUtils.tryToClose; + +class Migration43_44 implements Migration { + + private static final Logger LOG = getLogger(Migration43_44.class.getName()); + + private final DatabaseTypes dbTypes; + + Migration43_44(DatabaseTypes dbTypes) { + this.dbTypes = dbTypes; + } + + @Override + public int getStartVersion() { + return 43; + } + + @Override + public int getEndVersion() { + return 44; + } + + @Override + public void migrate(Connection txn) throws DbException { + Statement s = null; + try { + s = txn.createStatement(); + s.execute("DROP TABLE outgoingHandshakeKeys"); + s.execute("DROP TABLE incomingHandshakeKeys"); + s.execute("ALTER TABLE outgoingKeys" + + " ALTER COLUMN contactId DROP NOT NULL"); + s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys" + + " ADD COLUMN pendingContactId _HASH")); + s.execute("ALTER TABLE outgoingKeys" + + " ADD FOREIGN KEY (pendingContactId)" + + " REFERENCES pendingContacts (pendingContactId)" + + " ON DELETE CASCADE"); + s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys" + + " ADD COLUMN rootKey _SECRET")); + s.execute("ALTER TABLE outgoingKeys" + + " ADD COLUMN alice BOOLEAN"); + } catch (SQLException e) { + tryToClose(s, LOG, WARNING); + throw new DbException(e); + } + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java index 0df08372b..baab0ccc2 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java @@ -96,6 +96,7 @@ class ConnectionManagerImpl implements ConnectionManager { TransportConnectionReader r) throws IOException { InputStream streamReader = streamReaderFactory.createStreamReader( r.getInputStream(), ctx); + // TODO: Pending contacts, handshake mode return syncSessionFactory.createIncomingSession(ctx.getContactId(), streamReader); } @@ -104,6 +105,7 @@ class ConnectionManagerImpl implements ConnectionManager { TransportConnectionWriter w) throws IOException { StreamWriter streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), ctx); + // TODO: Pending contacts, handshake mode return syncSessionFactory.createSimplexOutgoingSession( ctx.getContactId(), w.getMaxLatency(), streamWriter); } @@ -112,6 +114,7 @@ class ConnectionManagerImpl implements ConnectionManager { TransportConnectionWriter w) throws IOException { StreamWriter streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), ctx); + // TODO: Pending contacts, handshake mode return syncSessionFactory.createDuplexOutgoingSession( ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(), streamWriter); @@ -145,6 +148,7 @@ class ConnectionManagerImpl implements ConnectionManager { disposeReader(false, false); return; } + // TODO: Pending contacts ContactId contactId = ctx.getContactId(); connectionRegistry.registerConnection(contactId, transportId, true); try { @@ -388,7 +392,7 @@ class ConnectionManagerImpl implements ConnectionManager { return; } // Check that the stream comes from the expected contact - if (!ctx.getContactId().equals(contactId)) { + if (!contactId.equals(ctx.getContactId())) { LOG.warning("Wrong contact ID for returning stream"); disposeReader(true, true); return; 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 4ad9fe7d5..18d1b63ff 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 @@ -17,8 +17,8 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.transport.KeyManager; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.StreamContext; -import org.briarproject.bramble.api.transport.TransportKeySetId; import java.util.HashMap; import java.util.Map; @@ -88,10 +88,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { } @Override - public Map addContact(Transaction txn, + public Map addContact(Transaction txn, ContactId c, SecretKey rootKey, long timestamp, boolean alice, boolean active) throws DbException { - Map ids = new HashMap<>(); + Map ids = new HashMap<>(); for (Entry e : managers.entrySet()) { TransportId t = e.getKey(); TransportKeyManager m = e.getValue(); @@ -101,9 +101,9 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { } @Override - public void activateKeys(Transaction txn, Map keys) throws DbException { - for (Entry e : keys.entrySet()) { + public void activateKeys(Transaction txn, Map keys) + throws DbException { + for (Entry e : keys.entrySet()) { TransportId t = e.getKey(); TransportKeyManager m = managers.get(t); if (m == null) { 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 deleted file mode 100644 index 5c24b2220..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableKeySet.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.briarproject.bramble.transport; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.transport.TransportKeySetId; - -class MutableKeySet { - - private final TransportKeySetId keySetId; - private final ContactId contactId; - private final MutableTransportKeys transportKeys; - - MutableKeySet(TransportKeySetId keySetId, ContactId contactId, - MutableTransportKeys transportKeys) { - this.keySetId = keySetId; - this.contactId = contactId; - this.transportKeys = transportKeys; - } - - TransportKeySetId getKeySetId() { - return keySetId; - } - - ContactId getContactId() { - return contactId; - } - - MutableTransportKeys getTransportKeys() { - return transportKeys; - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeySet.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeySet.java new file mode 100644 index 000000000..89ef17726 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeySet.java @@ -0,0 +1,50 @@ +package org.briarproject.bramble.transport; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContactId; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.transport.KeySetId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; + +@NotThreadSafe +@NotNullByDefault +class MutableTransportKeySet { + + private final KeySetId keySetId; + @Nullable + private final ContactId contactId; + @Nullable + private final PendingContactId pendingContactId; + private final MutableTransportKeys keys; + + MutableTransportKeySet(KeySetId keySetId, @Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, + MutableTransportKeys keys) { + if ((contactId == null) == (pendingContactId == null)) + throw new IllegalArgumentException(); + this.keySetId = keySetId; + this.contactId = contactId; + this.pendingContactId = pendingContactId; + this.keys = keys; + } + + KeySetId getKeySetId() { + return keySetId; + } + + @Nullable + ContactId getContactId() { + return contactId; + } + + @Nullable + PendingContactId getPendingContactId() { + return pendingContactId; + } + + MutableTransportKeys getKeys() { + return keys; + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeys.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeys.java index ebaef9973..192c76415 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeys.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/MutableTransportKeys.java @@ -1,9 +1,11 @@ package org.briarproject.bramble.transport; +import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.transport.TransportKeys; +import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; @NotThreadSafe @@ -13,6 +15,9 @@ class MutableTransportKeys { private final TransportId transportId; private final MutableIncomingKeys inPrev, inCurr, inNext; private final MutableOutgoingKeys outCurr; + @Nullable + private final SecretKey rootKey; + private final boolean alice; MutableTransportKeys(TransportKeys k) { transportId = k.getTransportId(); @@ -20,11 +25,24 @@ class MutableTransportKeys { inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys()); inNext = new MutableIncomingKeys(k.getNextIncomingKeys()); outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys()); + if (k.isHandshakeMode()) { + rootKey = k.getRootKey(); + alice = k.isAlice(); + } else { + rootKey = null; + alice = false; + } } TransportKeys snapshot() { - return new TransportKeys(transportId, inPrev.snapshot(), - inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot()); + if (rootKey == null) { + return new TransportKeys(transportId, inPrev.snapshot(), + inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot()); + } else { + return new TransportKeys(transportId, inPrev.snapshot(), + inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot(), + rootKey, alice); + } } TransportId getTransportId() { @@ -46,4 +64,18 @@ class MutableTransportKeys { MutableOutgoingKeys getCurrentOutgoingKeys() { return outCurr; } + + boolean isHandshakeMode() { + return rootKey != null; + } + + SecretKey getRootKey() { + if (rootKey == null) throw new UnsupportedOperationException(); + return rootKey; + } + + boolean isAlice() { + if (rootKey == null) throw new UnsupportedOperationException(); + return alice; + } } 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 2d08ec4c1..fa3f54e11 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 @@ -5,8 +5,8 @@ import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.StreamContext; -import org.briarproject.bramble.api.transport.TransportKeySetId; import javax.annotation.Nullable; @@ -15,11 +15,10 @@ interface TransportKeyManager { void start(Transaction txn) throws DbException; - TransportKeySetId addContact(Transaction txn, ContactId c, - SecretKey rootKey, long timestamp, boolean alice, boolean active) - throws DbException; + KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey, + long timestamp, boolean alice, boolean active) throws DbException; - void activateKeys(Transaction txn, TransportKeySetId k) throws DbException; + void activateKeys(Transaction txn, KeySetId k) throws DbException; void removeContact(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 e93aab417..3e070e28f 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 @@ -2,6 +2,7 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.Bytes; 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.TransportCrypto; import org.briarproject.bramble.api.db.DatabaseComponent; @@ -11,9 +12,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Scheduler; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.transport.ReorderingWindow.Change; @@ -28,10 +29,13 @@ 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.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.WARNING; +import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; @@ -43,7 +47,7 @@ import static org.briarproject.bramble.util.LogUtils.logException; class TransportKeyManagerImpl implements TransportKeyManager { private static final Logger LOG = - Logger.getLogger(TransportKeyManagerImpl.class.getName()); + getLogger(TransportKeyManagerImpl.class.getName()); private final DatabaseComponent db; private final TransportCrypto transportCrypto; @@ -55,10 +59,13 @@ class TransportKeyManagerImpl implements TransportKeyManager { private final AtomicBoolean used = new AtomicBoolean(false); private final ReentrantLock lock = new ReentrantLock(); - // The following are locking: lock - private final Map keys = new HashMap<>(); + @GuardedBy("lock") + private final Map keys = new HashMap<>(); + @GuardedBy("lock") private final Map inContexts = new HashMap<>(); - private final Map outContexts = new HashMap<>(); + @GuardedBy("lock") + private final Map outContexts = + new HashMap<>(); TransportKeyManagerImpl(DatabaseComponent db, TransportCrypto transportCrypto, Executor dbExecutor, @@ -82,62 +89,70 @@ class TransportKeyManagerImpl implements TransportKeyManager { // Load the transport keys from the DB Collection loaded = db.getTransportKeys(txn, transportId); - // Rotate the keys to the current time period - RotationResult rotationResult = rotateKeys(loaded, now); + // Update the keys to the current time period + UpdateResult updateResult = updateKeys(loaded, now); // Initialise mutable state for all contacts - addKeys(rotationResult.current); - // Write any rotated keys back to the DB - if (!rotationResult.rotated.isEmpty()) - db.updateTransportKeys(txn, rotationResult.rotated); + addKeys(updateResult.current); + // Write any updated keys back to the DB + if (!updateResult.updated.isEmpty()) + db.updateTransportKeys(txn, updateResult.updated); } finally { lock.unlock(); } - // Schedule the next key rotation - scheduleKeyRotation(now); + // Schedule the next key update + scheduleKeyUpdate(now); } - private RotationResult rotateKeys(Collection keys, + private UpdateResult updateKeys(Collection keys, long now) { - RotationResult rotationResult = new RotationResult(); + UpdateResult updateResult = new UpdateResult(); long timePeriod = now / timePeriodLength; for (TransportKeySet ks : keys) { TransportKeys k = ks.getKeys(); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, + TransportKeys k1 = transportCrypto.updateTransportKeys(k, timePeriod); TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(), - ks.getContactId(), k1); + ks.getContactId(), null, k1); if (k1.getTimePeriod() > k.getTimePeriod()) - rotationResult.rotated.add(ks1); - rotationResult.current.add(ks1); + updateResult.updated.add(ks1); + updateResult.current.add(ks1); } - return rotationResult; + return updateResult; } - // Locking: lock + @GuardedBy("lock") private void addKeys(Collection keys) { for (TransportKeySet ks : keys) { addKeys(ks.getKeySetId(), ks.getContactId(), + ks.getPendingContactId(), new MutableTransportKeys(ks.getKeys())); } } - // Locking: lock - private void addKeys(TransportKeySetId keySetId, ContactId contactId, - MutableTransportKeys m) { - MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); - keys.put(keySetId, ks); - encodeTags(keySetId, contactId, m.getPreviousIncomingKeys()); - encodeTags(keySetId, contactId, m.getCurrentIncomingKeys()); - encodeTags(keySetId, contactId, m.getNextIncomingKeys()); + @GuardedBy("lock") + private void addKeys(KeySetId keySetId, @Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, + MutableTransportKeys keys) { + MutableTransportKeySet ks = new MutableTransportKeySet(keySetId, + contactId, pendingContactId, keys); + this.keys.put(keySetId, ks); + boolean handshakeMode = keys.isHandshakeMode(); + encodeTags(keySetId, contactId, pendingContactId, + keys.getPreviousIncomingKeys(), handshakeMode); + encodeTags(keySetId, contactId, pendingContactId, + keys.getCurrentIncomingKeys(), handshakeMode); + encodeTags(keySetId, contactId, pendingContactId, + keys.getNextIncomingKeys(), handshakeMode); considerReplacingOutgoingKeys(ks); } - // Locking: lock - private void encodeTags(TransportKeySetId keySetId, ContactId contactId, - MutableIncomingKeys inKeys) { + @GuardedBy("lock") + private void encodeTags(KeySetId keySetId, @Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, + MutableIncomingKeys inKeys, boolean handshakeMode) { for (long streamNumber : inKeys.getWindow().getUnseen()) { - TagContext tagCtx = - new TagContext(keySetId, contactId, inKeys, streamNumber); + TagContext tagCtx = new TagContext(keySetId, contactId, + pendingContactId, inKeys, streamNumber, handshakeMode); byte[] tag = new byte[TAG_LENGTH]; transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION, streamNumber); @@ -145,27 +160,30 @@ class TransportKeyManagerImpl implements TransportKeyManager { } } - // Locking: lock - private void considerReplacingOutgoingKeys(MutableKeySet ks) { + @GuardedBy("lock") + private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) { // Use the active outgoing keys with the highest key set ID - if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) { - MutableKeySet old = outContexts.get(ks.getContactId()); + 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()) || old.getKeySetId().getInt() < ks.getKeySetId().getInt()) { - outContexts.put(ks.getContactId(), ks); + outContexts.put(c, ks); } } } - private void scheduleKeyRotation(long now) { + private void scheduleKeyUpdate(long now) { long delay = timePeriodLength - now % timePeriodLength; - scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS); + scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS); } - private void rotateKeys() { + private void updateKeys() { dbExecutor.execute(() -> { try { - db.transaction(false, this::rotateKeys); + db.transaction(false, this::updateKeys); } catch (DbException e) { logException(LOG, WARNING, e); } @@ -173,23 +191,22 @@ class TransportKeyManagerImpl implements TransportKeyManager { } @Override - public TransportKeySetId addContact(Transaction txn, ContactId c, - SecretKey rootKey, long timestamp, boolean alice, boolean active) - throws DbException { + public KeySetId addContact(Transaction txn, ContactId c, SecretKey rootKey, + long timestamp, boolean alice, boolean active) throws DbException { lock.lock(); try { // Work out what time period the timestamp belongs to long timePeriod = timestamp / timePeriodLength; // Derive the transport keys - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, timePeriod, alice, active); - // Rotate the keys to the current time period if necessary + // Update the keys to the current time period if necessary timePeriod = clock.currentTimeMillis() / timePeriodLength; - k = transportCrypto.rotateTransportKeys(k, timePeriod); + k = transportCrypto.updateTransportKeys(k, timePeriod); // Write the keys back to the DB - TransportKeySetId keySetId = db.addTransportKeys(txn, c, k); + KeySetId keySetId = db.addTransportKeys(txn, c, k); // Initialise mutable state for the contact - addKeys(keySetId, c, new MutableTransportKeys(k)); + addKeys(keySetId, c, null, new MutableTransportKeys(k)); return keySetId; } finally { lock.unlock(); @@ -197,13 +214,12 @@ class TransportKeyManagerImpl implements TransportKeyManager { } @Override - public void activateKeys(Transaction txn, TransportKeySetId k) - throws DbException { + public void activateKeys(Transaction txn, KeySetId k) throws DbException { lock.lock(); try { - MutableKeySet ks = keys.get(k); + MutableTransportKeySet ks = keys.get(k); if (ks == null) throw new IllegalArgumentException(); - MutableTransportKeys m = ks.getTransportKeys(); + MutableTransportKeys m = ks.getKeys(); m.getCurrentOutgoingKeys().activate(); considerReplacingOutgoingKeys(ks); db.setTransportKeysActive(txn, m.getTransportId(), k); @@ -218,13 +234,11 @@ class TransportKeyManagerImpl implements TransportKeyManager { try { // Remove mutable state for the contact Iterator it = inContexts.values().iterator(); - while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove(); + while (it.hasNext()) if (c.equals(it.next().contactId)) it.remove(); outContexts.remove(c); - Iterator it1 = keys.values().iterator(); - while (it1.hasNext()) { - ContactId c1 = it1.next().getContactId(); - if (c1 != null && c1.equals(c)) it1.remove(); - } + Iterator it1 = keys.values().iterator(); + while (it1.hasNext()) + if (c.equals(it1.next().getContactId())) it1.remove(); } finally { lock.unlock(); } @@ -234,10 +248,10 @@ class TransportKeyManagerImpl implements TransportKeyManager { public boolean canSendOutgoingStreams(ContactId c) { lock.lock(); try { - MutableKeySet ks = outContexts.get(c); + MutableTransportKeySet ks = outContexts.get(c); if (ks == null) return false; MutableOutgoingKeys outKeys = - ks.getTransportKeys().getCurrentOutgoingKeys(); + ks.getKeys().getCurrentOutgoingKeys(); if (!outKeys.isActive()) throw new AssertionError(); return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED; } finally { @@ -251,16 +265,16 @@ class TransportKeyManagerImpl implements TransportKeyManager { lock.lock(); try { // Look up the outgoing keys for the contact - MutableKeySet ks = outContexts.get(c); + MutableTransportKeySet ks = outContexts.get(c); if (ks == null) return null; - MutableOutgoingKeys outKeys = - ks.getTransportKeys().getCurrentOutgoingKeys(); + 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, transportId, + StreamContext ctx = new StreamContext(c, null, transportId, outKeys.getTagKey(), outKeys.getHeaderKey(), - outKeys.getStreamCounter()); + outKeys.getStreamCounter(), keys.isHandshakeMode()); // Increment the stream counter and write it back to the DB outKeys.incrementStreamCounter(); db.incrementStreamCounter(txn, transportId, ks.getKeySetId()); @@ -280,9 +294,10 @@ class TransportKeyManagerImpl implements TransportKeyManager { if (tagCtx == null) return null; MutableIncomingKeys inKeys = tagCtx.inKeys; // Create a stream context - StreamContext ctx = new StreamContext(tagCtx.contactId, transportId, + StreamContext ctx = new StreamContext(tagCtx.contactId, + tagCtx.pendingContactId, transportId, inKeys.getTagKey(), inKeys.getHeaderKey(), - tagCtx.streamNumber); + tagCtx.streamNumber, tagCtx.handshakeMode); // Update the reordering window ReorderingWindow window = inKeys.getWindow(); Change change = window.setSeen(tagCtx.streamNumber); @@ -292,7 +307,8 @@ class TransportKeyManagerImpl implements TransportKeyManager { transportCrypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION, streamNumber); TagContext tagCtx1 = new TagContext(tagCtx.keySetId, - tagCtx.contactId, inKeys, streamNumber); + tagCtx.contactId, tagCtx.pendingContactId, inKeys, + streamNumber, tagCtx.handshakeMode); inContexts.put(new Bytes(addTag), tagCtx1); } // Remove tags for any stream numbers removed from the window @@ -308,9 +324,9 @@ class TransportKeyManagerImpl implements TransportKeyManager { inKeys.getTimePeriod(), window.getBase(), window.getBitmap()); // If the outgoing keys are inactive, activate them - MutableKeySet ks = keys.get(tagCtx.keySetId); + MutableTransportKeySet ks = keys.get(tagCtx.keySetId); MutableOutgoingKeys outKeys = - ks.getTransportKeys().getCurrentOutgoingKeys(); + ks.getKeys().getCurrentOutgoingKeys(); if (!outKeys.isActive()) { LOG.info("Activating outgoing keys"); outKeys.activate(); @@ -323,51 +339,60 @@ class TransportKeyManagerImpl implements TransportKeyManager { } } - private void rotateKeys(Transaction txn) throws DbException { + private void updateKeys(Transaction txn) throws DbException { long now = clock.currentTimeMillis(); lock.lock(); try { - // Rotate the keys to the current time period + // Update the keys to the current time period Collection snapshot = new ArrayList<>(keys.size()); - for (MutableKeySet ks : keys.values()) { + for (MutableTransportKeySet ks : keys.values()) { snapshot.add(new TransportKeySet(ks.getKeySetId(), - ks.getContactId(), ks.getTransportKeys().snapshot())); + ks.getContactId(), ks.getPendingContactId(), + ks.getKeys().snapshot())); } - RotationResult rotationResult = rotateKeys(snapshot, now); + UpdateResult updateResult = updateKeys(snapshot, now); // Rebuild the mutable state for all contacts inContexts.clear(); outContexts.clear(); keys.clear(); - addKeys(rotationResult.current); - // Write any rotated keys back to the DB - if (!rotationResult.rotated.isEmpty()) - db.updateTransportKeys(txn, rotationResult.rotated); + addKeys(updateResult.current); + // Write any updated keys back to the DB + if (!updateResult.updated.isEmpty()) + db.updateTransportKeys(txn, updateResult.updated); } finally { lock.unlock(); } - // Schedule the next key rotation - scheduleKeyRotation(now); + // Schedule the next key update + scheduleKeyUpdate(now); } private static class TagContext { - private final TransportKeySetId keySetId; + private final KeySetId keySetId; + @Nullable private final ContactId contactId; + @Nullable + private final PendingContactId pendingContactId; private final MutableIncomingKeys inKeys; private final long streamNumber; + private final boolean handshakeMode; - private TagContext(TransportKeySetId keySetId, ContactId contactId, - MutableIncomingKeys inKeys, long streamNumber) { + private TagContext(KeySetId keySetId, @Nullable ContactId contactId, + @Nullable PendingContactId pendingContactId, + MutableIncomingKeys inKeys, long streamNumber, + boolean handshakeMode) { this.keySetId = keySetId; this.contactId = contactId; + this.pendingContactId = pendingContactId; this.inKeys = inKeys; this.streamNumber = streamNumber; + this.handshakeMode = handshakeMode; } } - private static class RotationResult { + private static class UpdateResult { private final Collection current = new ArrayList<>(); - private final Collection rotated = new ArrayList<>(); + private final Collection updated = new ArrayList<>(); } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/HandshakeKeyDerivationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/HandshakeKeyDerivationTest.java deleted file mode 100644 index e741b9f08..000000000 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/HandshakeKeyDerivationTest.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.briarproject.bramble.crypto; - -import org.briarproject.bramble.api.crypto.CryptoComponent; -import org.briarproject.bramble.api.crypto.SecretKey; -import org.briarproject.bramble.api.crypto.TransportCrypto; -import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.transport.HandshakeKeys; -import org.briarproject.bramble.test.BrambleTestCase; -import org.briarproject.bramble.test.TestSecureRandomProvider; -import org.junit.Test; - -import java.util.Arrays; - -import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent; -import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches; -import static org.briarproject.bramble.test.TestUtils.getSecretKey; -import static org.briarproject.bramble.test.TestUtils.getTransportId; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertSame; - -public class HandshakeKeyDerivationTest extends BrambleTestCase { - - private final CryptoComponent crypto = - new CryptoComponentImpl(new TestSecureRandomProvider(), null); - private final TransportCrypto transportCrypto = - new TransportCryptoImpl(crypto); - private final TransportId transportId = getTransportId(); - private final SecretKey rootKey = getSecretKey(); - - @Test - public void testKeysAreDistinct() { - HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, false); - assertAllDifferent(kA); - assertAllDifferent(kB); - } - - @Test - public void testKeysAreNotUpdatedToPreviousPeriod() { - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 122); - assertSame(k, k1); - } - - @Test - public void testKeysAreNotUpdatedToCurrentPeriod() { - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 123); - assertSame(k, k1); - } - - @Test - public void testKeysAreUpdatedByOnePeriod() { - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 124); - assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys()); - assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys()); - } - - @Test - public void testKeysAreUpdatedByTwoPeriods() { - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 125); - assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys()); - } - - @Test - public void testKeysAreUpdatedByThreePeriods() { - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 126); - assertAllDifferent(k, k1); - } - - @Test - public void testCurrentKeysMatchContact() { - // Start in time period 123 - HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, false); - // Alice's incoming keys should equal Bob's outgoing keys - assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); - // Bob's incoming keys should equal Alice's outgoing keys - assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); - // Update into the future - kA = transportCrypto.updateHandshakeKeys(kA, 456); - kB = transportCrypto.updateHandshakeKeys(kB, 456); - // Alice's incoming keys should equal Bob's outgoing keys - assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); - // Bob's incoming keys should equal Alice's outgoing keys - assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); - } - - @Test - public void testPreviousKeysMatchContact() { - // Start in time period 123 - HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, false); - // Compare Alice's previous keys in period 456 with Bob's current keys - // in period 455 - kA = transportCrypto.updateHandshakeKeys(kA, 456); - kB = transportCrypto.updateHandshakeKeys(kB, 455); - // Alice's previous incoming keys should equal Bob's current - // outgoing keys - assertMatches(kA.getPreviousIncomingKeys(), - kB.getCurrentOutgoingKeys()); - // Compare Alice's current keys in period 456 with Bob's previous keys - // in period 457 - kB = transportCrypto.updateHandshakeKeys(kB, 457); - // Bob's previous incoming keys should equal Alice's current - // outgoing keys - assertMatches(kB.getPreviousIncomingKeys(), - kA.getCurrentOutgoingKeys()); - } - - @Test - public void testNextKeysMatchContact() { - // Start in time period 123 - HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, false); - // Compare Alice's current keys in period 456 with Bob's next keys in - // period 455 - kA = transportCrypto.updateHandshakeKeys(kA, 456); - kB = transportCrypto.updateHandshakeKeys(kB, 455); - // Bob's next incoming keys should equal Alice's current outgoing keys - assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys()); - // Compare Alice's next keys in period 456 with Bob's current keys - // in period 457 - kB = transportCrypto.updateHandshakeKeys(kB, 457); - // Alice's next incoming keys should equal Bob's current outgoing keys - assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys()); - } - - @Test - public void testRootKeyAffectsOutput() { - SecretKey rootKey1 = getSecretKey(); - assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes())); - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId, - rootKey1, 123, true); - assertAllDifferent(k, k1); - } - - @Test - public void testTransportIdAffectsOutput() { - TransportId transportId1 = getTransportId(); - assertNotEquals(transportId.getString(), transportId1.getString()); - HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId, - rootKey, 123, true); - HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1, - rootKey, 123, true); - assertAllDifferent(k, k1); - } -} \ No newline at end of file diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTestUtils.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTestUtils.java deleted file mode 100644 index 878dc5488..000000000 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTestUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.briarproject.bramble.crypto; - -import org.briarproject.bramble.api.Bytes; -import org.briarproject.bramble.api.crypto.SecretKey; -import org.briarproject.bramble.api.transport.AbstractTransportKeys; -import org.briarproject.bramble.api.transport.IncomingKeys; -import org.briarproject.bramble.api.transport.OutgoingKeys; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -class KeyDerivationTestUtils { - - static void assertAllDifferent(AbstractTransportKeys... transportKeys) { - List secretKeys = new ArrayList<>(); - for (AbstractTransportKeys k : transportKeys) { - secretKeys.add(k.getPreviousIncomingKeys().getTagKey()); - secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey()); - secretKeys.add(k.getCurrentIncomingKeys().getTagKey()); - secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey()); - secretKeys.add(k.getNextIncomingKeys().getTagKey()); - secretKeys.add(k.getNextIncomingKeys().getHeaderKey()); - secretKeys.add(k.getCurrentOutgoingKeys().getTagKey()); - secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey()); - } - assertAllDifferent(secretKeys); - } - - static void assertAllDifferent(List keys) { - Set set = new HashSet<>(); - for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes()))); - } - - static void assertMatches(IncomingKeys in, OutgoingKeys out) { - assertArrayEquals(in.getTagKey().getBytes(), - out.getTagKey().getBytes()); - assertArrayEquals(in.getHeaderKey().getBytes(), - out.getHeaderKey().getBytes()); - } -} diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/TransportKeyDerivationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/TransportKeyDerivationTest.java index bfa031e73..e83e4052a 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/TransportKeyDerivationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/TransportKeyDerivationTest.java @@ -1,23 +1,30 @@ package org.briarproject.bramble.crypto; +import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.TestSecureRandomProvider; import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent; -import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches; import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTransportId; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class TransportKeyDerivationTest extends BrambleTestCase { @@ -29,70 +36,70 @@ public class TransportKeyDerivationTest extends BrambleTestCase { private final SecretKey rootKey = getSecretKey(); @Test - public void testKeysAreDistinct() { - TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreDistinct() { + TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, false, true); assertAllDifferent(kA); assertAllDifferent(kB); } @Test - public void testKeysAreNotRotatedToPreviousPeriod() { - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreNotRotatedToPreviousPeriod() { + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122); assertSame(k, k1); } @Test - public void testKeysAreNotRotatedToCurrentPeriod() { - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreNotRotatedToCurrentPeriod() { + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123); assertSame(k, k1); } @Test - public void testKeysAreRotatedByOnePeriod() { - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreRotatedByOnePeriod() { + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124); assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys()); assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys()); } @Test - public void testKeysAreRotatedByTwoPeriods() { - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreRotatedByTwoPeriods() { + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125); assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys()); } @Test - public void testKeysAreRotatedByThreePeriods() { - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + public void testRotationKeysAreRotatedByThreePeriods() { + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126); assertAllDifferent(k, k1); } @Test - public void testCurrentKeysMatchContact() { + public void testCurrentRotationKeysMatchContact() { // Start in time period 123 - TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, false, true); // Alice's incoming keys should equal Bob's outgoing keys assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); // Bob's incoming keys should equal Alice's outgoing keys assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); // Rotate into the future - kA = transportCrypto.rotateTransportKeys(kA, 456); - kB = transportCrypto.rotateTransportKeys(kB, 456); + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 456); // Alice's incoming keys should equal Bob's outgoing keys assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); // Bob's incoming keys should equal Alice's outgoing keys @@ -100,23 +107,23 @@ public class TransportKeyDerivationTest extends BrambleTestCase { } @Test - public void testPreviousKeysMatchContact() { + public void testPreviousRotationKeysMatchContact() { // Start in time period 123 - TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, false, true); // Compare Alice's previous keys in period 456 with Bob's current keys // in period 455 - kA = transportCrypto.rotateTransportKeys(kA, 456); - kB = transportCrypto.rotateTransportKeys(kB, 455); + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 455); // Alice's previous incoming keys should equal Bob's current // outgoing keys assertMatches(kA.getPreviousIncomingKeys(), kB.getCurrentOutgoingKeys()); // Compare Alice's current keys in period 456 with Bob's previous keys // in period 457 - kB = transportCrypto.rotateTransportKeys(kB, 457); + kB = transportCrypto.updateTransportKeys(kB, 457); // Bob's previous incoming keys should equal Alice's current // outgoing keys assertMatches(kB.getPreviousIncomingKeys(), @@ -124,44 +131,208 @@ public class TransportKeyDerivationTest extends BrambleTestCase { } @Test - public void testNextKeysMatchContact() { + public void testNextRotationKeysMatchContact() { // Start in time period 123 - TransportKeys kA = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys kB = transportCrypto.deriveTransportKeys(transportId, + TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, false, true); // Compare Alice's current keys in period 456 with Bob's next keys in // period 455 - kA = transportCrypto.rotateTransportKeys(kA, 456); - kB = transportCrypto.rotateTransportKeys(kB, 455); + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 455); // Bob's next incoming keys should equal Alice's current outgoing keys assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys()); // Compare Alice's next keys in period 456 with Bob's current keys // in period 457 - kB = transportCrypto.rotateTransportKeys(kB, 457); + kB = transportCrypto.updateTransportKeys(kB, 457); // Alice's next incoming keys should equal Bob's current outgoing keys assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys()); } @Test - public void testRootKeyAffectsOutput() { + public void testRootKeyAffectsRotationKeyDerivation() { SecretKey rootKey1 = getSecretKey(); assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes())); - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId, + TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId, rootKey1, 123, true, true); assertAllDifferent(k, k1); } @Test - public void testTransportIdAffectsOutput() { + public void testTransportIdAffectsRotationKeyDerivation() { TransportId transportId1 = getTransportId(); assertNotEquals(transportId.getString(), transportId1.getString()); - TransportKeys k = transportCrypto.deriveTransportKeys(transportId, + TransportKeys k = transportCrypto.deriveRotationKeys(transportId, rootKey, 123, true, true); - TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1, + TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId1, rootKey, 123, true, true); assertAllDifferent(k, k1); } + + @Test + public void testHandshakeKeysAreDistinct() { + TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, false); + assertAllDifferent(kA); + assertAllDifferent(kB); + } + + @Test + public void testHandshakeKeysAreNotUpdatedToPreviousPeriod() { + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122); + assertSame(k, k1); + } + + @Test + public void testHandshakeKeysAreNotUpdatedToCurrentPeriod() { + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123); + assertSame(k, k1); + } + + @Test + public void testHandshakeKeysAreUpdatedByOnePeriod() { + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124); + assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys()); + assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys()); + } + + @Test + public void testHandshakeKeysAreUpdatedByTwoPeriods() { + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125); + assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys()); + } + + @Test + public void testHandshakeKeysAreUpdatedByThreePeriods() { + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126); + assertAllDifferent(k, k1); + } + + @Test + public void testCurrentHandshakeKeysMatchContact() { + // Start in time period 123 + TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, false); + // Alice's incoming keys should equal Bob's outgoing keys + assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); + // Bob's incoming keys should equal Alice's outgoing keys + assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); + // Update into the future + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 456); + // Alice's incoming keys should equal Bob's outgoing keys + assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); + // Bob's incoming keys should equal Alice's outgoing keys + assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); + } + + @Test + public void testPreviousHandshakeKeysMatchContact() { + // Start in time period 123 + TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, false); + // Compare Alice's previous keys in period 456 with Bob's current keys + // in period 455 + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 455); + // Alice's previous incoming keys should equal Bob's current + // outgoing keys + assertMatches(kA.getPreviousIncomingKeys(), + kB.getCurrentOutgoingKeys()); + // Compare Alice's current keys in period 456 with Bob's previous keys + // in period 457 + kB = transportCrypto.updateTransportKeys(kB, 457); + // Bob's previous incoming keys should equal Alice's current + // outgoing keys + assertMatches(kB.getPreviousIncomingKeys(), + kA.getCurrentOutgoingKeys()); + } + + @Test + public void testNextHandshakeKeysMatchContact() { + // Start in time period 123 + TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, false); + // Compare Alice's current keys in period 456 with Bob's next keys in + // period 455 + kA = transportCrypto.updateTransportKeys(kA, 456); + kB = transportCrypto.updateTransportKeys(kB, 455); + // Bob's next incoming keys should equal Alice's current outgoing keys + assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys()); + // Compare Alice's next keys in period 456 with Bob's current keys + // in period 457 + kB = transportCrypto.updateTransportKeys(kB, 457); + // Alice's next incoming keys should equal Bob's current outgoing keys + assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys()); + } + + @Test + public void testRootKeyAffectsHandshakeKeyDerivation() { + SecretKey rootKey1 = getSecretKey(); + assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes())); + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId, + rootKey1, 123, true); + assertAllDifferent(k, k1); + } + + @Test + public void testTransportIdAffectsHandshakeKeyDerivation() { + TransportId transportId1 = getTransportId(); + assertNotEquals(transportId.getString(), transportId1.getString()); + TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId, + rootKey, 123, true); + TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1, + rootKey, 123, true); + assertAllDifferent(k, k1); + } + + private void assertAllDifferent(TransportKeys... transportKeys) { + List secretKeys = new ArrayList<>(); + for (TransportKeys k : transportKeys) { + secretKeys.add(k.getPreviousIncomingKeys().getTagKey()); + secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey()); + secretKeys.add(k.getCurrentIncomingKeys().getTagKey()); + secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey()); + secretKeys.add(k.getNextIncomingKeys().getTagKey()); + secretKeys.add(k.getNextIncomingKeys().getHeaderKey()); + secretKeys.add(k.getCurrentOutgoingKeys().getTagKey()); + secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey()); + } + assertAllDifferent(secretKeys); + } + + private void assertAllDifferent(List keys) { + Set set = new HashSet<>(); + for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes()))); + } + + private void assertMatches(IncomingKeys in, OutgoingKeys out) { + assertArrayEquals(in.getTagKey().getBytes(), + out.getTagKey().getBytes()); + assertArrayEquals(in.getHeaderKey().getBytes(), + out.getHeaderKey().getBytes()); + } } 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 1de270ad0..a77acbdd6 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 @@ -48,11 +48,10 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent; -import org.briarproject.bramble.api.transport.HandshakeKeys; import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.CaptureArgumentAction; @@ -119,7 +118,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { private final int maxLatency; private final ContactId contactId; private final Contact contact; - private final TransportKeySetId keySetId; + private final KeySetId keySetId; private final PendingContactId pendingContactId; public DatabaseComponentImplTest() { @@ -141,7 +140,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { contact = getContact(author, localAuthor.getId(), true); contactId = contact.getId(); alias = contact.getAlias(); - keySetId = new TransportKeySetId(345); + keySetId = new KeySetId(345); pendingContactId = new PendingContactId(getRandomId()); } @@ -286,24 +285,15 @@ 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, eventExecutor, shutdownManager); - try { - db.transaction(false, transaction -> - db.addHandshakeKeys(transaction, contactId, - createHandshakeKeys())); - fail(); - } catch (NoSuchContactException expected) { - // Expected - } - try { db.transaction(false, transaction -> db.addTransportKeys(transaction, contactId, @@ -499,8 +489,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { exactly(8).of(database).containsGroup(txn, groupId); will(returnValue(false)); exactly(8).of(database).abortTransaction(txn); - // This is needed for getMessageStatus() and setGroupVisibility() - exactly(2).of(database).containsContact(txn, contactId); + // Allow other checks to pass + allowing(database).containsContact(txn, contactId); will(returnValue(true)); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, @@ -583,8 +573,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { exactly(11).of(database).containsMessage(txn, messageId); will(returnValue(false)); exactly(11).of(database).abortTransaction(txn); - // This is needed for getMessageStatus() to proceed - exactly(1).of(database).containsContact(txn, contactId); + // Allow other checks to pass + allowing(database).containsContact(txn, contactId); will(returnValue(true)); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, @@ -684,15 +674,38 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { throws Exception { context.checking(new Expectations() {{ // Check whether the transport is in the DB (which it's not) - exactly(5).of(database).startTransaction(); + exactly(8).of(database).startTransaction(); will(returnValue(txn)); - exactly(5).of(database).containsTransport(txn, transportId); + exactly(8).of(database).containsTransport(txn, transportId); will(returnValue(false)); - exactly(5).of(database).abortTransaction(txn); + exactly(8).of(database).abortTransaction(txn); + // Allow other checks to pass + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + allowing(database).containsPendingContact(txn, pendingContactId); + will(returnValue(true)); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, eventExecutor, shutdownManager); + try { + db.transaction(false, transaction -> + db.addTransportKeys(transaction, contactId, + createHandshakeKeys())); + fail(); + } catch (NoSuchTransportException expected) { + // Expected + } + + try { + db.transaction(false, transaction -> + db.addTransportKeys(transaction, pendingContactId, + createHandshakeKeys())); + fail(); + } catch (NoSuchTransportException expected) { + // Expected + } + try { db.transaction(false, transaction -> db.getTransportKeys(transaction, transportId)); @@ -712,7 +725,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { try { db.transaction(false, transaction -> - db.removeTransport(transaction, transportId)); + db.removeTransportKeys(transaction, transportId, keySetId)); fail(); } catch (NoSuchTransportException expected) { // Expected @@ -720,7 +733,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { try { db.transaction(false, transaction -> - db.removeTransportKeys(transaction, transportId, keySetId)); + db.removeTransport(transaction, transportId)); fail(); } catch (NoSuchTransportException expected) { // Expected @@ -734,6 +747,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { } catch (NoSuchTransportException expected) { // Expected } + + try { + db.transaction(false, transaction -> + db.setTransportKeysActive(transaction, transportId, + keySetId)); + fail(); + } catch (NoSuchTransportException expected) { + // Expected + } } @Test @@ -753,7 +775,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { try { db.transaction(false, transaction -> - db.addHandshakeKeys(transaction, pendingContactId, + db.addTransportKeys(transaction, pendingContactId, createHandshakeKeys())); fail(); } catch (NoSuchPendingContactException expected) { @@ -1169,7 +1191,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { public void testTransportKeys() throws Exception { TransportKeys transportKeys = createTransportKeys(); TransportKeySet ks = - new TransportKeySet(keySetId, contactId, transportKeys); + new TransportKeySet(keySetId, contactId, null, transportKeys); Collection keys = singletonList(ks); context.checking(new Expectations() {{ @@ -1297,7 +1319,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { }); } - private HandshakeKeys createHandshakeKeys() { + private TransportKeys createHandshakeKeys() { SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey(); IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, @@ -1314,7 +1336,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { SecretKey outCurrHeaderKey = getSecretKey(); OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, 2, 456, true); - return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr, + return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr, getSecretKey(), true); } 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 9b8b280de..3cd9969f5 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 @@ -24,13 +24,10 @@ import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.system.Clock; -import org.briarproject.bramble.api.transport.HandshakeKeySet; -import org.briarproject.bramble.api.transport.HandshakeKeySetId; -import org.briarproject.bramble.api.transport.HandshakeKeys; import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.test.BrambleTestCase; @@ -114,8 +111,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { private final MessageId messageId; private final TransportId transportId; private final ContactId contactId; - private final TransportKeySetId keySetId, keySetId1; - private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1; + private final KeySetId keySetId, keySetId1; private final PendingContact pendingContact; private final Random random = new Random(); @@ -131,10 +127,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { messageId = message.getId(); transportId = getTransportId(); contactId = new ContactId(1); - keySetId = new TransportKeySetId(1); - keySetId1 = new TransportKeySetId(2); - handshakeKeySetId = new HandshakeKeySetId(1); - handshakeKeySetId1 = new HandshakeKeySetId(2); + keySetId = new KeySetId(1); + keySetId1 = new KeySetId(2); pendingContact = getPendingContact(); } @@ -703,14 +697,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { } } - // Rotate the transport keys - TransportKeys rotated = createTransportKeys(timePeriod + 1, active); - TransportKeys rotated1 = + // Update the transport keys + TransportKeys updated = createTransportKeys(timePeriod + 1, active); + TransportKeys updated1 = createTransportKeys(timePeriod1 + 1, active); db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId, - rotated)); + null, updated)); db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId, - rotated1)); + null, updated1)); // Retrieve the transport keys again allKeys = db.getTransportKeys(txn, transportId); @@ -718,10 +712,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { for (TransportKeySet ks : allKeys) { assertEquals(contactId, ks.getContactId()); if (ks.getKeySetId().equals(keySetId)) { - assertKeysEquals(rotated, ks.getKeys()); + assertKeysEquals(updated, ks.getKeys()); } else { assertEquals(keySetId1, ks.getKeySetId()); - assertKeysEquals(rotated1, ks.getKeys()); + assertKeysEquals(updated1, ks.getKeys()); } } @@ -745,6 +739,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { actual.getNextIncomingKeys()); assertKeysEquals(expected.getCurrentOutgoingKeys(), actual.getCurrentOutgoingKeys()); + if (expected.isHandshakeMode()) { + assertTrue(actual.isHandshakeMode()); + assertArrayEquals(expected.getRootKey().getBytes(), + actual.getRootKey().getBytes()); + assertEquals(expected.isAlice(), actual.isAlice()); + } else { + assertFalse(actual.isHandshakeMode()); + } } private void assertKeysEquals(IncomingKeys expected, IncomingKeys actual) { @@ -773,154 +775,135 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { boolean alice = random.nextBoolean(); SecretKey rootKey = getSecretKey(); SecretKey rootKey1 = getSecretKey(); - HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); - HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); + TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); + TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); Database db = open(false); Connection txn = db.startTransaction(); // Initially there should be no handshake keys in the database - assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId)); + assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); // Add the contact, the transport and the handshake keys db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), true)); db.addTransport(txn, transportId, 123); - assertEquals(handshakeKeySetId, - db.addHandshakeKeys(txn, contactId, keys)); - assertEquals(handshakeKeySetId1, - db.addHandshakeKeys(txn, contactId, keys1)); + assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); + assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); // Retrieve the handshake keys - Collection allKeys = - db.getHandshakeKeys(txn, transportId); + Collection allKeys = + db.getTransportKeys(txn, transportId); assertEquals(2, allKeys.size()); - for (HandshakeKeySet ks : allKeys) { + for (TransportKeySet ks : allKeys) { assertEquals(contactId, ks.getContactId()); assertNull(ks.getPendingContactId()); - if (ks.getKeySetId().equals(handshakeKeySetId)) { + if (ks.getKeySetId().equals(keySetId)) { assertKeysEquals(keys, ks.getKeys()); } else { - assertEquals(handshakeKeySetId1, ks.getKeySetId()); + assertEquals(keySetId1, ks.getKeySetId()); assertKeysEquals(keys1, ks.getKeys()); } } // Update the handshake keys - HandshakeKeys updated = + TransportKeys updated = createHandshakeKeys(timePeriod + 1, rootKey, alice); - HandshakeKeys updated1 = + TransportKeys updated1 = createHandshakeKeys(timePeriod1 + 1, rootKey1, alice); - db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId, - contactId, updated)); - db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1, - contactId, updated1)); + db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId, + null, updated)); + db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId, + null, updated1)); // Retrieve the handshake keys again - allKeys = db.getHandshakeKeys(txn, transportId); + allKeys = db.getTransportKeys(txn, transportId); assertEquals(2, allKeys.size()); - for (HandshakeKeySet ks : allKeys) { + for (TransportKeySet ks : allKeys) { assertEquals(contactId, ks.getContactId()); assertNull(ks.getPendingContactId()); - if (ks.getKeySetId().equals(handshakeKeySetId)) { + if (ks.getKeySetId().equals(keySetId)) { assertKeysEquals(updated, ks.getKeys()); } else { - assertEquals(handshakeKeySetId1, ks.getKeySetId()); + assertEquals(keySetId1, ks.getKeySetId()); assertKeysEquals(updated1, ks.getKeys()); } } // Removing the contact should remove the handshake keys db.removeContact(txn, contactId); - assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId)); + assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); db.commitTransaction(txn); db.close(); } - private void assertKeysEquals(HandshakeKeys expected, - HandshakeKeys actual) { - assertEquals(expected.getTransportId(), actual.getTransportId()); - assertEquals(expected.getTimePeriod(), actual.getTimePeriod()); - assertArrayEquals(expected.getRootKey().getBytes(), - actual.getRootKey().getBytes()); - assertEquals(expected.isAlice(), actual.isAlice()); - assertKeysEquals(expected.getPreviousIncomingKeys(), - actual.getPreviousIncomingKeys()); - assertKeysEquals(expected.getCurrentIncomingKeys(), - actual.getCurrentIncomingKeys()); - assertKeysEquals(expected.getNextIncomingKeys(), - actual.getNextIncomingKeys()); - assertKeysEquals(expected.getCurrentOutgoingKeys(), - actual.getCurrentOutgoingKeys()); - } - @Test public void testHandshakeKeysForPendingContact() throws Exception { long timePeriod = 123, timePeriod1 = 234; boolean alice = random.nextBoolean(); SecretKey rootKey = getSecretKey(); SecretKey rootKey1 = getSecretKey(); - HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); - HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); + TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); + TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); Database db = open(false); Connection txn = db.startTransaction(); // Initially there should be no handshake keys in the database - assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId)); + assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); // Add the pending contact, the transport and the handshake keys db.addPendingContact(txn, pendingContact); db.addTransport(txn, transportId, 123); - assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn, - pendingContact.getId(), keys)); - assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn, - pendingContact.getId(), keys1)); + assertEquals(keySetId, + db.addTransportKeys(txn, pendingContact.getId(), keys)); + assertEquals(keySetId1, + db.addTransportKeys(txn, pendingContact.getId(), keys1)); // Retrieve the handshake keys - Collection allKeys = - db.getHandshakeKeys(txn, transportId); + Collection allKeys = + db.getTransportKeys(txn, transportId); assertEquals(2, allKeys.size()); - for (HandshakeKeySet ks : allKeys) { + for (TransportKeySet ks : allKeys) { assertNull(ks.getContactId()); assertEquals(pendingContact.getId(), ks.getPendingContactId()); - if (ks.getKeySetId().equals(handshakeKeySetId)) { + if (ks.getKeySetId().equals(keySetId)) { assertKeysEquals(keys, ks.getKeys()); } else { - assertEquals(handshakeKeySetId1, ks.getKeySetId()); + assertEquals(keySetId1, ks.getKeySetId()); assertKeysEquals(keys1, ks.getKeys()); } } // Update the handshake keys - HandshakeKeys updated = + TransportKeys updated = createHandshakeKeys(timePeriod + 1, rootKey, alice); - HandshakeKeys updated1 = + TransportKeys updated1 = createHandshakeKeys(timePeriod1 + 1, rootKey1, alice); - db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId, + db.updateTransportKeys(txn, new TransportKeySet(keySetId, null, pendingContact.getId(), updated)); - db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1, + db.updateTransportKeys(txn, new TransportKeySet(keySetId1, null, pendingContact.getId(), updated1)); // Retrieve the handshake keys again - allKeys = db.getHandshakeKeys(txn, transportId); + allKeys = db.getTransportKeys(txn, transportId); assertEquals(2, allKeys.size()); - for (HandshakeKeySet ks : allKeys) { + for (TransportKeySet ks : allKeys) { assertNull(ks.getContactId()); assertEquals(pendingContact.getId(), ks.getPendingContactId()); - if (ks.getKeySetId().equals(handshakeKeySetId)) { + if (ks.getKeySetId().equals(keySetId)) { assertKeysEquals(updated, ks.getKeys()); } else { - assertEquals(handshakeKeySetId1, ks.getKeySetId()); + assertEquals(keySetId1, ks.getKeySetId()); assertKeysEquals(updated1, ks.getKeys()); } } // Removing the pending contact should remove the handshake keys db.removePendingContact(txn, pendingContact.getId()); - assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId)); + assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); db.commitTransaction(txn); db.close(); @@ -973,7 +956,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { long timePeriod = 123; SecretKey rootKey = getSecretKey(); boolean alice = random.nextBoolean(); - HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); + TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter(); Database db = open(false); @@ -984,20 +967,20 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), true)); db.addTransport(txn, transportId, 123); - assertEquals(handshakeKeySetId, - db.addHandshakeKeys(txn, contactId, keys)); + assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); // Increment the stream counter twice and retrieve the handshake keys - db.incrementStreamCounter(txn, transportId, handshakeKeySetId); - db.incrementStreamCounter(txn, transportId, handshakeKeySetId); - Collection newKeys = - db.getHandshakeKeys(txn, transportId); + db.incrementStreamCounter(txn, transportId, keySetId); + db.incrementStreamCounter(txn, transportId, keySetId); + Collection newKeys = + db.getTransportKeys(txn, transportId); assertEquals(1, newKeys.size()); - HandshakeKeySet ks = newKeys.iterator().next(); - assertEquals(handshakeKeySetId, ks.getKeySetId()); + TransportKeySet ks = newKeys.iterator().next(); + assertEquals(keySetId, ks.getKeySetId()); assertEquals(contactId, ks.getContactId()); - HandshakeKeys k = ks.getKeys(); + TransportKeys k = ks.getKeys(); assertEquals(transportId, k.getTransportId()); + assertNotNull(k.getRootKey()); assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes()); assertEquals(alice, k.isAlice()); OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); @@ -1066,7 +1049,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { long timePeriod = 123; SecretKey rootKey = getSecretKey(); boolean alice = random.nextBoolean(); - HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); + TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); long base = keys.getCurrentIncomingKeys().getWindowBase(); byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap(); @@ -1078,21 +1061,21 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), true)); db.addTransport(txn, transportId, 123); - assertEquals(handshakeKeySetId, - db.addHandshakeKeys(txn, contactId, keys)); + assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); // Update the reordering window and retrieve the handshake keys random.nextBytes(bitmap); - db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod, + db.setReorderingWindow(txn, keySetId, transportId, timePeriod, base + 1, bitmap); - Collection newKeys = - db.getHandshakeKeys(txn, transportId); + Collection newKeys = + db.getTransportKeys(txn, transportId); assertEquals(1, newKeys.size()); - HandshakeKeySet ks = newKeys.iterator().next(); - assertEquals(handshakeKeySetId, ks.getKeySetId()); + TransportKeySet ks = newKeys.iterator().next(); + assertEquals(keySetId, ks.getKeySetId()); assertEquals(contactId, ks.getContactId()); - HandshakeKeys k = ks.getKeys(); + TransportKeys k = ks.getKeys(); assertEquals(transportId, k.getTransportId()); + assertNotNull(k.getRootKey()); assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes()); assertEquals(alice, k.isAlice()); IncomingKeys inCurr = k.getCurrentIncomingKeys(); @@ -2308,7 +2291,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); } - private HandshakeKeys createHandshakeKeys(long timePeriod, + private TransportKeys createHandshakeKeys(long timePeriod, SecretKey rootKey, boolean alice) { SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey(); @@ -2326,7 +2309,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { SecretKey outCurrHeaderKey = getSecretKey(); OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, timePeriod, 456, true); - return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr, + return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr, rootKey, alice); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java index a370d6be7..df8787115 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java @@ -101,8 +101,8 @@ public class SyncIntegrationTest extends BrambleTestCase { private byte[] write() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamContext ctx = new StreamContext(contactId, transportId, tagKey, - headerKey, streamNumber); + StreamContext ctx = new StreamContext(contactId, null, transportId, + tagKey, headerKey, streamNumber, false); StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, ctx); SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter( @@ -131,8 +131,8 @@ public class SyncIntegrationTest extends BrambleTestCase { assertArrayEquals(expectedTag, tag); // Create the readers - StreamContext ctx = new StreamContext(contactId, transportId, tagKey, - headerKey, streamNumber); + StreamContext ctx = new StreamContext(contactId, null, transportId, + tagKey, headerKey, streamNumber, false); InputStream streamReader = streamReaderFactory.createStreamReader(in, ctx); SyncRecordReader recordReader = recordReaderFactory.createRecordReader( 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 8e78d5e56..e81264c3f 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 @@ -8,8 +8,8 @@ import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.StreamContext; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.DbExpectations; import org.jmock.Expectations; @@ -43,11 +43,11 @@ public class KeyManagerImplTest extends BrambleMockTestCase { private final DeterministicExecutor executor = new DeterministicExecutor(); private final Transaction txn = new Transaction(null, false); private final ContactId contactId = getContactId(); - private final TransportKeySetId keySetId = new TransportKeySetId(345); + private final KeySetId keySetId = new KeySetId(345); private final TransportId transportId = getTransportId(); private final TransportId unknownTransportId = getTransportId(); private final StreamContext streamContext = new StreamContext(contactId, - transportId, getSecretKey(), getSecretKey(), 1); + null, transportId, getSecretKey(), getSecretKey(), 1, false); private final byte[] tag = getRandomBytes(TAG_LENGTH); private final Random random = new Random(); @@ -95,8 +95,8 @@ public class KeyManagerImplTest extends BrambleMockTestCase { will(returnValue(keySetId)); }}); - Map ids = keyManager.addContact(txn, - contactId, secretKey, timestamp, alice, active); + Map ids = keyManager.addContact(txn, contactId, + secretKey, timestamp, alice, active); assertEquals(singletonMap(transportId, keySetId), ids); } 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 5acae322e..1aeabdc44 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 @@ -8,10 +8,10 @@ import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.transport.IncomingKeys; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.TransportKeySet; -import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.DbExpectations; @@ -61,22 +61,23 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; private final ContactId contactId = getContactId(); private final ContactId contactId1 = getContactId(); - private final TransportKeySetId keySetId = new TransportKeySetId(345); - private final TransportKeySetId keySetId1 = new TransportKeySetId(456); + private final KeySetId keySetId = new KeySetId(345); + private final KeySetId keySetId1 = new KeySetId(456); private final SecretKey tagKey = getSecretKey(); private final SecretKey headerKey = getSecretKey(); private final SecretKey rootKey = getSecretKey(); private final Random random = new Random(); @Test - public void testKeysAreRotatedAtStartup() throws Exception { - TransportKeys shouldRotate = createTransportKeys(900, 0, true); - TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true); + public void testKeysAreUpdatedAtStartup() throws Exception { + TransportKeys shouldUpdate = createTransportKeys(900, 0, true); + TransportKeys shouldNotUpdate = createTransportKeys(1000, 0, true); Collection loaded = asList( - new TransportKeySet(keySetId, contactId, shouldRotate), - new TransportKeySet(keySetId1, contactId1, shouldNotRotate) + new TransportKeySet(keySetId, contactId, null, shouldUpdate), + new TransportKeySet(keySetId1, contactId1, null, + shouldNotUpdate) ); - TransportKeys rotated = createTransportKeys(1000, 0, true); + TransportKeys updated = createTransportKeys(1000, 0, true); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ @@ -86,11 +87,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { // Load the transport keys oneOf(db).getTransportKeys(txn, transportId); will(returnValue(loaded)); - // Rotate the transport keys - oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000); - will(returnValue(rotated)); - oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000); - will(returnValue(shouldNotRotate)); + // Update the transport keys + oneOf(transportCrypto).updateTransportKeys(shouldUpdate, 1000); + will(returnValue(updated)); + oneOf(transportCrypto).updateTransportKeys(shouldNotUpdate, 1000); + will(returnValue(shouldNotUpdate)); // Encode the tags (3 sets per contact) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(6).of(transportCrypto).encodeTag( @@ -98,10 +99,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } - // Save the keys that were rotated + // Save the keys that were updated oneOf(db).updateTransportKeys(txn, singletonList( - new TransportKeySet(keySetId, contactId, rotated))); - // Schedule key rotation at the start of the next time period + new TransportKeySet(keySetId, contactId, null, updated))); + // Schedule a key update at the start of the next time period oneOf(scheduler).schedule(with(any(Runnable.class)), with(timePeriodLength - 1), with(MILLISECONDS)); }}); @@ -114,22 +115,22 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { } @Test - public void testKeysAreRotatedWhenAddingContact() throws Exception { + public void testKeysAreUpdatedWhenAddingContact() throws Exception { boolean alice = random.nextBoolean(); TransportKeys transportKeys = createTransportKeys(999, 0, true); - TransportKeys rotated = createTransportKeys(1000, 0, true); + TransportKeys updated = createTransportKeys(1000, 0, true); Transaction txn = new Transaction(null, false); context.checking(new Expectations() {{ - oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey, + oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, 999, alice, true); will(returnValue(transportKeys)); // Get the current time (1 ms after start of time period 1000) oneOf(clock).currentTimeMillis(); will(returnValue(timePeriodLength * 1000 + 1)); - // Rotate the transport keys - oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); - will(returnValue(rotated)); + // Update the transport keys + oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); + will(returnValue(updated)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(3).of(transportCrypto).encodeTag( @@ -138,7 +139,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { will(new EncodeTagAction()); } // Save the keys - oneOf(db).addTransportKeys(txn, contactId, rotated); + oneOf(db).addTransportKeys(txn, contactId, updated); will(returnValue(keySetId)); }}); @@ -173,7 +174,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { MAX_32_BIT_UNSIGNED + 1, true); Transaction txn = new Transaction(null, false); - expectAddContactNoRotation(alice, true, transportKeys, txn); + expectAddContactKeysNotUpdated(alice, true, transportKeys, txn); TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( db, transportCrypto, dbExecutor, scheduler, clock, transportId, @@ -194,7 +195,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { MAX_32_BIT_UNSIGNED, true); Transaction txn = new Transaction(null, false); - expectAddContactNoRotation(alice, true, transportKeys, txn); + expectAddContactKeysNotUpdated(alice, true, transportKeys, txn); context.checking(new Expectations() {{ // Increment the stream counter @@ -231,7 +232,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { TransportKeys transportKeys = createTransportKeys(1000, 0, active); Transaction txn = new Transaction(null, false); - expectAddContactNoRotation(alice, active, transportKeys, txn); + expectAddContactKeysNotUpdated(alice, active, transportKeys, txn); TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( db, transportCrypto, dbExecutor, scheduler, clock, transportId, @@ -257,7 +258,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { List tags = new ArrayList<>(); context.checking(new Expectations() {{ - oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey, + oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, 1000, alice, true); will(returnValue(transportKeys)); // Get the current time (the start of time period 1000) @@ -270,8 +271,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction(tags)); } - // Rotate the transport keys (the keys are unaffected) - oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); + // Updated the transport keys (the keys are unaffected) + oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); @@ -312,11 +313,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { } @Test - public void testKeysAreRotatedToCurrentPeriod() throws Exception { + public void testKeysAreUpdatedToCurrentPeriod() throws Exception { TransportKeys transportKeys = createTransportKeys(1000, 0, true); Collection loaded = singletonList( - new TransportKeySet(keySetId, contactId, transportKeys)); - TransportKeys rotated = createTransportKeys(1001, 0, true); + new TransportKeySet(keySetId, contactId, null, transportKeys)); + TransportKeys updated = createTransportKeys(1001, 0, true); Transaction txn = new Transaction(null, false); Transaction txn1 = new Transaction(null, false); @@ -327,8 +328,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { // Load the transport keys oneOf(db).getTransportKeys(txn, transportId); will(returnValue(loaded)); - // Rotate the transport keys (the keys are unaffected) - oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); + // Update the transport keys (the keys are unaffected) + oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { @@ -337,21 +338,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } - // Schedule key rotation at the start of the next time period + // Schedule a key update at the start of the next time period oneOf(scheduler).schedule(with(any(Runnable.class)), with(timePeriodLength), with(MILLISECONDS)); will(new RunAction()); oneOf(dbExecutor).execute(with(any(Runnable.class))); will(new RunAction()); - // Start a transaction for key rotation + // Start a transaction for updating keys oneOf(db).transaction(with(false), withDbRunnable(txn1)); // Get the current time (the start of time period 1001) oneOf(clock).currentTimeMillis(); will(returnValue(timePeriodLength * 1001)); - // Rotate the transport keys - oneOf(transportCrypto).rotateTransportKeys( + // Update the transport keys + oneOf(transportCrypto).updateTransportKeys( with(any(TransportKeys.class)), with(1001L)); - will(returnValue(rotated)); + will(returnValue(updated)); // Encode the tags (3 sets) for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { exactly(3).of(transportCrypto).encodeTag( @@ -359,10 +360,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } - // Save the keys that were rotated + // Save the keys that were updated oneOf(db).updateTransportKeys(txn1, singletonList( - new TransportKeySet(keySetId, contactId, rotated))); - // Schedule key rotation at the start of the next time period + new TransportKeySet(keySetId, contactId, null, updated))); + // Schedule a key update at the start of the next time period oneOf(scheduler).schedule(with(any(Runnable.class)), with(timePeriodLength), with(MILLISECONDS)); }}); @@ -380,7 +381,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { TransportKeys transportKeys = createTransportKeys(1000, 0, false); Transaction txn = new Transaction(null, false); - expectAddContactNoRotation(alice, false, transportKeys, txn); + expectAddContactKeysNotUpdated(alice, false, transportKeys, txn); context.checking(new Expectations() {{ // Activate the keys @@ -422,7 +423,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { List tags = new ArrayList<>(); context.checking(new Expectations() {{ - oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey, + oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, 1000, alice, false); will(returnValue(transportKeys)); // Get the current time (the start of time period 1000) @@ -435,8 +436,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction(tags)); } - // Rotate the transport keys (the keys are unaffected) - oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); + // Update the transport keys (the keys are unaffected) + oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); @@ -486,10 +487,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { assertEquals(0L, ctx.getStreamNumber()); } - private void expectAddContactNoRotation(boolean alice, boolean active, + private void expectAddContactKeysNotUpdated(boolean alice, boolean active, TransportKeys transportKeys, Transaction txn) throws Exception { context.checking(new Expectations() {{ - oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey, + oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, 1000, alice, active); will(returnValue(transportKeys)); // Get the current time (the start of time period 1000) @@ -502,8 +503,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase { with(PROTOCOL_VERSION), with(i)); will(new EncodeTagAction()); } - // Rotate the transport keys (the keys are unaffected) - oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); + // Upate the transport keys (the keys are unaffected) + oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); will(returnValue(transportKeys)); // Save the keys oneOf(db).addTransportKeys(txn, contactId, transportKeys); 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 6fb2b9421..28234791b 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 @@ -25,7 +25,7 @@ import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.transport.KeyManager; -import org.briarproject.bramble.api.transport.TransportKeySetId; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.SessionId; @@ -432,7 +432,7 @@ class IntroduceeProtocolEngine s.getRemote().acceptTimestamp); if (timestamp == -1) throw new AssertionError(); - Map keys = null; + Map keys = null; try { contactManager.addContact(txn, s.getRemote().author, localAuthor.getId(), false); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java index 1f4e902c5..13fd4df0f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java @@ -10,7 +10,7 @@ import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.bramble.api.transport.TransportKeySetId; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.introduction.Role; @@ -35,12 +35,12 @@ class IntroduceeSession extends Session @Nullable private final byte[] masterKey; @Nullable - private final Map transportKeys; + private final Map transportKeys; IntroduceeSession(SessionId sessionId, IntroduceeState state, long requestTimestamp, GroupId contactGroupId, Author introducer, Local local, Remote remote, @Nullable byte[] masterKey, - @Nullable Map transportKeys) { + @Nullable Map transportKeys) { super(sessionId, state, requestTimestamp); this.contactGroupId = contactGroupId; this.introducer = introducer; @@ -115,8 +115,7 @@ class IntroduceeSession extends Session } static IntroduceeSession awaitActivate(IntroduceeSession s, AuthMessage m, - Message sent, - @Nullable Map transportKeys) { + Message sent, @Nullable Map transportKeys) { Local local = new Local(s.local, sent.getId(), sent.getTimestamp()); Remote remote = new Remote(s.remote, m.getMessageId()); return new IntroduceeSession(s.getSessionId(), AWAIT_ACTIVATE, @@ -183,7 +182,7 @@ class IntroduceeSession extends Session } @Nullable - Map getTransportKeys() { + Map getTransportKeys() { return transportKeys; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java index 434608503..8f9819cf8 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java @@ -6,13 +6,14 @@ import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.transport.TransportKeySetId; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.briar.introduction.IntroduceeSession.Common; import org.briarproject.briar.introduction.IntroduceeSession.Local; import org.briarproject.briar.introduction.IntroduceeSession.Remote; import org.briarproject.briar.introduction.IntroducerSession.Introducee; import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -143,10 +144,10 @@ class SessionEncoderImpl implements SessionEncoder { @Nullable private BdfDictionary encodeTransportKeys( - @Nullable Map keys) { + @Nullable Map keys) { if (keys == null) return null; BdfDictionary d = new BdfDictionary(); - for (Map.Entry e : keys.entrySet()) { + for (Entry e : keys.entrySet()) { d.put(e.getKey().getString(), e.getValue().getInt()); } return d; diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java index 421b93649..938a6a7ab 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java @@ -14,7 +14,7 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.bramble.api.transport.TransportKeySetId; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.introduction.Role; import org.briarproject.briar.introduction.IntroduceeSession.Local; @@ -114,7 +114,7 @@ class SessionParserImpl implements SessionParser { Local local = parseLocal(d.getDictionary(SESSION_KEY_LOCAL)); Remote remote = parseRemote(d.getDictionary(SESSION_KEY_REMOTE)); byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY); - Map transportKeys = parseTransportKeys( + Map transportKeys = parseTransportKeys( d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS)); return new IntroduceeSession(sessionId, state, requestTimestamp, introducerGroupId, introducer, local, remote, @@ -188,13 +188,13 @@ class SessionParserImpl implements SessionParser { } @Nullable - private Map parseTransportKeys( + private Map parseTransportKeys( @Nullable BdfDictionary d) throws FormatException { if (d == null) return null; - Map map = new HashMap<>(d.size()); + Map map = new HashMap<>(d.size()); for (String key : d.keySet()) { map.put(new TransportId(key), - new TransportKeySetId(d.getLong(key).intValue())); + new KeySetId(d.getLong(key).intValue())); } return map; } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java index d11f1b32a..ae31dcf06 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java @@ -12,7 +12,7 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.bramble.api.transport.TransportKeySetId; +import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.introduction.IntroducerSession.Introducee; @@ -76,8 +76,7 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase { getTransportPropertiesMap(3); private final Map remoteTransportProperties = getTransportPropertiesMap(3); - private final Map transportKeys = - new HashMap<>(); + private final Map transportKeys = new HashMap<>(); private final byte[] localMacKey = getRandomBytes(SecretKey.LENGTH); private final byte[] remoteMacKey = getRandomBytes(SecretKey.LENGTH); @@ -90,9 +89,9 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase { sessionParser = new SessionParserImpl(clientHelper); author1 = getRealAuthor(authorFactory); author2 = getRealAuthor(authorFactory); - transportKeys.put(getTransportId(), new TransportKeySetId(1)); - transportKeys.put(getTransportId(), new TransportKeySetId(2)); - transportKeys.put(getTransportId(), new TransportKeySetId(3)); + transportKeys.put(getTransportId(), new KeySetId(1)); + transportKeys.put(getTransportId(), new KeySetId(2)); + transportKeys.put(getTransportId(), new KeySetId(3)); } @Test