Merge branch 'static-transport-keys' into 'master'

Add database support for pending contacts and handshake keys

See merge request briar/briar!1078
This commit is contained in:
Torsten Grote
2019-04-22 14:00:52 +00:00
56 changed files with 2588 additions and 727 deletions

View File

@@ -18,30 +18,30 @@ public interface ContactExchangeTask {
byte PROTOCOL_VERSION = 1; byte PROTOCOL_VERSION = 1;
/** /**
* Label for deriving Alice's header key from the master secret. * Label for deriving Alice's header key from the master key.
*/ */
String ALICE_KEY_LABEL = String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY"; "org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's header key from the master secret. * Label for deriving Bob's header key from the master key.
*/ */
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY"; String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/** /**
* Label for deriving Alice's key binding nonce from the master secret. * Label for deriving Alice's key binding nonce from the master key.
*/ */
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE"; String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/** /**
* Label for deriving Bob's key binding nonce from the master secret. * Label for deriving Bob's key binding nonce from the master key.
*/ */
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE"; String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/** /**
* Exchanges contact information with a remote peer. * Exchanges contact information with a remote peer.
*/ */
void startExchange(LocalAuthor localAuthor, SecretKey masterSecret, void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice); boolean alice);
} }

View File

@@ -13,8 +13,6 @@ import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.bramble.api.contact.PendingContact.PendingContactState.FAILED;
@NotNullByDefault @NotNullByDefault
public interface ContactManager { public interface ContactManager {
@@ -33,7 +31,7 @@ public interface ContactManager {
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException; boolean active) throws DbException;
/** /**
@@ -50,7 +48,7 @@ public interface ContactManager {
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Author remote, AuthorId local, SecretKey master, ContactId addContact(Author remote, AuthorId local, SecretKey rootKey,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException; throws DbException;
@@ -79,7 +77,8 @@ public interface ContactManager {
Collection<PendingContact> getPendingContacts(); Collection<PendingContact> getPendingContacts();
/** /**
* Removes a {@link PendingContact} that is in state {@link FAILED}. * Removes a {@link PendingContact} that is in state
* {@link PendingContactState FAILED}.
*/ */
void removePendingContact(PendingContact pendingContact); void removePendingContact(PendingContact pendingContact);

View File

@@ -8,26 +8,29 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class PendingContact { public class PendingContact {
public enum PendingContactState {
WAITING_FOR_CONNECTION,
CONNECTED,
ADDING_CONTACT,
FAILED
}
private final PendingContactId id; private final PendingContactId id;
private final byte[] publicKey;
private final String alias; private final String alias;
private final PendingContactState state; private final PendingContactState state;
private final long timestamp; private final long timestamp;
public PendingContact(PendingContactId id, String alias, public PendingContact(PendingContactId id, byte[] publicKey,
PendingContactState state, long timestamp) { String alias, PendingContactState state, long timestamp) {
this.id = id; this.id = id;
this.publicKey = publicKey;
this.alias = alias; this.alias = alias;
this.state = state; this.state = state;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public PendingContactId getId() {
return id;
}
public byte[] getPublicKey() {
return publicKey;
}
public String getAlias() { public String getAlias() {
return alias; return alias;
} }
@@ -50,5 +53,4 @@ public class PendingContact {
return o instanceof PendingContact && return o instanceof PendingContact &&
id.equals(((PendingContact) o).id); id.equals(((PendingContact) o).id);
} }
} }

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum PendingContactState {
WAITING_FOR_CONNECTION(0),
CONNECTED(1),
ADDING_CONTACT(2),
FAILED(3);
private final int value;
PendingContactState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static PendingContactState fromValue(int value) {
for (PendingContactState s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.contact.event; package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContact.PendingContactState;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
/** /**
@@ -11,19 +12,34 @@ public interface TransportCrypto {
/** /**
* Derives initial transport keys for the given transport in the given * Derives initial transport keys for the given transport in the given
* rotation period from the given master secret. * 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.
* @param active whether the keys are usable for outgoing streams. * @param active whether the keys are usable for outgoing streams.
*/ */
TransportKeys deriveTransportKeys(TransportId t, SecretKey master, TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
long rotationPeriod, boolean alice, boolean active); long timePeriod, boolean alice, boolean active);
/** /**
* Rotates the given transport keys to the given rotation period. If the * Rotates the given transport keys to the given time period. If the keys
* keys are for the given period or any later period they are not rotated. * are for the given period or any later period they are not rotated.
*/ */
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); 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.
*/
HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice);
/**
* Updates the given handshake 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);
/** /**
* Encodes the pseudo-random tag that is used to recognise a stream. * Encodes the pseudo-random tag that is used to recognise a stream.

View File

@@ -2,6 +2,8 @@ package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -20,8 +22,11 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
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.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -108,6 +113,20 @@ public interface DatabaseComponent {
*/ */
void addGroup(Transaction txn, Group g) throws DbException; 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 a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -119,6 +138,12 @@ public interface DatabaseComponent {
void addLocalMessage(Transaction txn, Message m, Metadata meta, void addLocalMessage(Transaction txn, Message m, Metadata meta,
boolean shared) throws DbException; boolean shared) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(Transaction txn, PendingContact p)
throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -129,27 +154,41 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.
* <p/>
* Read-only.
*/ */
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local) boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
throws DbException; throws DbException;
/** /**
* Returns true if the database contains the given group. * Returns true if the database contains the given group.
* <p/>
* Read-only.
*/ */
boolean containsGroup(Transaction txn, GroupId g) throws DbException; boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains the given local author. * Returns true if the database contains the given local author.
* <p/>
* Read-only.
*/ */
boolean containsLocalAuthor(Transaction txn, AuthorId local) boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException; throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID, * {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -269,6 +308,14 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g) Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -417,6 +464,14 @@ public interface DatabaseComponent {
*/ */
long getNextSendTime(Transaction txn, ContactId c) throws DbException; long getNextSendTime(Transaction txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(Transaction txn)
throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/> * <p/>
@@ -429,14 +484,20 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t) Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException; 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. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k) void incrementStreamCounter(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
@@ -491,6 +552,12 @@ public interface DatabaseComponent {
*/ */
void removeGroup(Transaction txn, Group g) throws DbException; 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 a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -501,6 +568,12 @@ public interface DatabaseComponent {
*/ */
void removeMessage(Transaction txn, MessageId m) throws DbException; void removeMessage(Transaction txn, MessageId m) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -509,8 +582,8 @@ public interface DatabaseComponent {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k) void removeTransportKeys(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Marks the given contact as verified. * Marks the given contact as verified.
@@ -553,21 +626,36 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException; Collection<MessageId> dependencies) throws DbException;
/** /**
* Sets the reordering window for the given key set and transport in the * Sets the reordering window for the given transport key set in the given
* given rotation period. * time period.
*/ */
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, TransportKeySetId k,
long rotationPeriod, long base, byte[] bitmap) throws DbException; 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;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k) 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<HandshakeKeySet> keys)
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Stores the given transport keys, deleting any keys they have replaced.
*/ */
void updateTransportKeys(Transaction txn, Collection<KeySet> keys) void updateTransportKeys(Transaction txn, Collection<TransportKeySet> keys)
throws DbException; throws DbException;
} }

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a database operation is attempted for a pending contact that is
* not in the database. This exception may occur due to concurrent updates and
* does not indicate a database error.
*/
public class NoSuchPendingContactException extends DbException {
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class PendingContactExistsException extends DbException {
}

View File

@@ -40,8 +40,8 @@ public interface KeyAgreementConstants {
"org.briarproject.bramble.keyagreement/SHARED_SECRET"; "org.briarproject.bramble.keyagreement/SHARED_SECRET";
/** /**
* Label for deriving the master secret. * Label for deriving the master key.
*/ */
String MASTER_SECRET_LABEL = String MASTER_KEY_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET"; "org.briarproject.bramble.keyagreement/MASTER_SECRET";
} }

View File

@@ -0,0 +1,57 @@
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();
}
}

View File

@@ -0,0 +1,70 @@
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);
}
}

View File

@@ -0,0 +1,36 @@
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 HandshakeKeySet set of handshake keys} within the scope of the local
* device.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySetId {
private final int id;
public HandshakeKeySetId(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 HandshakeKeySetId &&
id == ((HandshakeKeySetId) o).id;
}
}

View File

@@ -0,0 +1,36 @@
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;
}
}

View File

@@ -9,27 +9,27 @@ import static org.briarproject.bramble.api.transport.TransportConstants.REORDERI
/** /**
* Contains transport keys for receiving streams from a given contact over a * Contains transport keys for receiving streams from a given contact over a
* given transport in a given rotation period. * given transport in a given time period.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class IncomingKeys { public class IncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod, windowBase; private final long timePeriod, windowBase;
private final byte[] windowBitmap; private final byte[] windowBitmap;
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod) { long timePeriod) {
this(tagKey, headerKey, rotationPeriod, 0, this(tagKey, headerKey, timePeriod, 0,
new byte[REORDERING_WINDOW_SIZE / 8]); new byte[REORDERING_WINDOW_SIZE / 8]);
} }
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, long windowBase, byte[] windowBitmap) { long timePeriod, long windowBase, byte[] windowBitmap) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.rotationPeriod = rotationPeriod; this.timePeriod = timePeriod;
this.windowBase = windowBase; this.windowBase = windowBase;
this.windowBitmap = windowBitmap; this.windowBitmap = windowBitmap;
} }
@@ -42,8 +42,8 @@ public class IncomingKeys {
return headerKey; return headerKey;
} }
public long getRotationPeriod() { public long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
public long getWindowBase() { public long getWindowBase() {

View File

@@ -27,14 +27,14 @@ public interface KeyManager {
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams * @param active whether the derived keys can be used for outgoing streams
*/ */
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c, Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
SecretKey master, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException; throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
throws DbException; throws DbException;
/** /**

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A set of transport keys for communicating with a contact.
*/
@Immutable
@NotNullByDefault
public class KeySet {
private final KeySetId keySetId;
private final ContactId contactId;
private final TransportKeys transportKeys;
public KeySet(KeySetId keySetId, ContactId contactId,
TransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.transportKeys = transportKeys;
}
public KeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getTransportKeys() {
return transportKeys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof KeySet && keySetId.equals(((KeySet) o).keySetId);
}
}

View File

@@ -7,26 +7,26 @@ import javax.annotation.concurrent.Immutable;
/** /**
* Contains transport keys for sending streams to a given contact over a given * Contains transport keys for sending streams to a given contact over a given
* transport in a given rotation period. * transport in a given time period.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class OutgoingKeys { public class OutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod, streamCounter; private final long timePeriod, streamCounter;
private final boolean active; private final boolean active;
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, boolean active) { long timePeriod, boolean active) {
this(tagKey, headerKey, rotationPeriod, 0, active); this(tagKey, headerKey, timePeriod, 0, active);
} }
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, long streamCounter, boolean active) { long timePeriod, long streamCounter, boolean active) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.rotationPeriod = rotationPeriod; this.timePeriod = timePeriod;
this.streamCounter = streamCounter; this.streamCounter = streamCounter;
this.active = active; this.active = active;
} }
@@ -39,8 +39,8 @@ public class OutgoingKeys {
return headerKey; return headerKey;
} }
public long getRotationPeriod() { public long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
public long getStreamCounter() { public long getStreamCounter() {

View File

@@ -82,30 +82,58 @@ public interface TransportConstants {
int REORDERING_WINDOW_SIZE = 32; int REORDERING_WINDOW_SIZE = 32;
/** /**
* Label for deriving Alice's initial tag key from the master secret. * Label for deriving Alice's initial tag key from the root key in
* rotation mode.
*/ */
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY"; String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
/** /**
* Label for deriving Bob's initial tag key from the master secret. * Label for deriving Bob's initial tag key from the root key in rotation
* mode.
*/ */
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY"; String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
/** /**
* Label for deriving Alice's initial header key from the master secret. * Label for deriving Alice's initial header key from the root key in
* rotation mode.
*/ */
String ALICE_HEADER_LABEL = String ALICE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HEADER_KEY"; "org.briarproject.bramble.transport/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's initial header key from the master secret. * Label for deriving Bob's initial header key from the root key in
* rotation mode.
*/ */
String BOB_HEADER_LABEL = String BOB_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HEADER_KEY"; "org.briarproject.bramble.transport/BOB_HEADER_KEY";
/** /**
* Label for deriving the next period's key in key rotation. * Label for deriving the next period's key in rotation mode.
*/ */
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE"; String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
/**
* Label for deriving Alice's tag key from the root key in handshake mode.
*/
String ALICE_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Bob's tag key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Alice's header key from the root key in handshake
* mode.
*/
String ALICE_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_HEADER_KEY";
} }

View File

@@ -0,0 +1,49 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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.
*/
@Immutable
@NotNullByDefault
public class TransportKeySet {
private final TransportKeySetId keySetId;
private final ContactId contactId;
private final TransportKeys keys;
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
TransportKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
}
public TransportKeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof TransportKeySet &&
keySetId.equals(((TransportKeySet) o).keySetId);
}
}

View File

@@ -5,18 +5,19 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Type-safe wrapper for an integer that uniquely identifies a set of transport * Type-safe wrapper for an integer that uniquely identifies a
* keys within the scope of the local device. * {@link TransportKeySet set of transport keys} within the scope of the local
* device.
* <p/> * <p/>
* Key sets created on a given device must have increasing identifiers. * Key sets created on a given device must have increasing identifiers.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class KeySetId { public class TransportKeySetId {
private final int id; private final int id;
public KeySetId(int id) { public TransportKeySetId(int id) {
this.id = id; this.id = id;
} }
@@ -31,6 +32,7 @@ public class KeySetId {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof KeySetId && id == ((KeySetId) o).id; return o instanceof TransportKeySetId &&
id == ((TransportKeySetId) o).id;
} }
} }

View File

@@ -6,52 +6,15 @@ import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Keys for communicating with a given contact over a given transport. * Keys for communicating with a given contact over a given transport. Unlike
* {@link HandshakeKeys} these keys provide forward secrecy.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportKeys { public class TransportKeys extends AbstractTransportKeys {
private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
public TransportKeys(TransportId transportId, IncomingKeys inPrev, public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
if (inPrev.getRotationPeriod() != outCurr.getRotationPeriod() - 1) super(transportId, inPrev, inCurr, inNext, outCurr);
throw new IllegalArgumentException();
if (inCurr.getRotationPeriod() != outCurr.getRotationPeriod())
throw new IllegalArgumentException();
if (inNext.getRotationPeriod() != outCurr.getRotationPeriod() + 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 getRotationPeriod() {
return outCurr.getRotationPeriod();
} }
} }

View File

@@ -1,6 +1,8 @@
package org.briarproject.bramble.test; package org.briarproject.bramble.test;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -25,6 +27,7 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -140,6 +143,18 @@ public class TestUtils {
return new Message(id, groupId, timestamp, body); return new Message(id, groupId, timestamp, body);
} }
public static PendingContact getPendingContact() {
return getPendingContact(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static PendingContact getPendingContact(int nameLength) {
PendingContactId id = new PendingContactId(getRandomId());
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String alias = getRandomString(nameLength);
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
}
public static double getMedian(Collection<? extends Number> samples) { public static double getMedian(Collection<? extends Number> samples) {
int size = samples.size(); int size = samples.size();
if (size == 0) throw new IllegalArgumentException(); if (size == 0) throw new IllegalArgumentException();

View File

@@ -77,7 +77,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private volatile LocalAuthor localAuthor; private volatile LocalAuthor localAuthor;
private volatile DuplexTransportConnection conn; private volatile DuplexTransportConnection conn;
private volatile TransportId transportId; private volatile TransportId transportId;
private volatile SecretKey masterSecret; private volatile SecretKey masterKey;
private volatile boolean alice; private volatile boolean alice;
@Inject @Inject
@@ -104,13 +104,13 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
@Override @Override
public void startExchange(LocalAuthor localAuthor, SecretKey masterSecret, public void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice) { boolean alice) {
this.localAuthor = localAuthor; this.localAuthor = localAuthor;
this.conn = conn; this.conn = conn;
this.transportId = transportId; this.transportId = transportId;
this.masterSecret = masterSecret; this.masterKey = masterKey;
this.alice = alice; this.alice = alice;
start(); start();
} }
@@ -142,9 +142,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
// Derive the header keys for the transport streams // Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, masterKey,
masterSecret, new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret, SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
// Create the readers // Create the readers
@@ -163,9 +163,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
.createRecordWriter(streamWriter.getOutputStream()); .createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret, byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret, byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] localNonce = alice ? aliceNonce : bobNonce; byte[] localNonce = alice ? aliceNonce : bobNonce;
byte[] remoteNonce = alice ? bobNonce : aliceNonce; byte[] remoteNonce = alice ? bobNonce : aliceNonce;
@@ -293,7 +293,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
throws DbException { throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
ContactId contactId = contactManager.addContact(txn, remoteAuthor, ContactId contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterSecret, timestamp, alice, localAuthor.getId(), masterKey, timestamp, alice,
true, true); true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);

View File

@@ -30,8 +30,9 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.contact.PendingContact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
@@ -69,10 +70,10 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException { boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice, active); keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;
@@ -88,11 +89,11 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public ContactId addContact(Author remote, AuthorId local, SecretKey master, public ContactId addContact(Author remote, AuthorId local,
long timestamp, boolean alice, boolean verified, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean verified,
throws DbException { boolean active) throws DbException {
return db.transactionWithResult(false, txn -> return db.transactionWithResult(false, txn ->
addContact(txn, remote, local, master, timestamp, alice, addContact(txn, remote, local, rootKey, timestamp, alice,
verified, active)); verified, active));
} }
@@ -123,8 +124,8 @@ class ContactManagerImpl implements ContactManager {
public PendingContact addRemoteContactRequest(String link, String alias) { public PendingContact addRemoteContactRequest(String link, String alias) {
// TODO replace with real implementation // TODO replace with real implementation
PendingContactId id = new PendingContactId(link.getBytes()); PendingContactId id = new PendingContactId(link.getBytes());
return new PendingContact(id, alias, WAITING_FOR_CONNECTION, return new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH], alias,
System.currentTimeMillis()); WAITING_FOR_CONNECTION, System.currentTimeMillis());
} }
@Override @Override

View File

@@ -4,18 +4,22 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId; 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.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.Blake2bDigest; import org.spongycastle.crypto.digests.Blake2bDigest;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
@@ -24,6 +28,9 @@ import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.writeUint16;
import static org.briarproject.bramble.util.ByteUtils.writeUint64;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
class TransportCryptoImpl implements TransportCrypto { class TransportCryptoImpl implements TransportCrypto {
@@ -36,45 +43,44 @@ class TransportCryptoImpl implements TransportCrypto {
@Override @Override
public TransportKeys deriveTransportKeys(TransportId t, public TransportKeys deriveTransportKeys(TransportId t,
SecretKey master, long rotationPeriod, boolean alice, SecretKey rootKey, long timePeriod, boolean weAreAlice,
boolean active) { boolean active) {
// Keys for the previous period are derived from the master secret // Keys for the previous period are derived from the root key
SecretKey inTagPrev = deriveTagKey(master, t, !alice); SecretKey inTagPrev = deriveTagKey(rootKey, t, !weAreAlice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice); SecretKey inHeaderPrev = deriveHeaderKey(rootKey, t, !weAreAlice);
SecretKey outTagPrev = deriveTagKey(master, t, alice); SecretKey outTagPrev = deriveTagKey(rootKey, t, weAreAlice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice); SecretKey outHeaderPrev = deriveHeaderKey(rootKey, t, weAreAlice);
// Derive the keys for the current and next periods // Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod); SecretKey inTagCurr = rotateKey(inTagPrev, timePeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod); SecretKey inHeaderCurr = rotateKey(inHeaderPrev, timePeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1); SecretKey inTagNext = rotateKey(inTagCurr, timePeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1); SecretKey inHeaderNext = rotateKey(inHeaderCurr, timePeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod); SecretKey outTagCurr = rotateKey(outTagPrev, timePeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod); SecretKey outHeaderCurr = rotateKey(outHeaderPrev, timePeriod);
// Initialise the reordering windows and stream counters // Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev, IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
rotationPeriod - 1); timePeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr, IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
rotationPeriod); timePeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext, IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
rotationPeriod + 1); timePeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr, OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
rotationPeriod, active); timePeriod, active);
// Collect and return the keys // Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
} }
@Override @Override
public TransportKeys rotateTransportKeys(TransportKeys k, public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
long rotationPeriod) { if (k.getTimePeriod() >= timePeriod) return k;
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys(); IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getRotationPeriod(); long startPeriod = outCurr.getTimePeriod();
boolean active = outCurr.isActive(); boolean active = outCurr.isActive();
// Rotate the keys // Rotate the keys
for (long p = startPeriod + 1; p <= rotationPeriod; p++) { for (long p = startPeriod + 1; p <= timePeriod; p++) {
inPrev = inCurr; inPrev = inCurr;
inCurr = inNext; inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
@@ -89,24 +95,117 @@ class TransportCryptoImpl implements TransportCrypto {
outCurr); outCurr);
} }
private SecretKey rotateKey(SecretKey k, long rotationPeriod) { private SecretKey rotateKey(SecretKey k, long timePeriod) {
byte[] period = new byte[INT_64_BYTES]; byte[] period = new byte[INT_64_BYTES];
ByteUtils.writeUint64(rotationPeriod, period, 0); writeUint64(timePeriod, period, 0);
return crypto.deriveKey(ROTATE_LABEL, k, period); return crypto.deriveKey(ROTATE_LABEL, k, period);
} }
private SecretKey deriveTagKey(SecretKey master, TransportId t, private SecretKey deriveTagKey(SecretKey rootKey, TransportId t,
boolean alice) { boolean keyBelongsToAlice) {
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL; String label = keyBelongsToAlice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = StringUtils.toUtf8(t.getString()); byte[] id = toUtf8(t.getString());
return crypto.deriveKey(label, master, id); return crypto.deriveKey(label, rootKey, id);
} }
private SecretKey deriveHeaderKey(SecretKey master, TransportId t, private SecretKey deriveHeaderKey(SecretKey rootKey, TransportId t,
boolean alice) { boolean keyBelongsToAlice) {
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL; String label = keyBelongsToAlice ? ALICE_HEADER_LABEL :
byte[] id = StringUtils.toUtf8(t.getString()); BOB_HEADER_LABEL;
return crypto.deriveKey(label, master, id); byte[] id = toUtf8(t.getString());
return crypto.deriveKey(label, rootKey, id);
}
@Override
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean weAreAlice) {
if (timePeriod < 1) throw new IllegalArgumentException();
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod - 1);
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
weAreAlice);
}
private IncomingKeys deriveIncomingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, !weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, !weAreAlice,
timePeriod);
return new IncomingKeys(tag, header, timePeriod);
}
private OutgoingKeys deriveOutgoingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, weAreAlice,
timePeriod);
return new OutgoingKeys(tag, header, timePeriod, true);
}
private SecretKey deriveHandshakeTagKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_TAG_LABEL :
BOB_HANDSHAKE_TAG_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
private SecretKey deriveHandshakeHeaderKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_HEADER_LABEL :
BOB_HANDSHAKE_HEADER_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
@Override
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
long elapsed = timePeriod - k.getTimePeriod();
TransportId t = k.getTransportId();
SecretKey rootKey = k.getRootKey();
boolean weAreAlice = k.isAlice();
if (elapsed <= 0) {
// The keys are for the given period or later - don't update them
return k;
} else if (elapsed == 1) {
// The keys are one period old - shift by one period, keeping the
// reordering windows for keys we retain
IncomingKeys inPrev = k.getCurrentIncomingKeys();
IncomingKeys inCurr = k.getNextIncomingKeys();
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else if (elapsed == 2) {
// The keys are two periods old - shift by two periods, keeping
// the reordering windows for keys we retain
IncomingKeys inPrev = k.getNextIncomingKeys();
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else {
// The keys are more than two periods old - derive fresh keys
return deriveHandshakeKeys(t, rootKey, timePeriod, weAreAlice);
}
} }
@Override @Override
@@ -125,14 +224,14 @@ class TransportCryptoImpl implements TransportCrypto {
// The input is the protocol version as a 16-bit integer, followed by // The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer // the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES]; byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0); writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES]; byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0); writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length); prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength]; byte[] mac = new byte[macLength];
prf.doFinal(mac, 0); prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC // The output is the first TAG_LENGTH bytes of the MAC
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); arraycopy(mac, 0, tag, 0, TAG_LENGTH);
} }
} }

View File

@@ -2,9 +2,13 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException; import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
@@ -23,8 +27,11 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
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.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -33,11 +40,14 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A low-level interface to the database (DatabaseComponent provides a * A low-level interface to the database ({@link DatabaseComponent} provides a
* high-level interface). Most operations take a transaction argument, which is * high-level interface).
* obtained by calling {@link #startTransaction()}. Every transaction must be * <p/>
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or * Most operations take a transaction argument, which is obtained by calling
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown. * {@link #startTransaction()}. Every transaction must be terminated by calling
* either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an
* exception is thrown.
*/ */
@NotNullByDefault @NotNullByDefault
interface Database<T> { interface Database<T> {
@@ -95,6 +105,20 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException; 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 a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -121,6 +145,11 @@ interface Database<T> {
*/ */
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(T txn, PendingContact p) throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -131,7 +160,7 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException; throws DbException;
/** /**
@@ -171,6 +200,14 @@ interface Database<T> {
*/ */
boolean containsMessage(T txn, MessageId m) throws DbException; boolean containsMessage(T txn, MessageId m) throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(T txn, PendingContactId p)
throws DbException;
/** /**
* Returns true if the database contains the given transport. * Returns true if the database contains the given transport.
* <p/> * <p/>
@@ -277,6 +314,14 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g) Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -467,6 +512,13 @@ interface Database<T> {
*/ */
long getNextSendTime(T txn, ContactId c) throws DbException; long getNextSendTime(T txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(T txn) throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact and have been requested by the contact, up to the given * given contact and have been requested by the contact, up to the given
@@ -489,13 +541,19 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<KeySet> getTransportKeys(T txn, TransportId t) Collection<TransportKeySet> 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; throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(T txn, TransportId t, KeySetId k) void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -564,6 +622,12 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g) void removeGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -581,6 +645,11 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c, void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException; Collection<MessageId> requested) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(T txn, PendingContactId p) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -589,7 +658,7 @@ interface Database<T> {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(T txn, TransportId t, KeySetId k) void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -634,16 +703,29 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Sets the reordering window for the given key set and transport in the * Sets the state of the given pending contact.
* given rotation period.
*/ */
void setReorderingWindow(T txn, KeySetId k, TransportId t, void setPendingContactState(T txn, PendingContactId p,
long rotationPeriod, long base, byte[] bitmap) throws DbException; PendingContactState state) throws DbException;
/**
* Sets the reordering window for the given transport key set 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,
long timePeriod, long base, byte[] bitmap) throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(T txn, TransportId t, KeySetId k) void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -654,8 +736,13 @@ interface Database<T> {
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency) void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency)
throws DbException; throws DbException;
/**
* Updates the given handshake keys.
*/
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
/** /**
* Updates the given transport keys following key rotation. * Updates the given transport keys following key rotation.
*/ */
void updateTransportKeys(T txn, KeySet ks) throws DbException; void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
} }

View File

@@ -2,6 +2,8 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
@@ -21,8 +23,10 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.db.NullableDbCallable; import org.briarproject.bramble.api.db.NullableDbCallable;
import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.TaskAction; import org.briarproject.bramble.api.db.TaskAction;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -59,8 +63,11 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
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.api.transport.TransportKeys;
import java.util.ArrayList; import java.util.ArrayList;
@@ -253,6 +260,30 @@ class DatabaseComponentImpl<T> 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 @Override
public void addLocalAuthor(Transaction transaction, LocalAuthor a) public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException { throws DbException {
@@ -281,6 +312,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta); db.mergeMessageMetadata(txn, m.getId(), meta);
} }
@Override
public void addPendingContact(Transaction transaction, PendingContact p)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException();
db.addPendingContact(txn, p);
}
@Override @Override
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { int maxLatency) throws DbException {
@@ -291,8 +332,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public KeySetId addTransportKeys(Transaction transaction, ContactId c, public TransportKeySetId addTransportKeys(Transaction transaction,
TransportKeys k) throws DbException { ContactId c, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -325,6 +366,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsLocalAuthor(txn, local); return db.containsLocalAuthor(txn, local);
} }
@Override
public boolean containsPendingContact(Transaction transaction,
PendingContactId p) throws DbException {
T txn = unbox(transaction);
return db.containsPendingContact(txn, p);
}
@Override @Override
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -498,6 +546,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getGroupVisibility(txn, c, g); return db.getGroupVisibility(txn, c, g);
} }
@Override
public Collection<HandshakeKeySet> 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 @Override
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a) public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -655,6 +712,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getNextSendTime(txn, c); return db.getNextSendTime(txn, c);
} }
@Override
public Collection<PendingContact> getPendingContacts(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getPendingContacts(txn);
}
@Override @Override
public Settings getSettings(Transaction transaction, String namespace) public Settings getSettings(Transaction transaction, String namespace)
throws DbException { throws DbException {
@@ -663,7 +727,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public Collection<KeySet> getTransportKeys(Transaction transaction, public Collection<TransportKeySet> getTransportKeys(Transaction transaction,
TransportId t) throws DbException { TransportId t) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -673,7 +737,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void incrementStreamCounter(Transaction transaction, TransportId t, public void incrementStreamCounter(Transaction transaction, TransportId t,
KeySetId k) throws DbException { 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 {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -822,6 +896,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); 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 @Override
public void removeLocalAuthor(Transaction transaction, AuthorId a) public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -844,6 +928,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.removeMessage(txn, m); db.removeMessage(txn, m);
} }
@Override
public void removePendingContact(Transaction transaction,
PendingContactId p) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
db.removePendingContact(txn, p);
}
@Override @Override
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
@@ -856,7 +950,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void removeTransportKeys(Transaction transaction, public void removeTransportKeys(Transaction transaction,
TransportId t, KeySetId k) throws DbException { TransportId t, TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -955,19 +1049,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void setReorderingWindow(Transaction transaction, KeySetId k, public void setReorderingWindow(Transaction transaction,
TransportId t, long rotationPeriod, long base, byte[] bitmap) TransportKeySetId k, TransportId t, long timePeriod, long base,
throws DbException { byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, rotationPeriod, base, bitmap); 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 {
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 @Override
public void setTransportKeysActive(Transaction transaction, TransportId t, public void setTransportKeysActive(Transaction transaction, TransportId t,
KeySetId k) throws DbException { TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -976,12 +1081,24 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void updateTransportKeys(Transaction transaction, public void updateHandshakeKeys(Transaction transaction,
Collection<KeySet> keys) throws DbException { Collection<HandshakeKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
for (KeySet ks : keys) { for (HandshakeKeySet ks : keys) {
TransportId t = ks.getTransportKeys().getTransportId(); TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateHandshakeKeys(txn, ks);
}
}
@Override
public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (TransportKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t)) if (db.containsTransport(txn, t))
db.updateTransportKeys(txn, ks); db.updateTransportKeys(txn, ks);
} }

View File

@@ -2,6 +2,9 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
@@ -26,10 +29,13 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock; 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.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; 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.api.transport.TransportKeys;
import java.sql.Connection; import java.sql.Connection;
@@ -55,6 +61,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.sql.Types.BINARY;
import static java.sql.Types.INTEGER; import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR; import static java.sql.Types.VARCHAR;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
@@ -85,9 +92,9 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 41; static final int CODE_SCHEMA_VERSION = 42;
// Rotation period offsets for incoming transport keys // Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
private static final int OFFSET_CURR = 0; private static final int OFFSET_CURR = 0;
private static final int OFFSET_NEXT = 1; private static final int OFFSET_NEXT = 1;
@@ -248,7 +255,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE outgoingKeys" "CREATE TABLE outgoingKeys"
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER," + " keySetId _COUNTER,"
+ " rotationPeriod BIGINT NOT NULL," + " timePeriod BIGINT NOT NULL,"
+ " contactId INT NOT NULL," + " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL,"
@@ -267,8 +274,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE incomingKeys" "CREATE TABLE incomingKeys"
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL," + " keySetId INT NOT NULL,"
+ " rotationPeriod BIGINT NOT NULL," + " timePeriod BIGINT NOT NULL,"
+ " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL," + " base BIGINT NOT NULL,"
@@ -280,9 +286,57 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " ON DELETE CASCADE," + " ON DELETE CASCADE,"
+ " FOREIGN KEY (keySetId)" + " FOREIGN KEY (keySetId)"
+ " REFERENCES outgoingKeys (keySetId)" + " 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," + " ON DELETE CASCADE,"
+ " UNIQUE (keySetId),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (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)"; + " ON DELETE CASCADE)";
private static final String INDEX_CONTACTS_BY_AUTHOR_ID = private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
@@ -428,7 +482,8 @@ abstract class JdbcDatabase implements Database<Connection> {
return Arrays.asList( return Arrays.asList(
new Migration38_39(), new Migration38_39(),
new Migration39_40(), new Migration39_40(),
new Migration40_41(dbTypes) new Migration40_41(dbTypes),
new Migration41_42(dbTypes)
); );
} }
@@ -478,6 +533,11 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_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(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
@@ -715,6 +775,103 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@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 @Override
public void addLocalAuthor(Connection txn, LocalAuthor a) public void addLocalAuthor(Connection txn, LocalAuthor a)
throws DbException { throws DbException {
@@ -895,6 +1052,29 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void addPendingContact(Connection txn, PendingContact p)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO pendingContacts (pendingContactId,"
+ " publicKey, alias, state, timestamp)"
+ " VALUES (?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getId().getBytes());
ps.setBytes(2, p.getPublicKey());
ps.setString(3, p.getAlias());
ps.setInt(4, p.getState().getValue());
ps.setLong(5, p.getTimestamp());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void addTransport(Connection txn, TransportId t, int maxLatency) public void addTransport(Connection txn, TransportId t, int maxLatency)
throws DbException { throws DbException {
@@ -915,20 +1095,20 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public KeySetId addTransportKeys(Connection txn, ContactId c, public TransportKeySetId addTransportKeys(Connection txn, ContactId c,
TransportKeys k) throws DbException { TransportKeys k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Store the outgoing keys // Store the outgoing keys
String sql = "INSERT INTO outgoingKeys (contactId, transportId," String sql = "INSERT INTO outgoingKeys (contactId, transportId,"
+ " rotationPeriod, tagKey, headerKey, stream, active)" + " timePeriod, tagKey, headerKey, stream, active)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setString(2, k.getTransportId().getString()); ps.setString(2, k.getTransportId().getString());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(3, outCurr.getRotationPeriod()); ps.setLong(3, outCurr.getTimePeriod());
ps.setBytes(4, outCurr.getTagKey().getBytes()); ps.setBytes(4, outCurr.getTagKey().getBytes());
ps.setBytes(5, outCurr.getHeaderKey().getBytes()); ps.setBytes(5, outCurr.getHeaderKey().getBytes());
ps.setLong(6, outCurr.getStreamCounter()); ps.setLong(6, outCurr.getStreamCounter());
@@ -942,45 +1122,44 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException(); if (!rs.next()) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1)); TransportKeySetId keySetId = new TransportKeySetId(rs.getInt(1));
if (rs.next()) throw new DbStateException(); if (rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.close(); ps.close();
// Store the incoming keys // Store the incoming keys
sql = "INSERT INTO incomingKeys (keySetId, contactId, transportId," sql = "INSERT INTO incomingKeys (keySetId, transportId,"
+ " rotationPeriod, tagKey, headerKey, base, bitmap," + " timePeriod, tagKey, headerKey, base, bitmap,"
+ " periodOffset)" + " periodOffset)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, keySetId.getInt()); ps.setInt(1, keySetId.getInt());
ps.setInt(2, c.getInt()); ps.setString(2, k.getTransportId().getString());
ps.setString(3, k.getTransportId().getString()); // Previous time period
// Previous rotation period
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(4, inPrev.getRotationPeriod()); ps.setLong(3, inPrev.getTimePeriod());
ps.setBytes(5, inPrev.getTagKey().getBytes()); ps.setBytes(4, inPrev.getTagKey().getBytes());
ps.setBytes(6, inPrev.getHeaderKey().getBytes()); ps.setBytes(5, inPrev.getHeaderKey().getBytes());
ps.setLong(7, inPrev.getWindowBase()); ps.setLong(6, inPrev.getWindowBase());
ps.setBytes(8, inPrev.getWindowBitmap()); ps.setBytes(7, inPrev.getWindowBitmap());
ps.setInt(9, OFFSET_PREV); ps.setInt(8, OFFSET_PREV);
ps.addBatch(); ps.addBatch();
// Current rotation period // Current time period
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
ps.setLong(4, inCurr.getRotationPeriod()); ps.setLong(3, inCurr.getTimePeriod());
ps.setBytes(5, inCurr.getTagKey().getBytes()); ps.setBytes(4, inCurr.getTagKey().getBytes());
ps.setBytes(6, inCurr.getHeaderKey().getBytes()); ps.setBytes(5, inCurr.getHeaderKey().getBytes());
ps.setLong(7, inCurr.getWindowBase()); ps.setLong(6, inCurr.getWindowBase());
ps.setBytes(8, inCurr.getWindowBitmap()); ps.setBytes(7, inCurr.getWindowBitmap());
ps.setInt(9, OFFSET_CURR); ps.setInt(8, OFFSET_CURR);
ps.addBatch(); ps.addBatch();
// Next rotation period // Next time period
IncomingKeys inNext = k.getNextIncomingKeys(); IncomingKeys inNext = k.getNextIncomingKeys();
ps.setLong(4, inNext.getRotationPeriod()); ps.setLong(3, inNext.getTimePeriod());
ps.setBytes(5, inNext.getTagKey().getBytes()); ps.setBytes(4, inNext.getTagKey().getBytes());
ps.setBytes(6, inNext.getHeaderKey().getBytes()); ps.setBytes(5, inNext.getHeaderKey().getBytes());
ps.setLong(7, inNext.getWindowBase()); ps.setLong(6, inNext.getWindowBase());
ps.setBytes(8, inNext.getWindowBitmap()); ps.setBytes(7, inNext.getWindowBitmap());
ps.setInt(9, OFFSET_NEXT); ps.setInt(8, OFFSET_NEXT);
ps.addBatch(); ps.addBatch();
int[] batchAffected = ps.executeBatch(); int[] batchAffected = ps.executeBatch();
if (batchAffected.length != 3) throw new DbStateException(); if (batchAffected.length != 3) throw new DbStateException();
@@ -1107,6 +1286,29 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public boolean containsPendingContact(Connection txn, PendingContactId p)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM pendingContacts"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getBytes());
rs = ps.executeQuery();
boolean found = rs.next();
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public boolean containsTransport(Connection txn, TransportId t) public boolean containsTransport(Connection txn, TransportId t)
throws DbException { throws DbException {
@@ -1256,14 +1458,14 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public Collection<Contact> getContacts(Connection txn) public Collection<Contact> getContacts(Connection txn)
throws DbException { throws DbException {
PreparedStatement ps = null; Statement s = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, authorId, formatVersion, name," String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " alias, publicKey, localAuthorId, verified, active" + " alias, publicKey, localAuthorId, verified, active"
+ " FROM contacts"; + " FROM contacts";
ps = txn.prepareStatement(sql); s = txn.createStatement();
rs = ps.executeQuery(); rs = s.executeQuery(sql);
List<Contact> contacts = new ArrayList<>(); List<Contact> contacts = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
ContactId contactId = new ContactId(rs.getInt(1)); ContactId contactId = new ContactId(rs.getInt(1));
@@ -1281,11 +1483,11 @@ abstract class JdbcDatabase implements Database<Connection> {
alias, verified, active)); alias, verified, active));
} }
rs.close(); rs.close();
ps.close(); s.close();
return contacts; return contacts;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs, LOG, WARNING); tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING); tryToClose(s, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1485,6 +1687,86 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Collection<HandshakeKeySet> 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<IncomingKeys> 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<HandshakeKeySet> 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 @Override
public Collection<LocalAuthor> getLocalAuthors(Connection txn) public Collection<LocalAuthor> getLocalAuthors(Connection txn)
throws DbException { throws DbException {
@@ -2086,6 +2368,38 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Collection<PendingContact> getPendingContacts(Connection txn)
throws DbException {
Statement s = null;
ResultSet rs = null;
try {
String sql = "SELECT pendingContactId, publicKey, alias, state,"
+ " timestamp"
+ " FROM pendingContacts";
s = txn.createStatement();
rs = s.executeQuery(sql);
List<PendingContact> pendingContacts = new ArrayList<>();
while (rs.next()) {
PendingContactId id = new PendingContactId(rs.getBytes(1));
byte[] publicKey = rs.getBytes(2);
String alias = rs.getString(3);
PendingContactState state =
PendingContactState.fromValue(rs.getInt(4));
long timestamp = rs.getLong(5);
pendingContacts.add(new PendingContact(id, publicKey, alias,
state, timestamp));
}
rs.close();
s.close();
return pendingContacts;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Collection<MessageId> getRequestedMessagesToSend(Connection txn, public Collection<MessageId> getRequestedMessagesToSend(Connection txn,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, int maxLatency) throws DbException {
@@ -2149,14 +2463,13 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public Collection<KeySet> getTransportKeys(Connection txn, TransportId t) public Collection<TransportKeySet> getTransportKeys(Connection txn,
throws DbException { TransportId t) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Retrieve the incoming keys // Retrieve the incoming keys
String sql = "SELECT rotationPeriod, tagKey, headerKey," String sql = "SELECT timePeriod, tagKey, headerKey, base, bitmap"
+ " base, bitmap"
+ " FROM incomingKeys" + " FROM incomingKeys"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
+ " ORDER BY keySetId, periodOffset"; + " ORDER BY keySetId, periodOffset";
@@ -2165,18 +2478,18 @@ abstract class JdbcDatabase implements Database<Connection> {
rs = ps.executeQuery(); rs = ps.executeQuery();
List<IncomingKeys> inKeys = new ArrayList<>(); List<IncomingKeys> inKeys = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
long rotationPeriod = rs.getLong(1); long timePeriod = rs.getLong(1);
SecretKey tagKey = new SecretKey(rs.getBytes(2)); SecretKey tagKey = new SecretKey(rs.getBytes(2));
SecretKey headerKey = new SecretKey(rs.getBytes(3)); SecretKey headerKey = new SecretKey(rs.getBytes(3));
long windowBase = rs.getLong(4); long windowBase = rs.getLong(4);
byte[] windowBitmap = rs.getBytes(5); byte[] windowBitmap = rs.getBytes(5);
inKeys.add(new IncomingKeys(tagKey, headerKey, rotationPeriod, inKeys.add(new IncomingKeys(tagKey, headerKey, timePeriod,
windowBase, windowBitmap)); windowBase, windowBitmap));
} }
rs.close(); rs.close();
ps.close(); ps.close();
// Retrieve the outgoing keys in the same order // Retrieve the outgoing keys in the same order
sql = "SELECT keySetId, contactId, rotationPeriod," sql = "SELECT keySetId, contactId, timePeriod,"
+ " tagKey, headerKey, stream, active" + " tagKey, headerKey, stream, active"
+ " FROM outgoingKeys" + " FROM outgoingKeys"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
@@ -2184,25 +2497,27 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, t.getString()); ps.setString(1, t.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
Collection<KeySet> keys = new ArrayList<>(); Collection<TransportKeySet> keys = new ArrayList<>();
for (int i = 0; rs.next(); i++) { for (int i = 0; rs.next(); i++) {
// There should be three times as many incoming keys // There should be three times as many incoming keys
if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1)); TransportKeySetId keySetId =
new TransportKeySetId(rs.getInt(1));
ContactId contactId = new ContactId(rs.getInt(2)); ContactId contactId = new ContactId(rs.getInt(2));
long rotationPeriod = rs.getLong(3); long timePeriod = rs.getLong(3);
SecretKey tagKey = new SecretKey(rs.getBytes(4)); SecretKey tagKey = new SecretKey(rs.getBytes(4));
SecretKey headerKey = new SecretKey(rs.getBytes(5)); SecretKey headerKey = new SecretKey(rs.getBytes(5));
long streamCounter = rs.getLong(6); long streamCounter = rs.getLong(6);
boolean active = rs.getBoolean(7); boolean active = rs.getBoolean(7);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
rotationPeriod, streamCounter, active); timePeriod, streamCounter, active);
IncomingKeys inPrev = inKeys.get(i * 3); IncomingKeys inPrev = inKeys.get(i * 3);
IncomingKeys inCurr = inKeys.get(i * 3 + 1); IncomingKeys inCurr = inKeys.get(i * 3 + 1);
IncomingKeys inNext = inKeys.get(i * 3 + 2); IncomingKeys inNext = inKeys.get(i * 3 + 2);
TransportKeys transportKeys = new TransportKeys(t, inPrev, TransportKeys transportKeys = new TransportKeys(t, inPrev,
inCurr, inNext, outCurr); inCurr, inNext, outCurr);
keys.add(new KeySet(keySetId, contactId, transportKeys)); keys.add(new TransportKeySet(keySetId, contactId,
transportKeys));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -2216,7 +2531,26 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void incrementStreamCounter(Connection txn, TransportId t, public void incrementStreamCounter(Connection txn, TransportId t,
KeySetId k) throws DbException { 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 {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE outgoingKeys SET stream = stream + 1" String sql = "UPDATE outgoingKeys SET stream = stream + 1"
@@ -2594,6 +2928,27 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@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 @Override
public void removeLocalAuthor(Connection txn, AuthorId a) public void removeLocalAuthor(Connection txn, AuthorId a)
throws DbException { throws DbException {
@@ -2671,6 +3026,24 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void removePendingContact(Connection txn, PendingContactId p)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM pendingContacts"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getBytes());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void removeTransport(Connection txn, TransportId t) public void removeTransport(Connection txn, TransportId t)
throws DbException { throws DbException {
@@ -2689,8 +3062,8 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void removeTransportKeys(Connection txn, TransportId t, KeySetId k) public void removeTransportKeys(Connection txn, TransportId t,
throws DbException { TransportKeySetId k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Delete any existing outgoing keys - this will also remove any // Delete any existing outgoing keys - this will also remove any
@@ -2893,19 +3266,63 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void setReorderingWindow(Connection txn, KeySetId k, TransportId t, public void setPendingContactState(Connection txn, PendingContactId p,
long rotationPeriod, long base, byte[] bitmap) throws DbException { PendingContactState state) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE pendingContacts SET state = ?"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, state.getValue());
ps.setBytes(2, p.getBytes());
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 setReorderingWindow(Connection txn, TransportKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?" String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
+ " WHERE transportId = ? AND keySetId = ?" + " WHERE transportId = ? AND keySetId = ?"
+ " AND rotationPeriod = ?"; + " AND timePeriod = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setLong(1, base); ps.setLong(1, base);
ps.setBytes(2, bitmap); ps.setBytes(2, bitmap);
ps.setString(3, t.getString()); ps.setString(3, t.getString());
ps.setInt(4, k.getInt()); ps.setInt(4, k.getInt());
ps.setLong(5, rotationPeriod); 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 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(); int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
@@ -2917,7 +3334,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void setTransportKeysActive(Connection txn, TransportId t, public void setTransportKeysActive(Connection txn, TransportId t,
KeySetId k) throws DbException { TransportKeySetId k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE outgoingKeys SET active = true" String sql = "UPDATE outgoingKeys SET active = true"
@@ -2972,18 +3389,18 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void updateTransportKeys(Connection txn, KeySet ks) public void updateTransportKeys(Connection txn, TransportKeySet ks)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Update the outgoing keys // Update the outgoing keys
String sql = "UPDATE outgoingKeys SET rotationPeriod = ?," String sql = "UPDATE outgoingKeys SET timePeriod = ?,"
+ " tagKey = ?, headerKey = ?, stream = ?" + " tagKey = ?, headerKey = ?, stream = ?"
+ " WHERE transportId = ? AND keySetId = ?"; + " WHERE transportId = ? AND keySetId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(1, outCurr.getRotationPeriod()); ps.setLong(1, outCurr.getTimePeriod());
ps.setBytes(2, outCurr.getTagKey().getBytes()); ps.setBytes(2, outCurr.getTagKey().getBytes());
ps.setBytes(3, outCurr.getHeaderKey().getBytes()); ps.setBytes(3, outCurr.getHeaderKey().getBytes());
ps.setLong(4, outCurr.getStreamCounter()); ps.setLong(4, outCurr.getStreamCounter());
@@ -2993,34 +3410,101 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
// Update the incoming keys // Update the incoming keys
sql = "UPDATE incomingKeys SET rotationPeriod = ?," sql = "UPDATE incomingKeys SET timePeriod = ?,"
+ " tagKey = ?, headerKey = ?, base = ?, bitmap = ?" + " tagKey = ?, headerKey = ?, base = ?, bitmap = ?"
+ " WHERE transportId = ? AND keySetId = ?" + " WHERE transportId = ? AND keySetId = ?"
+ " AND periodOffset = ?"; + " AND periodOffset = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(6, k.getTransportId().getString()); ps.setString(6, k.getTransportId().getString());
ps.setInt(7, ks.getKeySetId().getInt()); ps.setInt(7, ks.getKeySetId().getInt());
// Previous rotation period // Previous time period
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(1, inPrev.getRotationPeriod()); ps.setLong(1, inPrev.getTimePeriod());
ps.setBytes(2, inPrev.getTagKey().getBytes()); ps.setBytes(2, inPrev.getTagKey().getBytes());
ps.setBytes(3, inPrev.getHeaderKey().getBytes()); ps.setBytes(3, inPrev.getHeaderKey().getBytes());
ps.setLong(4, inPrev.getWindowBase()); ps.setLong(4, inPrev.getWindowBase());
ps.setBytes(5, inPrev.getWindowBitmap()); ps.setBytes(5, inPrev.getWindowBitmap());
ps.setInt(8, OFFSET_PREV); ps.setInt(8, OFFSET_PREV);
ps.addBatch(); ps.addBatch();
// Current rotation period // Current time period
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
ps.setLong(1, inCurr.getRotationPeriod()); ps.setLong(1, inCurr.getTimePeriod());
ps.setBytes(2, inCurr.getTagKey().getBytes()); ps.setBytes(2, inCurr.getTagKey().getBytes());
ps.setBytes(3, inCurr.getHeaderKey().getBytes()); ps.setBytes(3, inCurr.getHeaderKey().getBytes());
ps.setLong(4, inCurr.getWindowBase()); ps.setLong(4, inCurr.getWindowBase());
ps.setBytes(5, inCurr.getWindowBitmap()); ps.setBytes(5, inCurr.getWindowBitmap());
ps.setInt(8, OFFSET_CURR); ps.setInt(8, OFFSET_CURR);
ps.addBatch(); ps.addBatch();
// Next rotation period // Next time period
IncomingKeys inNext = k.getNextIncomingKeys(); IncomingKeys inNext = k.getNextIncomingKeys();
ps.setLong(1, inNext.getRotationPeriod()); 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);
}
}
@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(2, inNext.getTagKey().getBytes());
ps.setBytes(3, inNext.getHeaderKey().getBytes()); ps.setBytes(3, inNext.getHeaderKey().getBytes());
ps.setLong(4, inNext.getWindowBase()); ps.setLong(4, inNext.getWindowBase());

View File

@@ -17,7 +17,7 @@ class Migration40_41 implements Migration<Connection> {
private final DatabaseTypes dbTypes; private final DatabaseTypes dbTypes;
public Migration40_41(DatabaseTypes databaseTypes) { Migration40_41(DatabaseTypes databaseTypes) {
this.dbTypes = databaseTypes; this.dbTypes = databaseTypes;
} }

View File

@@ -0,0 +1,97 @@
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 Migration41_42 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration41_42.class.getName());
private final DatabaseTypes dbTypes;
Migration41_42(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 41;
}
@Override
public int getEndVersion() {
return 42;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " DROP COLUMN contactId");
s.execute(dbTypes.replaceTypes("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))"));
s.execute(dbTypes.replaceTypes("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)"));
s.execute(dbTypes.replaceTypes("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)"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -14,7 +14,7 @@ import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
@@ -90,7 +90,7 @@ class KeyAgreementProtocol {
/** /**
* Perform the BQP protocol. * Perform the BQP protocol.
* *
* @return the negotiated master secret. * @return the negotiated master key.
* @throws AbortException when the protocol may have been tampered with. * @throws AbortException when the protocol may have been tampered with.
* @throws IOException for all other other connection errors. * @throws IOException for all other other connection errors.
*/ */
@@ -115,7 +115,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey); receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey); sendConfirm(s, theirPublicKey);
} }
return crypto.deriveKey(MASTER_SECRET_LABEL, s); return crypto.deriveKey(MASTER_KEY_LABEL, s);
} catch (AbortException e) { } catch (AbortException e) {
sendAbort(e.getCause() != null); sendAbort(e.getCause() != null);
throw e; throw e;

View File

@@ -114,9 +114,9 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
keyAgreementCrypto, payloadEncoder, transport, remotePayload, keyAgreementCrypto, payloadEncoder, transport, remotePayload,
localPayload, localKeyPair, alice); localPayload, localKeyPair, alice);
try { try {
SecretKey master = protocol.perform(); SecretKey masterKey = protocol.perform();
KeyAgreementResult result = KeyAgreementResult result =
new KeyAgreementResult(master, transport.getConnection(), new KeyAgreementResult(masterKey, transport.getConnection(),
transport.getTransportId(), alice); transport.getTransportId(), alice);
LOG.info("Finished BQP protocol"); LOG.info("Finished BQP protocol");
// Broadcast result to caller // Broadcast result to caller

View File

@@ -19,8 +19,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeyManager; 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.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -95,22 +95,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c, public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
SecretKey master, long timestamp, boolean alice, boolean active) ContactId c, SecretKey rootKey, long timestamp, boolean alice,
throws DbException { boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, TransportKeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addContact(txn, c, master, timestamp, alice, active)); ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
} }
return ids; return ids;
} }
@Override @Override
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) public void activateKeys(Transaction txn, Map<TransportId,
throws DbException { TransportKeySetId> keys) throws DbException {
for (Entry<TransportId, KeySetId> e : keys.entrySet()) { for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) { if (m == null) {

View File

@@ -11,18 +11,18 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableIncomingKeys { class MutableIncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod; private final long timePeriod;
private final ReorderingWindow window; private final ReorderingWindow window;
MutableIncomingKeys(IncomingKeys in) { MutableIncomingKeys(IncomingKeys in) {
tagKey = in.getTagKey(); tagKey = in.getTagKey();
headerKey = in.getHeaderKey(); headerKey = in.getHeaderKey();
rotationPeriod = in.getRotationPeriod(); timePeriod = in.getTimePeriod();
window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap()); window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap());
} }
IncomingKeys snapshot() { IncomingKeys snapshot() {
return new IncomingKeys(tagKey, headerKey, rotationPeriod, return new IncomingKeys(tagKey, headerKey, timePeriod,
window.getBase(), window.getBitmap()); window.getBase(), window.getBitmap());
} }
@@ -34,8 +34,8 @@ class MutableIncomingKeys {
return headerKey; return headerKey;
} }
long getRotationPeriod() { long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
ReorderingWindow getWindow() { ReorderingWindow getWindow() {

View File

@@ -1,22 +1,22 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
class MutableKeySet { class MutableKeySet {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableTransportKeys transportKeys; private final MutableTransportKeys transportKeys;
MutableKeySet(KeySetId keySetId, ContactId contactId, MutableKeySet(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys transportKeys) { MutableTransportKeys transportKeys) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.transportKeys = transportKeys; this.transportKeys = transportKeys;
} }
KeySetId getKeySetId() { TransportKeySetId getKeySetId() {
return keySetId; return keySetId;
} }

View File

@@ -11,20 +11,20 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableOutgoingKeys { class MutableOutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod; private final long timePeriod;
private long streamCounter; private long streamCounter;
private boolean active; private boolean active;
MutableOutgoingKeys(OutgoingKeys out) { MutableOutgoingKeys(OutgoingKeys out) {
tagKey = out.getTagKey(); tagKey = out.getTagKey();
headerKey = out.getHeaderKey(); headerKey = out.getHeaderKey();
rotationPeriod = out.getRotationPeriod(); timePeriod = out.getTimePeriod();
streamCounter = out.getStreamCounter(); streamCounter = out.getStreamCounter();
active = out.isActive(); active = out.isActive();
} }
OutgoingKeys snapshot() { OutgoingKeys snapshot() {
return new OutgoingKeys(tagKey, headerKey, rotationPeriod, return new OutgoingKeys(tagKey, headerKey, timePeriod,
streamCounter, active); streamCounter, active);
} }
@@ -36,8 +36,8 @@ class MutableOutgoingKeys {
return headerKey; return headerKey;
} }
long getRotationPeriod() { long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
long getStreamCounter() { long getStreamCounter() {

View File

@@ -5,8 +5,8 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,10 +15,11 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
KeySetId addContact(Transaction txn, ContactId c, SecretKey master, TransportKeySetId addContact(Transaction txn, ContactId c,
long timestamp, boolean alice, boolean active) throws DbException; SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException; void activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);

View File

@@ -11,9 +11,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; 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.api.transport.TransportKeys;
import org.briarproject.bramble.transport.ReorderingWindow.Change; import org.briarproject.bramble.transport.ReorderingWindow.Change;
@@ -51,12 +51,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
private final TransportId transportId; private final TransportId transportId;
private final long rotationPeriodLength; private final long timePeriodLength;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
// The following are locking: lock // The following are locking: lock
private final Map<KeySetId, MutableKeySet> keys = new HashMap<>(); private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
private final Map<Bytes, TagContext> inContexts = new HashMap<>(); private final Map<Bytes, TagContext> inContexts = new HashMap<>();
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>(); private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
@@ -70,7 +70,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
this.scheduler = scheduler; this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
this.transportId = transportId; this.transportId = transportId;
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
} }
@Override @Override
@@ -80,8 +80,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
lock.lock(); lock.lock();
try { try {
// Load the transport keys from the DB // Load the transport keys from the DB
Collection<KeySet> loaded = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> loaded =
// Rotate the keys to the current rotation period db.getTransportKeys(txn, transportId);
// Rotate the keys to the current time period
RotationResult rotationResult = rotateKeys(loaded, now); RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts // Initialise mutable state for all contacts
addKeys(rotationResult.current); addKeys(rotationResult.current);
@@ -95,15 +96,17 @@ class TransportKeyManagerImpl implements TransportKeyManager {
scheduleKeyRotation(now); scheduleKeyRotation(now);
} }
private RotationResult rotateKeys(Collection<KeySet> keys, long now) { private RotationResult rotateKeys(Collection<TransportKeySet> keys,
long now) {
RotationResult rotationResult = new RotationResult(); RotationResult rotationResult = new RotationResult();
long rotationPeriod = now / rotationPeriodLength; long timePeriod = now / timePeriodLength;
for (KeySet ks : keys) { for (TransportKeySet ks : keys) {
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
TransportKeys k1 = TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
transportCrypto.rotateTransportKeys(k, rotationPeriod); timePeriod);
KeySet ks1 = new KeySet(ks.getKeySetId(), ks.getContactId(), k1); TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
if (k1.getRotationPeriod() > k.getRotationPeriod()) ks.getContactId(), k1);
if (k1.getTimePeriod() > k.getTimePeriod())
rotationResult.rotated.add(ks1); rotationResult.rotated.add(ks1);
rotationResult.current.add(ks1); rotationResult.current.add(ks1);
} }
@@ -111,15 +114,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void addKeys(Collection<KeySet> keys) { private void addKeys(Collection<TransportKeySet> keys) {
for (KeySet ks : keys) { for (TransportKeySet ks : keys) {
addKeys(ks.getKeySetId(), ks.getContactId(), addKeys(ks.getKeySetId(), ks.getContactId(),
new MutableTransportKeys(ks.getTransportKeys())); new MutableTransportKeys(ks.getKeys()));
} }
} }
// Locking: lock // Locking: lock
private void addKeys(KeySetId keySetId, ContactId contactId, private void addKeys(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys m) { MutableTransportKeys m) {
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
keys.put(keySetId, ks); keys.put(keySetId, ks);
@@ -130,7 +133,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void encodeTags(KeySetId keySetId, ContactId contactId, private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys) { MutableIncomingKeys inKeys) {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = TagContext tagCtx =
@@ -155,7 +158,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
long delay = rotationPeriodLength - now % rotationPeriodLength; long delay = timePeriodLength - now % timePeriodLength;
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS); scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
} }
@@ -170,20 +173,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContact(Transaction txn, ContactId c, SecretKey master, public TransportKeySetId addContact(Transaction txn, ContactId c,
long timestamp, boolean alice, boolean active) throws DbException { SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Work out what rotation period the timestamp belongs to // Work out what time period the timestamp belongs to
long rotationPeriod = timestamp / rotationPeriodLength; long timePeriod = timestamp / timePeriodLength;
// Derive the transport keys // Derive the transport keys
TransportKeys k = transportCrypto.deriveTransportKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, rotationPeriod, alice, active); rootKey, timePeriod, alice, active);
// Rotate the keys to the current rotation period if necessary // Rotate the keys to the current time period if necessary
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength; timePeriod = clock.currentTimeMillis() / timePeriodLength;
k = transportCrypto.rotateTransportKeys(k, rotationPeriod); k = transportCrypto.rotateTransportKeys(k, timePeriod);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k); TransportKeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the contact
addKeys(keySetId, c, new MutableTransportKeys(k)); addKeys(keySetId, c, new MutableTransportKeys(k));
return keySetId; return keySetId;
@@ -193,7 +197,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException { public void activateKeys(Transaction txn, TransportKeySetId k)
throws DbException {
lock.lock(); lock.lock();
try { try {
MutableKeySet ks = keys.get(k); MutableKeySet ks = keys.get(k);
@@ -300,7 +305,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Write the window back to the DB // Write the window back to the DB
db.setReorderingWindow(txn, tagCtx.keySetId, transportId, db.setReorderingWindow(txn, tagCtx.keySetId, transportId,
inKeys.getRotationPeriod(), window.getBase(), inKeys.getTimePeriod(), window.getBase(),
window.getBitmap()); window.getBitmap());
// If the outgoing keys are inactive, activate them // If the outgoing keys are inactive, activate them
MutableKeySet ks = keys.get(tagCtx.keySetId); MutableKeySet ks = keys.get(tagCtx.keySetId);
@@ -322,11 +327,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Rotate the keys to the current rotation period // Rotate the keys to the current time period
Collection<KeySet> snapshot = new ArrayList<>(keys.size()); Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
for (MutableKeySet ks : keys.values()) { for (MutableKeySet ks : keys.values()) {
snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(), snapshot.add(new TransportKeySet(ks.getKeySetId(),
ks.getTransportKeys().snapshot())); ks.getContactId(), ks.getTransportKeys().snapshot()));
} }
RotationResult rotationResult = rotateKeys(snapshot, now); RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts // Rebuild the mutable state for all contacts
@@ -346,12 +351,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class TagContext { private static class TagContext {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableIncomingKeys inKeys; private final MutableIncomingKeys inKeys;
private final long streamNumber; private final long streamNumber;
private TagContext(KeySetId keySetId, ContactId contactId, private TagContext(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys, long streamNumber) { MutableIncomingKeys inKeys, long streamNumber) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
@@ -362,7 +367,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class RotationResult { private static class RotationResult {
private final Collection<KeySet> current = new ArrayList<>(); private final Collection<TransportKeySet> current = new ArrayList<>();
private final Collection<KeySet> rotated = new ArrayList<>(); private final Collection<TransportKeySet> rotated = new ArrayList<>();
} }
} }

View File

@@ -64,7 +64,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey master = getSecretKey(); SecretKey rootKey = getSecretKey();
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean(); boolean alice = new Random().nextBoolean();
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
@@ -73,14 +73,14 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified, active); oneOf(db).addContact(txn, remote, local, verified, active);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContact(txn, contactId, master, timestamp, oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
alice, active); alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
}}); }});
assertEquals(contactId, contactManager.addContact(remote, local, assertEquals(contactId, contactManager.addContact(remote, local,
master, timestamp, alice, verified, active)); rootKey, timestamp, alice, verified, active));
} }
@Test @Test

View File

@@ -0,0 +1,167 @@
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);
}
}

View File

@@ -1,167 +0,0 @@
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.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.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.assertTrue;
public class KeyDerivationTest 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 master = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
assertAllDifferent(k);
}
@Test
public void testCurrentKeysMatchCurrentKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
// Rotate into the future
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testPreviousKeysMatchPreviousKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 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);
// Alice's previous incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's outgoing keys should equal Bob's previous incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getPreviousIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getPreviousIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testNextKeysMatchNextKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 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);
// Alice's outgoing keys should equal Bob's next incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getNextIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getNextIncomingKeys().getHeaderKey().getBytes());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getNextIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
}
@Test
public void testMasterKeyAffectsOutput() {
SecretKey master1 = getSecretKey();
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
master1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertFalse(transportId.getString().equals(transportId1.getString()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
master, 123, true, true);
assertAllDifferent(k, k1);
}
private void assertAllDifferent(TransportKeys... transportKeys) {
List<SecretKey> 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<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
}

View File

@@ -0,0 +1,45 @@
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<SecretKey> 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<SecretKey> keys) {
Set<Bytes> 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());
}
}

View File

@@ -0,0 +1,167 @@
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.TransportKeys;
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 TransportKeyDerivationTest 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() {
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(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);
// 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
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(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);
// 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);
// 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
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(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);
// 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);
// 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()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
rootKey1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
rootKey, 123, true, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
@@ -13,6 +14,7 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -44,10 +46,11 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; 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.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; 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.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
@@ -111,7 +114,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final int maxLatency; private final int maxLatency;
private final ContactId contactId; private final ContactId contactId;
private final Contact contact; private final Contact contact;
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final PendingContactId pendingContactId;
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = getClientId(); clientId = getClientId();
@@ -132,7 +136,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
contactId = new ContactId(234); contactId = new ContactId(234);
contact = new Contact(contactId, author, localAuthor.getId(), alias, contact = new Contact(contactId, author, localAuthor.getId(), alias,
true, true); true, true);
keySetId = new KeySetId(345); keySetId = new TransportKeySetId(345);
pendingContactId = new PendingContactId(getRandomId());
} }
private DatabaseComponent createDatabaseComponent(Database<Object> database, private DatabaseComponent createDatabaseComponent(Database<Object> database,
@@ -279,15 +284,24 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(17).of(database).startTransaction(); exactly(18).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(18).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(17).of(database).abortTransaction(txn); exactly(18).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addTransportKeys(transaction, contactId, db.addTransportKeys(transaction, contactId,
@@ -718,6 +732,39 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
} }
@Test
public void testVariousMethodsThrowExceptionIfPendingContactIsMissing()
throws Exception {
context.checking(new Expectations() {{
// Check whether the pending contact is in the DB (which it's not)
exactly(2).of(database).startTransaction();
will(returnValue(txn));
exactly(2).of(database).containsPendingContact(txn,
pendingContactId);
will(returnValue(false));
exactly(2).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, pendingContactId,
createHandshakeKeys()));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.removePendingContact(transaction, pendingContactId));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
}
@Test @Test
public void testGenerateAck() throws Exception { public void testGenerateAck() throws Exception {
Collection<MessageId> messagesToAck = asList(messageId, messageId1); Collection<MessageId> messagesToAck = asList(messageId, messageId1);
@@ -1117,8 +1164,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
TransportKeys transportKeys = createTransportKeys(); TransportKeys transportKeys = createTransportKeys();
KeySet ks = new KeySet(keySetId, contactId, transportKeys); TransportKeySet ks =
Collection<KeySet> keys = singletonList(ks); new TransportKeySet(keySetId, contactId, transportKeys);
Collection<TransportKeySet> keys = singletonList(ks);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// startTransaction() // startTransaction()
@@ -1245,6 +1293,27 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
} }
private HandshakeKeys createHandshakeKeys() {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
2, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
3, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456, true);
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
getSecretKey(), true);
}
private TransportKeys createTransportKeys() { private TransportKeys createTransportKeys() {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -20,10 +21,13 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock; 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.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; 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.api.transport.TransportKeys;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
@@ -52,6 +56,7 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
@@ -70,6 +75,7 @@ import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -90,7 +96,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// All our transports use a maximum latency of 30 seconds // All our transports use a maximum latency of 30 seconds
private static final int MAX_LATENCY = 30 * 1000; private static final int MAX_LATENCY = 30 * 1000;
private final SecretKey key = getSecretKey(); private final SecretKey key = getSecretKey();
private final File testDir = getTestDirectory(); private final File testDir = getTestDirectory();
private final GroupId groupId; private final GroupId groupId;
@@ -103,7 +108,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final MessageId messageId; private final MessageId messageId;
private final TransportId transportId; private final TransportId transportId;
private final ContactId contactId; private final ContactId contactId;
private final KeySetId keySetId, keySetId1; private final TransportKeySetId keySetId, keySetId1;
private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1;
private final PendingContact pendingContact;
private final Random random = new Random(); private final Random random = new Random();
JdbcDatabaseTest() { JdbcDatabaseTest() {
@@ -117,8 +124,11 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
messageId = message.getId(); messageId = message.getId();
transportId = getTransportId(); transportId = getTransportId();
contactId = new ContactId(1); contactId = new ContactId(1);
keySetId = new KeySetId(1); keySetId = new TransportKeySetId(1);
keySetId1 = new KeySetId(2); keySetId1 = new TransportKeySetId(2);
handshakeKeySetId = new HandshakeKeySetId(1);
handshakeKeySetId1 = new HandshakeKeySetId(2);
pendingContact = getPendingContact();
} }
protected abstract JdbcDatabase createDatabase(DatabaseConfig config, protected abstract JdbcDatabase createDatabase(DatabaseConfig config,
@@ -653,10 +663,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
long rotationPeriod = 123, rotationPeriod1 = 234; long timePeriod = 123, timePeriod1 = 234;
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
TransportKeys keys = createTransportKeys(rotationPeriod, active); TransportKeys keys = createTransportKeys(timePeriod, active);
TransportKeys keys1 = createTransportKeys(rotationPeriod1, active); TransportKeys keys1 = createTransportKeys(timePeriod1, active);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -673,35 +683,38 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
// Retrieve the transport keys // Retrieve the transport keys
Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(keys, ks.getTransportKeys()); assertKeysEquals(keys, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getTransportKeys()); assertKeysEquals(keys1, ks.getKeys());
} }
} }
// Rotate the transport keys // Rotate the transport keys
TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active); TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
TransportKeys rotated1 = TransportKeys rotated1 =
createTransportKeys(rotationPeriod1 + 1, active); createTransportKeys(timePeriod1 + 1, active);
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated)); db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1)); rotated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
rotated1));
// Retrieve the transport keys again // Retrieve the transport keys again
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(rotated, ks.getTransportKeys()); assertKeysEquals(rotated, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(rotated1, ks.getTransportKeys()); assertKeysEquals(rotated1, ks.getKeys());
} }
} }
@@ -716,7 +729,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private void assertKeysEquals(TransportKeys expected, private void assertKeysEquals(TransportKeys expected,
TransportKeys actual) { TransportKeys actual) {
assertEquals(expected.getTransportId(), actual.getTransportId()); assertEquals(expected.getTransportId(), actual.getTransportId());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertKeysEquals(expected.getPreviousIncomingKeys(), assertKeysEquals(expected.getPreviousIncomingKeys(),
actual.getPreviousIncomingKeys()); actual.getPreviousIncomingKeys());
assertKeysEquals(expected.getCurrentIncomingKeys(), assertKeysEquals(expected.getCurrentIncomingKeys(),
@@ -732,7 +745,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getTagKey().getBytes()); actual.getTagKey().getBytes());
assertArrayEquals(expected.getHeaderKey().getBytes(), assertArrayEquals(expected.getHeaderKey().getBytes(),
actual.getHeaderKey().getBytes()); actual.getHeaderKey().getBytes());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertEquals(expected.getWindowBase(), actual.getWindowBase()); assertEquals(expected.getWindowBase(), actual.getWindowBase());
assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap()); assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap());
} }
@@ -742,15 +755,174 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getTagKey().getBytes()); actual.getTagKey().getBytes());
assertArrayEquals(expected.getHeaderKey().getBytes(), assertArrayEquals(expected.getHeaderKey().getBytes(),
actual.getHeaderKey().getBytes()); actual.getHeaderKey().getBytes());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertEquals(expected.getStreamCounter(), actual.getStreamCounter()); assertEquals(expected.getStreamCounter(), actual.getStreamCounter());
assertEquals(expected.isActive(), actual.isActive()); assertEquals(expected.isActive(), actual.isActive());
} }
@Test
public void testHandshakeKeys() 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);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the contact, the transport and the handshake keys
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
assertEquals(handshakeKeySetId1,
db.addHandshakeKeys(txn, contactId, keys1));
// Retrieve the handshake keys
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
contactId, updated));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
contactId, updated1));
// Retrieve the handshake keys again
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
}
}
// Removing the contact should remove the handshake keys
db.removeContact(txn, contactId);
assertEquals(emptyList(), db.getHandshakeKeys(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);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getHandshakeKeys(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));
// Retrieve the handshake keys
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
pendingContact.getId(), updated));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
pendingContact.getId(), updated1));
// Retrieve the handshake keys again
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, 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));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testIncrementStreamCounter() throws Exception { public void testIncrementStreamCounter() throws Exception {
long rotationPeriod = 123; long timePeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod, true); TransportKeys keys = createTransportKeys(timePeriod, true);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter(); long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -766,15 +938,63 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Increment the stream counter twice and retrieve the transport keys // Increment the stream counter twice and retrieve the transport keys
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, keySetId);
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, keySetId);
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
KeySet ks = newKeys.iterator().next(); TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
assertEquals(rotationPeriod, outCurr.getRotationPeriod()); assertEquals(timePeriod, outCurr.getTimePeriod());
assertEquals(streamCounter + 2, outCurr.getStreamCounter());
// The rest of the keys should be unaffected
assertKeysEquals(keys.getPreviousIncomingKeys(),
k.getPreviousIncomingKeys());
assertKeysEquals(keys.getCurrentIncomingKeys(),
k.getCurrentIncomingKeys());
assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
db.commitTransaction(txn);
db.close();
}
@Test
public void testIncrementStreamCounterForHandshakeKeys() throws Exception {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the contact, transport and handshake keys
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Increment the stream counter twice and retrieve the handshake keys
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
assertEquals(timePeriod, outCurr.getTimePeriod());
assertEquals(streamCounter + 2, outCurr.getStreamCounter()); assertEquals(streamCounter + 2, outCurr.getStreamCounter());
// The rest of the keys should be unaffected // The rest of the keys should be unaffected
@@ -791,8 +1011,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testSetReorderingWindow() throws Exception { public void testSetReorderingWindow() throws Exception {
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
long rotationPeriod = 123; long timePeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod, active); TransportKeys keys = createTransportKeys(timePeriod, active);
long base = keys.getCurrentIncomingKeys().getWindowBase(); long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap(); byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
@@ -808,17 +1028,68 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Update the reordering window and retrieve the transport keys // Update the reordering window and retrieve the transport keys
random.nextBytes(bitmap); random.nextBytes(bitmap);
db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod, db.setReorderingWindow(txn, keySetId, transportId, timePeriod,
base + 1, bitmap); base + 1, bitmap);
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
KeySet ks = newKeys.iterator().next(); TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
assertEquals(rotationPeriod, inCurr.getRotationPeriod()); assertEquals(timePeriod, inCurr.getTimePeriod());
assertEquals(base + 1, inCurr.getWindowBase());
assertArrayEquals(bitmap, inCurr.getWindowBitmap());
// The rest of the keys should be unaffected
assertKeysEquals(keys.getPreviousIncomingKeys(),
k.getPreviousIncomingKeys());
assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
assertKeysEquals(keys.getCurrentOutgoingKeys(),
k.getCurrentOutgoingKeys());
db.commitTransaction(txn);
db.close();
}
@Test
public void testSetReorderingWindowForHandshakeKeys() throws Exception {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the contact, transport and handshake keys
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Update the reordering window and retrieve the handshake keys
random.nextBytes(bitmap);
db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
base + 1, bitmap);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
IncomingKeys inCurr = k.getCurrentIncomingKeys();
assertEquals(timePeriod, inCurr.getTimePeriod());
assertEquals(base + 1, inCurr.getWindowBase()); assertEquals(base + 1, inCurr.getWindowBase());
assertArrayEquals(bitmap, inCurr.getWindowBitmap()); assertArrayEquals(bitmap, inCurr.getWindowBitmap());
@@ -1965,6 +2236,39 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testPendingContacts() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
assertEquals(emptyList(), db.getPendingContacts(txn));
db.addPendingContact(txn, pendingContact);
Collection<PendingContact> pendingContacts =
db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size());
PendingContact retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(pendingContact.getState(), retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.setPendingContactState(txn, pendingContact.getId(), FAILED);
pendingContacts = db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size());
retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(FAILED, retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.removePendingContact(txn, pendingContact.getId());
assertEquals(emptyList(), db.getPendingContacts(txn));
db.commitTransaction(txn);
db.close();
}
private Database<Connection> open(boolean resume) throws Exception { private Database<Connection> open(boolean resume) throws Exception {
return open(resume, new TestMessageFactory(), new SystemClock()); return open(resume, new TestMessageFactory(), new SystemClock());
} }
@@ -1978,27 +2282,48 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
return db; return db;
} }
private TransportKeys createTransportKeys(long rotationPeriod, private TransportKeys createTransportKeys(long timePeriod, boolean active) {
boolean active) {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
rotationPeriod - 1, 123, new byte[4]); timePeriod - 1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey(); SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey(); SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey, IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
rotationPeriod, 234, new byte[4]); timePeriod, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey(); SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey(); SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey, IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
rotationPeriod + 1, 345, new byte[4]); timePeriod + 1, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey(); SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
rotationPeriod, 456, active); timePeriod, 456, active);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }
private HandshakeKeys createHandshakeKeys(long timePeriod,
SecretKey rootKey, boolean alice) {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
timePeriod - 1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
timePeriod, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
timePeriod + 1, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
timePeriod, 456, true);
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
rootKey, alice);
}
@After @After
public void tearDown() { public void tearDown() {
deleteTestDirectory(testDir); deleteTestDirectory(testDir);

View File

@@ -17,7 +17,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -74,7 +74,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
Payload ourPayload = new Payload(aliceCommit, null); Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey(); SecretKey masterKey = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -134,13 +134,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
true, false); true, false);
will(returnValue(bobConfirm)); will(returnValue(bobConfirm));
// Alice computes master secret // Alice derives master key
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
will(returnValue(masterSecret)); will(returnValue(masterKey));
}}); }});
// execute // execute
assertThat(masterSecret, is(equalTo(protocol.perform()))); assertThat(masterKey, is(equalTo(protocol.perform())));
} }
@Test @Test
@@ -150,7 +150,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
Payload ourPayload = new Payload(bobCommit, null); Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey(); SecretKey masterKey = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -209,13 +209,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobConfirm)); will(returnValue(bobConfirm));
oneOf(transport).sendConfirm(bobConfirm); oneOf(transport).sendConfirm(bobConfirm);
// Bob computes master secret // Bob derives master key
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
will(returnValue(masterSecret)); will(returnValue(masterKey));
}}); }});
// execute // execute
assertThat(masterSecret, is(equalTo(protocol.perform()))); assertThat(masterKey, is(equalTo(protocol.perform())));
} }
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
@@ -373,8 +373,8 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice aborts // Alice aborts
oneOf(transport).sendAbort(false); oneOf(transport).sendAbort(false);
// Alice never computes master secret // Alice never derives master key
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); never(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
}}); }});
// execute // execute

View File

@@ -12,8 +12,8 @@ import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; 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.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
@@ -51,7 +51,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final ContactId contactId = new ContactId(123); private final ContactId contactId = new ContactId(123);
private final ContactId inactiveContactId = new ContactId(234); private final ContactId inactiveContactId = new ContactId(234);
private final KeySetId keySetId = new KeySetId(345); private final TransportKeySetId keySetId = new TransportKeySetId(345);
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final TransportId unknownTransportId = getTransportId(); private final TransportId unknownTransportId = getTransportId();
private final StreamContext streamContext = private final StreamContext streamContext =
@@ -113,8 +113,8 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId, Map<TransportId, TransportKeySetId> ids = keyManager.addContact(txn,
secretKey, timestamp, alice, active); contactId, secretKey, timestamp, alice, active);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }

View File

@@ -8,10 +8,10 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.StreamContext; 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.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
@@ -57,31 +57,31 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final long maxLatency = 30 * 1000; // 30 seconds private final long maxLatency = 30 * 1000; // 30 seconds
private final long rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
private final ContactId contactId = new ContactId(123); private final ContactId contactId = new ContactId(123);
private final ContactId contactId1 = new ContactId(234); private final ContactId contactId1 = new ContactId(234);
private final KeySetId keySetId = new KeySetId(345); private final TransportKeySetId keySetId = new TransportKeySetId(345);
private final KeySetId keySetId1 = new KeySetId(456); private final TransportKeySetId keySetId1 = new TransportKeySetId(456);
private final SecretKey tagKey = TestUtils.getSecretKey(); private final SecretKey tagKey = TestUtils.getSecretKey();
private final SecretKey headerKey = TestUtils.getSecretKey(); private final SecretKey headerKey = TestUtils.getSecretKey();
private final SecretKey masterKey = TestUtils.getSecretKey(); private final SecretKey rootKey = TestUtils.getSecretKey();
private final Random random = new Random(); private final Random random = new Random();
@Test @Test
public void testKeysAreRotatedAtStartup() throws Exception { public void testKeysAreRotatedAtStartup() throws Exception {
TransportKeys shouldRotate = createTransportKeys(900, 0, true); TransportKeys shouldRotate = createTransportKeys(900, 0, true);
TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true); TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
Collection<KeySet> loaded = asList( Collection<TransportKeySet> loaded = asList(
new KeySet(keySetId, contactId, shouldRotate), new TransportKeySet(keySetId, contactId, shouldRotate),
new KeySet(keySetId1, contactId1, shouldNotRotate) new TransportKeySet(keySetId1, contactId1, shouldNotRotate)
); );
TransportKeys rotated = createTransportKeys(1000, 0, true); TransportKeys rotated = createTransportKeys(1000, 0, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get the current time (1 ms after start of rotation period 1000) // Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1)); will(returnValue(timePeriodLength * 1000 + 1));
// Load the transport keys // Load the transport keys
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
@@ -98,11 +98,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn, oneOf(db).updateTransportKeys(txn, singletonList(
singletonList(new KeySet(keySetId, contactId, rotated))); new TransportKeySet(keySetId, contactId, rotated)));
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength - 1), with(MILLISECONDS)); with(timePeriodLength - 1), with(MILLISECONDS));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
@@ -120,12 +120,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
999, alice, true); 999, alice, true);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (1 ms after start of rotation period 1000) // Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1)); will(returnValue(timePeriodLength * 1000 + 1));
// Rotate the transport keys // Rotate the transport keys
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(rotated)); will(returnValue(rotated));
@@ -144,10 +144,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is 1 ms before the start of rotation period 1000 // The timestamp is 1 ms before the start of time period 1000
long timestamp = rotationPeriodLength * 1000 - 1; long timestamp = timePeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@@ -177,10 +177,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
} }
@@ -203,10 +203,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
// The first request should return a stream context // The first request should return a stream context
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn, StreamContext ctx = transportKeyManager.getStreamContext(txn,
@@ -235,10 +235,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, active)); rootKey, timestamp, alice, active));
assertEquals(active, assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId)); transportKeyManager.canSendOutgoingStreams(contactId));
// The tag should not be recognised // The tag should not be recognised
@@ -256,12 +256,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
List<byte[]> tags = new ArrayList<>(); List<byte[]> tags = new ArrayList<>();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, true); 1000, alice, true);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000) // Get the current time (the start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000)); will(returnValue(timePeriodLength * 1000));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -280,7 +280,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(tagKey), with(PROTOCOL_VERSION), with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE)); with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1) // Save the reordering window (previous time period, base 1)
oneOf(db).setReorderingWindow(txn, keySetId, transportId, 999, oneOf(db).setReorderingWindow(txn, keySetId, transportId, 999,
1, new byte[REORDERING_WINDOW_SIZE / 8]); 1, new byte[REORDERING_WINDOW_SIZE / 8]);
}}); }});
@@ -288,12 +288,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous rotation period, stream number 0) // Use the first tag (previous time period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
byte[] tag = tags.get(0); byte[] tag = tags.get(0);
// The first request should return a stream context // The first request should return a stream context
@@ -313,16 +313,16 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testKeysAreRotatedToCurrentPeriod() throws Exception { public void testKeysAreRotatedToCurrentPeriod() throws Exception {
TransportKeys transportKeys = createTransportKeys(1000, 0, true); TransportKeys transportKeys = createTransportKeys(1000, 0, true);
Collection<KeySet> loaded = Collection<TransportKeySet> loaded = singletonList(
singletonList(new KeySet(keySetId, contactId, transportKeys)); new TransportKeySet(keySetId, contactId, transportKeys));
TransportKeys rotated = createTransportKeys(1001, 0, true); TransportKeys rotated = createTransportKeys(1001, 0, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Transaction txn1 = new Transaction(null, false); Transaction txn1 = new Transaction(null, false);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Get the current time (the start of rotation period 1000) // Get the current time (the start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000)); will(returnValue(timePeriodLength * 1000));
// Load the transport keys // Load the transport keys
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
@@ -336,17 +336,17 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength), with(MILLISECONDS)); with(timePeriodLength), with(MILLISECONDS));
will(new RunAction()); will(new RunAction());
oneOf(dbExecutor).execute(with(any(Runnable.class))); oneOf(dbExecutor).execute(with(any(Runnable.class)));
will(new RunAction()); will(new RunAction());
// Start a transaction for key rotation // Start a transaction for key rotation
oneOf(db).transaction(with(false), withDbRunnable(txn1)); oneOf(db).transaction(with(false), withDbRunnable(txn1));
// Get the current time (the start of rotation period 1001) // Get the current time (the start of time period 1001)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1001)); will(returnValue(timePeriodLength * 1001));
// Rotate the transport keys // Rotate the transport keys
oneOf(transportCrypto).rotateTransportKeys( oneOf(transportCrypto).rotateTransportKeys(
with(any(TransportKeys.class)), with(1001L)); with(any(TransportKeys.class)), with(1001L));
@@ -359,11 +359,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn1, oneOf(db).updateTransportKeys(txn1, singletonList(
singletonList(new KeySet(keySetId, contactId, rotated))); new TransportKeySet(keySetId, contactId, rotated)));
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength), with(MILLISECONDS)); with(timePeriodLength), with(MILLISECONDS));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
@@ -391,10 +391,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, false)); rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -421,12 +421,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
List<byte[]> tags = new ArrayList<>(); List<byte[]> tags = new ArrayList<>();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, false); 1000, alice, false);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000) // Get the current time (the start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000)); will(returnValue(timePeriodLength * 1000));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -445,7 +445,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(tagKey), with(PROTOCOL_VERSION), with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE)); with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1) // Save the reordering window (previous time period, base 1)
oneOf(db).setReorderingWindow(txn, keySetId, transportId, 999, oneOf(db).setReorderingWindow(txn, keySetId, transportId, 999,
1, new byte[REORDERING_WINDOW_SIZE / 8]); 1, new byte[REORDERING_WINDOW_SIZE / 8]);
// Activate the keys // Activate the keys
@@ -457,10 +457,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of time period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice, false)); rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -488,12 +488,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private void expectAddContactNoRotation(boolean alice, boolean active, private void expectAddContactNoRotation(boolean alice, boolean active,
TransportKeys transportKeys, Transaction txn) throws Exception { TransportKeys transportKeys, Transaction txn) throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, active); 1000, alice, active);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000) // Get the current time (the start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000)); will(returnValue(timePeriodLength * 1000));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -510,16 +510,16 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
}}); }});
} }
private TransportKeys createTransportKeys(long rotationPeriod, private TransportKeys createTransportKeys(long timePeriod,
long streamCounter, boolean active) { long streamCounter, boolean active) {
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey, IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
rotationPeriod - 1); timePeriod - 1);
IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey, IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey,
rotationPeriod); timePeriod);
IncomingKeys inNext = new IncomingKeys(tagKey, headerKey, IncomingKeys inNext = new IncomingKeys(tagKey, headerKey,
rotationPeriod + 1); timePeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
rotationPeriod, streamCounter, active); timePeriod, streamCounter, active);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }

View File

@@ -23,7 +23,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
@@ -430,7 +430,7 @@ class IntroduceeProtocolEngine
s.getRemote().acceptTimestamp); s.getRemote().acceptTimestamp);
if (timestamp == -1) throw new AssertionError(); if (timestamp == -1) throw new AssertionError();
Map<TransportId, KeySetId> keys = null; Map<TransportId, TransportKeySetId> keys = null;
try { try {
contactManager contactManager
.addContact(txn, s.getRemote().author, localAuthor.getId(), .addContact(txn, s.getRemote().author, localAuthor.getId(),

View File

@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.introduction.Role; import org.briarproject.briar.api.introduction.Role;
@@ -17,9 +17,9 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_ACTIVATE; import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_ACTIVATE;
import static org.briarproject.briar.introduction.IntroduceeState.START; import static org.briarproject.briar.introduction.IntroduceeState.START;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -33,12 +33,12 @@ class IntroduceeSession extends Session<IntroduceeState>
@Nullable @Nullable
private final byte[] masterKey; private final byte[] masterKey;
@Nullable @Nullable
private final Map<TransportId, KeySetId> transportKeys; private final Map<TransportId, TransportKeySetId> transportKeys;
IntroduceeSession(SessionId sessionId, IntroduceeState state, IntroduceeSession(SessionId sessionId, IntroduceeState state,
long requestTimestamp, GroupId contactGroupId, Author introducer, long requestTimestamp, GroupId contactGroupId, Author introducer,
Local local, Remote remote, @Nullable byte[] masterKey, Local local, Remote remote, @Nullable byte[] masterKey,
@Nullable Map<TransportId, KeySetId> transportKeys) { @Nullable Map<TransportId, TransportKeySetId> transportKeys) {
super(sessionId, state, requestTimestamp); super(sessionId, state, requestTimestamp);
this.contactGroupId = contactGroupId; this.contactGroupId = contactGroupId;
this.introducer = introducer; this.introducer = introducer;
@@ -113,7 +113,8 @@ class IntroduceeSession extends Session<IntroduceeState>
} }
static IntroduceeSession awaitActivate(IntroduceeSession s, AuthMessage m, static IntroduceeSession awaitActivate(IntroduceeSession s, AuthMessage m,
Message sent, @Nullable Map<TransportId, KeySetId> transportKeys) { Message sent,
@Nullable Map<TransportId, TransportKeySetId> transportKeys) {
Local local = new Local(s.local, sent.getId(), sent.getTimestamp()); Local local = new Local(s.local, sent.getId(), sent.getTimestamp());
Remote remote = new Remote(s.remote, m.getMessageId()); Remote remote = new Remote(s.remote, m.getMessageId());
return new IntroduceeSession(s.getSessionId(), AWAIT_ACTIVATE, return new IntroduceeSession(s.getSessionId(), AWAIT_ACTIVATE,
@@ -180,7 +181,7 @@ class IntroduceeSession extends Session<IntroduceeState>
} }
@Nullable @Nullable
Map<TransportId, KeySetId> getTransportKeys() { Map<TransportId, TransportKeySetId> getTransportKeys() {
return transportKeys; return transportKeys;
} }

View File

@@ -41,7 +41,8 @@ interface IntroductionCrypto {
/** /**
* Derives a MAC key from the session's master key for Alice or Bob. * Derives a MAC key from the session's master key for Alice or Bob.
* *
* @param masterKey The key returned by {@link #deriveMasterKey(IntroduceeSession)} * @param masterKey The key returned by
* {@link #deriveMasterKey(IntroduceeSession)}
* @param alice true for Alice's MAC key, false for Bob's * @param alice true for Alice's MAC key, false for Bob's
* @return The MAC key * @return The MAC key
*/ */

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.briar.introduction.IntroduceeSession.Common; import org.briarproject.briar.introduction.IntroduceeSession.Common;
import org.briarproject.briar.introduction.IntroduceeSession.Local; import org.briarproject.briar.introduction.IntroduceeSession.Local;
import org.briarproject.briar.introduction.IntroduceeSession.Remote; import org.briarproject.briar.introduction.IntroduceeSession.Remote;
@@ -143,10 +143,10 @@ class SessionEncoderImpl implements SessionEncoder {
@Nullable @Nullable
private BdfDictionary encodeTransportKeys( private BdfDictionary encodeTransportKeys(
@Nullable Map<TransportId, KeySetId> keys) { @Nullable Map<TransportId, TransportKeySetId> keys) {
if (keys == null) return null; if (keys == null) return null;
BdfDictionary d = new BdfDictionary(); BdfDictionary d = new BdfDictionary();
for (Map.Entry<TransportId, KeySetId> e : keys.entrySet()) { for (Map.Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
d.put(e.getKey().getString(), e.getValue().getInt()); d.put(e.getKey().getString(), e.getValue().getInt());
} }
return d; return d;

View File

@@ -10,7 +10,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.introduction.Role; import org.briarproject.briar.api.introduction.Role;
import org.briarproject.briar.introduction.IntroduceeSession.Local; import org.briarproject.briar.introduction.IntroduceeSession.Local;
@@ -110,7 +110,7 @@ class SessionParserImpl implements SessionParser {
Local local = parseLocal(d.getDictionary(SESSION_KEY_LOCAL)); Local local = parseLocal(d.getDictionary(SESSION_KEY_LOCAL));
Remote remote = parseRemote(d.getDictionary(SESSION_KEY_REMOTE)); Remote remote = parseRemote(d.getDictionary(SESSION_KEY_REMOTE));
byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY); byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY);
Map<TransportId, KeySetId> transportKeys = parseTransportKeys( Map<TransportId, TransportKeySetId> transportKeys = parseTransportKeys(
d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS)); d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS));
return new IntroduceeSession(sessionId, state, requestTimestamp, return new IntroduceeSession(sessionId, state, requestTimestamp,
introducerGroupId, introducer, local, remote, introducerGroupId, introducer, local, remote,
@@ -184,14 +184,13 @@ class SessionParserImpl implements SessionParser {
} }
@Nullable @Nullable
private Map<TransportId, KeySetId> parseTransportKeys( private Map<TransportId, TransportKeySetId> parseTransportKeys(
@Nullable BdfDictionary d) throws FormatException { @Nullable BdfDictionary d) throws FormatException {
if (d == null) return null; if (d == null) return null;
Map<TransportId, KeySetId> map = new HashMap<>(d.size()); Map<TransportId, TransportKeySetId> map = new HashMap<>(d.size());
for (String key : d.keySet()) { for (String key : d.keySet()) {
map.put(new TransportId(key), map.put(new TransportId(key),
new KeySetId(d.getLong(key).intValue()) new TransportKeySetId(d.getLong(key).intValue()));
);
} }
return map; return map;
} }

View File

@@ -10,7 +10,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.introduction.IntroducerSession.Introducee; import org.briarproject.briar.introduction.IntroducerSession.Introducee;
@@ -75,7 +75,8 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
getTransportPropertiesMap(3); getTransportPropertiesMap(3);
private final Map<TransportId, TransportProperties> private final Map<TransportId, TransportProperties>
remoteTransportProperties = getTransportPropertiesMap(3); remoteTransportProperties = getTransportPropertiesMap(3);
private final Map<TransportId, KeySetId> transportKeys = new HashMap<>(); private final Map<TransportId, TransportKeySetId> transportKeys =
new HashMap<>();
private final byte[] localMacKey = getRandomBytes(SecretKey.LENGTH); private final byte[] localMacKey = getRandomBytes(SecretKey.LENGTH);
private final byte[] remoteMacKey = getRandomBytes(SecretKey.LENGTH); private final byte[] remoteMacKey = getRandomBytes(SecretKey.LENGTH);
@@ -88,9 +89,9 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
sessionParser = new SessionParserImpl(clientHelper); sessionParser = new SessionParserImpl(clientHelper);
author1 = getRealAuthor(authorFactory); author1 = getRealAuthor(authorFactory);
author2 = getRealAuthor(authorFactory); author2 = getRealAuthor(authorFactory);
transportKeys.put(getTransportId(), new KeySetId(1)); transportKeys.put(getTransportId(), new TransportKeySetId(1));
transportKeys.put(getTransportId(), new KeySetId(2)); transportKeys.put(getTransportId(), new TransportKeySetId(2));
transportKeys.put(getTransportId(), new KeySetId(3)); transportKeys.put(getTransportId(), new TransportKeySetId(3));
} }
@Test @Test

View File

@@ -56,7 +56,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private final File testDir = getTestDirectory(); private final File testDir = getTestDirectory();
private final File aliceDir = new File(testDir, "alice"); private final File aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob"); private final File bobDir = new File(testDir, "bob");
private final SecretKey master = getSecretKey(); private final SecretKey rootKey = getSecretKey();
private final long timestamp = System.currentTimeMillis(); private final long timestamp = System.currentTimeMillis();
private SimplexMessagingIntegrationTestComponent alice, bob; private SimplexMessagingIntegrationTestComponent alice, bob;
@@ -110,7 +110,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
lifecycleManager.waitForStartup(); lifecycleManager.waitForStartup();
// Add the other user as a contact // Add the other user as a contact
ContactManager contactManager = device.getContactManager(); ContactManager contactManager = device.getContactManager();
return contactManager.addContact(remote, local.getId(), master, return contactManager.addContact(remote, local.getId(), rootKey,
timestamp, alice, true, true); timestamp, alice, true, true);
} }