Merge branch '1570-derive-handshake-root-key' into 'master'

Add contact manager and key manager methods for converting a pending contact

Closes #1570

See merge request briar/briar!1114
This commit is contained in:
Torsten Grote
2019-06-03 14:33:21 +00:00
23 changed files with 369 additions and 407 deletions

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@@ -16,8 +15,6 @@ import javax.annotation.concurrent.ThreadSafe;
@NotNullByDefault @NotNullByDefault
public class Bytes implements Comparable<Bytes> { public class Bytes implements Comparable<Bytes> {
public static final BytesComparator COMPARATOR = new BytesComparator();
private final byte[] bytes; private final byte[] bytes;
private int hashCode = -1; private int hashCode = -1;
@@ -45,14 +42,7 @@ public class Bytes implements Comparable<Bytes> {
@Override @Override
public int compareTo(Bytes other) { public int compareTo(Bytes other) {
byte[] aBytes = bytes, bBytes = other.bytes; return compare(bytes, other.bytes);
int length = Math.min(aBytes.length, bBytes.length);
for (int i = 0; i < length; i++) {
int aUnsigned = aBytes[i] & 0xFF, bUnsigned = bBytes[i] & 0xFF;
if (aUnsigned < bUnsigned) return -1;
if (aUnsigned > bUnsigned) return 1;
}
return aBytes.length - bBytes.length;
} }
@Override @Override
@@ -61,11 +51,13 @@ public class Bytes implements Comparable<Bytes> {
"(" + StringUtils.toHexString(getBytes()) + ")"; "(" + StringUtils.toHexString(getBytes()) + ")";
} }
public static class BytesComparator implements Comparator<Bytes> { public static int compare(byte[] a, byte[] b) {
int length = Math.min(a.length, b.length);
@Override for (int i = 0; i < length; i++) {
public int compare(Bytes a, Bytes b) { int aUnsigned = a[i] & 0xFF, bUnsigned = b[i] & 0xFF;
return a.compareTo(b); if (aUnsigned < bUnsigned) return -1;
if (aUnsigned > bUnsigned) return 1;
} }
return a.length - b.length;
} }
} }

View File

@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -29,10 +30,18 @@ public interface ContactManager {
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID * derives and stores rotation mode transport keys for each transport, and
* for the contact. * returns an ID for the contact.
* *
* @param rootKey The root key for a set of rotation mode transport keys
* @param timestamp The timestamp for deriving rotation mode transport
* keys from the root key
* @param alice True if the local party is Alice * @param alice True if the local party is Alice
* @param verified True if the contact's identity has been verified, which
* is true if the contact was added in person or false if the contact was
* introduced or added remotely
* @param active True if the rotation mode transport keys can be used for
* outgoing streams
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey rootKey, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
@@ -40,28 +49,52 @@ public interface ContactManager {
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* replacing the given pending contact, derives and stores transport keys * replacing the given pending contact, derives and stores handshake mode
* for each transport, and returns an ID for the contact. * and rotation mode transport keys for each transport, and returns an ID
* for the contact.
* *
* @param rootKey The root key for a set of rotation mode transport keys
* @param timestamp The timestamp for deriving rotation mode transport
* keys from the root key
* @param alice True if the local party is Alice * @param alice True if the local party is Alice
* @param verified True if the contact's identity has been verified, which
* is true if the contact was added in person or false if the contact was
* introduced or added remotely
* @param active True if the rotation mode transport keys can be used for
* outgoing streams
* @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid
*/ */
ContactId addContact(Transaction txn, PendingContactId p, Author remote, ContactId addContact(Transaction txn, PendingContactId p, Author remote,
AuthorId local, SecretKey rootKey, long timestamp, boolean alice, AuthorId local, SecretKey rootKey, long timestamp, boolean alice,
boolean verified, boolean active) throws DbException; boolean verified, boolean active)
throws DbException, GeneralSecurityException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms * Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact. * and returns an ID for the contact.
*
* @param verified True if the contact's identity has been verified, which
* is true if the contact was added in person or false if the contact was
* introduced or added remotely
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException; boolean verified) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID * derives and stores rotation mode transport keys for each transport, and
* for the contact. * returns an ID for the contact.
* *
* @param rootKey The root key for a set of rotation mode transport keys
* @param timestamp The timestamp for deriving rotation mode transport
* keys from the root key
* @param alice True if the local party is Alice * @param alice True if the local party is Alice
* @param verified True if the contact's identity has been verified, which
* is true if the contact was added in person or false if the contact was
* introduced or added remotely
* @param active True if the rotation mode transport keys can be used for
* outgoing streams
*/ */
ContactId addContact(Author remote, AuthorId local, SecretKey rootKey, ContactId addContact(Author remote, AuthorId local, SecretKey rootKey,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
@@ -77,15 +110,16 @@ public interface ContactManager {
* Creates a {@link PendingContact} from the given handshake link and * Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it. * alias, adds it to the database and returns it.
* *
* @param link The handshake link received from the contact we want to add * @param link The handshake link received from the pending contact
* @param alias The alias the user has given this contact * @param alias The alias the user has given this pending contact
* @return A PendingContact representing the contact to be added
* @throws UnsupportedVersionException If the link uses a format version * @throws UnsupportedVersionException If the link uses a format version
* that is not supported * that is not supported
* @throws FormatException If the link is invalid * @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid
*/ */
PendingContact addPendingContact(String link, String alias) PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException; throws DbException, FormatException, GeneralSecurityException;
/** /**
* Returns the pending contact with the given ID. * Returns the pending contact with the given ID.
@@ -116,8 +150,8 @@ public interface ContactManager {
Contact getContact(Transaction txn, ContactId c) throws DbException; Contact getContact(Transaction txn, ContactId c) throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given {@code remoteAuthorId}
* that was added by the LocalAuthor with the given localAuthorId * that belongs to the local pseudonym with the given {@code localAuthorId}.
* *
* @throws NoSuchContactException If the contact is not in the database * @throws NoSuchContactException If the contact is not in the database
*/ */
@@ -125,8 +159,8 @@ public interface ContactManager {
throws DbException; throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given {@code remoteAuthorId}
* that was added by the LocalAuthor with the given localAuthorId * that belongs to the local pseudonym with the given {@code localAuthorId}.
* *
* @throws NoSuchContactException If the contact is not in the database * @throws NoSuchContactException If the contact is not in the database
*/ */
@@ -134,7 +168,7 @@ public interface ContactManager {
AuthorId localAuthorId) throws DbException; AuthorId localAuthorId) throws DbException;
/** /**
* Returns all active contacts. * Returns all contacts.
*/ */
Collection<Contact> getContacts() throws DbException; Collection<Contact> getContacts() throws DbException;
@@ -149,25 +183,27 @@ public interface ContactManager {
void removeContact(Transaction txn, ContactId c) throws DbException; void removeContact(Transaction txn, ContactId c) throws DbException;
/** /**
* Sets an alias for the contact or unsets it if alias is null. * Sets an alias for the contact or unsets it if {@code alias} is null.
*/ */
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias) void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
throws DbException; throws DbException;
/** /**
* Sets an alias for the contact or unsets it if alias is null. * Sets an alias for the contact or unsets it if {@code alias} is null.
*/ */
void setContactAlias(ContactId c, @Nullable String alias) void setContactAlias(ContactId c, @Nullable String alias)
throws DbException; throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this {@code remoteAuthorId} belongs to
* the local pseudonym with this {@code localAuthorId}.
*/ */
boolean contactExists(Transaction txn, AuthorId remoteAuthorId, boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException; AuthorId localAuthorId) throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this {@code remoteAuthorId} belongs to
* the local pseudonym with this {@code localAuthorId}.
*/ */
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException; throws DbException;

View File

@@ -3,12 +3,37 @@ 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.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.security.GeneralSecurityException;
/** /**
* Crypto operations for the transport security protocol - see * Crypto operations for the transport security protocol - see
* https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md * https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md
*/ */
public interface TransportCrypto { public interface TransportCrypto {
/**
* Returns true if the local peer is Alice.
*/
boolean isAlice(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair);
/**
* Derives the static master key shared with a contact or pending contact.
*/
SecretKey deriveStaticMasterKey(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair) throws GeneralSecurityException;
/**
* Derives the handshake mode root key from the static master key. To
* prevent tag reuse, separate root keys are derived for contacts and
* pending contacts.
*
* @param pendingContact Whether the static master key is shared with a
* pending contact or a contact
*/
SecretKey deriveHandshakeRootKey(SecretKey staticMasterKey,
boolean pendingContact);
/** /**
* Derives initial rotation mode transport keys for the given transport in * Derives initial rotation mode transport keys for the given transport in
* the given time period from the given root key. * the given time period from the given root key.

View File

@@ -61,14 +61,7 @@ public interface DatabaseComponent extends TransactionManager {
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException; @Nullable PublicKey handshake, boolean verified) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* replacing the given pending contact, and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, PendingContactId p, Author remote,
AuthorId local, boolean verified) throws DbException;
/** /**
* Stores a group. * Stores a group.

View File

@@ -2,11 +2,14 @@ package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import java.security.GeneralSecurityException;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -18,9 +21,9 @@ import javax.annotation.Nullable;
public interface KeyManager { public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * Derives and stores a set of rotation mode transport keys for
* stores a set of rotation mode transport keys for communicating with the * communicating with the given contact over each transport and returns the
* contact over each transport and returns the key set IDs. * key set IDs.
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
@@ -28,7 +31,7 @@ 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> addContactWithRotationKeys(Transaction txn, Map<TransportId, KeySetId> addRotationKeys(Transaction txn,
ContactId c, SecretKey rootKey, long timestamp, boolean alice, ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException; boolean active) throws DbException;
@@ -39,11 +42,10 @@ public interface KeyManager {
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
*
* @param alice True if the local party is Alice
*/ */
Map<TransportId, KeySetId> addContactWithHandshakeKeys(Transaction txn, Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
ContactId c, SecretKey rootKey, boolean alice) throws DbException; PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException;
/** /**
* Informs the key manager that a new pending contact has been added. * Informs the key manager that a new pending contact has been added.
@@ -53,12 +55,10 @@ public interface KeyManager {
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the pending contact can be * {@link StreamContext StreamContexts} for the pending contact can be
* created after this method has returned. * created after this method has returned.
*
* @param alice True if the local party is Alice
*/ */
Map<TransportId, KeySetId> addPendingContact(Transaction txn, Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException; throws DbException, GeneralSecurityException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.

View File

@@ -63,14 +63,6 @@ public interface TransportConstants {
int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH
- MAC_LENGTH; - MAC_LENGTH;
/**
* The minimum stream length in bytes that all transport plugins must
* support. Streams may be shorter than this length, but all transport
* plugins must support streams of at least this length.
*/
int MIN_STREAM_LENGTH = STREAM_HEADER_LENGTH + FRAME_HEADER_LENGTH
+ MAC_LENGTH;
/** /**
* The maximum difference in milliseconds between two peers' clocks. * The maximum difference in milliseconds between two peers' clocks.
*/ */
@@ -81,6 +73,26 @@ public interface TransportConstants {
*/ */
int REORDERING_WINDOW_SIZE = 32; int REORDERING_WINDOW_SIZE = 32;
/**
* Label for deriving the static master key from handshake key pairs.
*/
String STATIC_MASTER_KEY_LABEL =
"org.briarproject.bramble.transport/STATIC_MASTER_KEY";
/**
* Label for deriving the handshake mode root key for a pending contact
* from the static master key.
*/
String PENDING_CONTACT_ROOT_KEY_LABEL =
"org.briarproject.bramble.transport/PENDING_CONTACT_ROOT_KEY";
/**
* Label for deriving the handshake mode root key for a contact from the
* static master key.
*/
String CONTACT_ROOT_KEY_LABEL =
"org.briarproject.bramble.transport/CONTACT_ROOT_KEY";
/** /**
* Label for deriving Alice's initial tag key from the root key in * Label for deriving Alice's initial tag key from the root key in
* rotation mode. * rotation mode.

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.client; package org.briarproject.bramble.client;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
@@ -55,7 +54,7 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) { private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
try { try {
if (Bytes.COMPARATOR.compare(local, remote) < 0) if (local.compareTo(remote) < 0)
return clientHelper.toByteArray(BdfList.of(local, remote)); return clientHelper.toByteArray(BdfList.of(local, remote));
else return clientHelper.toByteArray(BdfList.of(remote, local)); else return clientHelper.toByteArray(BdfList.of(remote, local));
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -8,6 +8,8 @@ import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
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.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -21,6 +23,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import java.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -51,6 +54,7 @@ class ContactManagerImpl implements ContactManager {
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PendingContactFactory pendingContactFactory; private final PendingContactFactory pendingContactFactory;
private final List<ContactHook> hooks; private final List<ContactHook> hooks;
@Inject @Inject
@@ -73,9 +77,8 @@ class ContactManagerImpl implements ContactManager {
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey rootKey, 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); ContactId c = db.addContact(txn, remote, local, null, verified);
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp, keyManager.addRotationKeys(txn, c, rootKey, timestamp, alice, active);
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;
@@ -85,10 +88,14 @@ class ContactManagerImpl implements ContactManager {
public ContactId addContact(Transaction txn, PendingContactId p, public ContactId addContact(Transaction txn, PendingContactId p,
Author remote, AuthorId local, SecretKey rootKey, long timestamp, Author remote, AuthorId local, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active) boolean alice, boolean verified, boolean active)
throws DbException { throws DbException, GeneralSecurityException {
ContactId c = db.addContact(txn, p, remote, local, verified); PublicKey theirPublicKey = db.getPendingContact(txn, p).getPublicKey();
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp, db.removePendingContact(txn, p);
alice, active); ContactId c =
db.addContact(txn, remote, local, theirPublicKey, verified);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addContact(txn, c, theirPublicKey, ourKeyPair);
keyManager.addRotationKeys(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;
@@ -97,7 +104,7 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException { boolean verified) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, remote, local, null, verified);
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;
@@ -120,10 +127,19 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public PendingContact addPendingContact(String link, String alias) public PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException { throws DbException, FormatException, GeneralSecurityException {
PendingContact p = PendingContact p =
pendingContactFactory.createPendingContact(link, alias); pendingContactFactory.createPendingContact(link, alias);
db.transaction(false, txn -> db.addPendingContact(txn, p)); Transaction txn = db.startTransaction(false);
try {
db.addPendingContact(txn, p);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
ourKeyPair);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return p; return p;
} }

View File

@@ -1,6 +1,8 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
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;
@@ -10,9 +12,12 @@ import org.briarproject.bramble.api.transport.TransportKeys;
import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.Blake2bDigest; import org.spongycastle.crypto.digests.Blake2bDigest;
import java.security.GeneralSecurityException;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.System.arraycopy; import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.Bytes.compare;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_HEADER_LABEL; 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_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;
@@ -21,7 +26,10 @@ import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HAND
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_TAG_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.CONTACT_ROOT_KEY_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.PENDING_CONTACT_ROOT_KEY_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.STATIC_MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; 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;
@@ -40,6 +48,36 @@ class TransportCryptoImpl implements TransportCrypto {
this.crypto = crypto; this.crypto = crypto;
} }
@Override
public boolean isAlice(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair) {
byte[] theirPublic = theirHandshakePublicKey.getEncoded();
byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded();
return compare(ourPublic, theirPublic) < 0;
}
@Override
public SecretKey deriveStaticMasterKey(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair) throws GeneralSecurityException {
byte[] theirPublic = theirHandshakePublicKey.getEncoded();
byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded();
boolean alice = compare(ourPublic, theirPublic) < 0;
byte[][] inputs = {
alice ? ourPublic : theirPublic,
alice ? theirPublic : ourPublic
};
return crypto.deriveSharedSecret(STATIC_MASTER_KEY_LABEL,
theirHandshakePublicKey, ourHandshakeKeyPair, inputs);
}
@Override
public SecretKey deriveHandshakeRootKey(SecretKey staticMasterKey,
boolean pendingContact) {
String label = pendingContact ?
PENDING_CONTACT_ROOT_KEY_LABEL : CONTACT_ROOT_KEY_LABEL;
return crypto.deriveKey(label, staticMasterKey);
}
@Override @Override
public TransportKeys deriveRotationKeys(TransportId t, public TransportKeys deriveRotationKeys(TransportId t,
SecretKey rootKey, long timePeriod, boolean weAreAlice, SecretKey rootKey, long timePeriod, boolean weAreAlice,

View File

@@ -88,8 +88,8 @@ interface Database<T> {
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified) ContactId addContact(T txn, Author remote, AuthorId local,
throws DbException; @Nullable PublicKey handshake, boolean verified) throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -695,14 +695,6 @@ interface Database<T> {
void setTransportKeysActive(T txn, TransportId t, KeySetId k) void setTransportKeysActive(T txn, TransportId t, KeySetId k)
throws DbException; throws DbException;
/**
* Transfers ownership of any transport keys from the given pending contact
* to the given contact and copies the pending contact's handshake public
* key to the contact.
*/
void transferKeys(T txn, PendingContactId p, ContactId c)
throws DbException;
/** /**
* Updates the transmission count, expiry time and estimated time of arrival * Updates the transmission count, expiry time and estimated time of arrival
* of the given message with respect to the given contact, using the latency * of the given message with respect to the given contact, using the latency

View File

@@ -85,6 +85,7 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED; import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
@@ -99,7 +100,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
class DatabaseComponentImpl<T> implements DatabaseComponent { class DatabaseComponentImpl<T> implements DatabaseComponent {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(DatabaseComponentImpl.class.getName()); getLogger(DatabaseComponentImpl.class.getName());
private final Database<T> db; private final Database<T> db;
private final Class<T> txnClass; private final Class<T> txnClass;
@@ -234,39 +235,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public ContactId addContact(Transaction transaction, Author remote, public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean verified) throws DbException { AuthorId local, @Nullable PublicKey handshake, boolean verified)
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsIdentity(txn, local))
throw new NoSuchIdentityException();
if (db.containsIdentity(txn, remote.getId()))
throw new ContactExistsException(local, remote);
if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(local, remote);
ContactId c = db.addContact(txn, remote, local, verified);
transaction.attach(new ContactAddedEvent(c, verified));
return c;
}
@Override
public ContactId addContact(Transaction transaction, PendingContactId p,
Author remote, AuthorId local, boolean verified)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsIdentity(txn, local)) if (!db.containsIdentity(txn, local))
throw new NoSuchIdentityException(); throw new NoSuchIdentityException();
if (db.containsIdentity(txn, remote.getId())) if (db.containsIdentity(txn, remote.getId()))
throw new ContactExistsException(local, remote); throw new ContactExistsException(local, remote);
if (db.containsContact(txn, remote.getId(), local)) if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(local, remote); throw new ContactExistsException(local, remote);
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, remote, local, handshake, verified);
db.transferKeys(txn, p, c);
db.removePendingContact(txn, p);
transaction.attach(new ContactAddedEvent(c, verified)); transaction.attach(new ContactAddedEvent(c, verified));
transaction.attach(new PendingContactRemovedEvent(p));
return c; return c;
} }

View File

@@ -629,22 +629,25 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public ContactId addContact(Connection txn, Author remote, AuthorId local, public ContactId addContact(Connection txn, Author remote, AuthorId local,
boolean verified) throws DbException { @Nullable PublicKey handshake, boolean verified)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Create a contact row // Create a contact row
String sql = "INSERT INTO contacts" String sql = "INSERT INTO contacts"
+ " (authorId, formatVersion, name, publicKey," + " (authorId, formatVersion, name, publicKey,"
+ " localAuthorId, verified)" + " localAuthorId, handshakePublicKey, verified)"
+ " VALUES (?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getId().getBytes()); ps.setBytes(1, remote.getId().getBytes());
ps.setInt(2, remote.getFormatVersion()); ps.setInt(2, remote.getFormatVersion());
ps.setString(3, remote.getName()); ps.setString(3, remote.getName());
ps.setBytes(4, remote.getPublicKey().getEncoded()); ps.setBytes(4, remote.getPublicKey().getEncoded());
ps.setBytes(5, local.getBytes()); ps.setBytes(5, local.getBytes());
ps.setBoolean(6, verified); if (handshake == null) ps.setNull(6, BINARY);
else ps.setBytes(6, handshake.getEncoded());
ps.setBoolean(7, verified);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -3139,48 +3142,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void transferKeys(Connection txn, PendingContactId p, ContactId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Transfer the handshake public key
String sql = "SELECT publicKey from pendingContacts"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
byte[] publicKey = rs.getBytes(1);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
sql = "UPDATE contacts SET handshakePublicKey = ?"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, publicKey);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Transfer the transport keys
sql = "UPDATE outgoingKeys"
+ " SET contactId = ?, pendingContactId = NULL"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, p.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m, public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
int maxLatency) throws DbException { int maxLatency) throws DbException {

View File

@@ -3,12 +3,17 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
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.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
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.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
@@ -21,6 +26,7 @@ import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import java.security.GeneralSecurityException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -46,17 +52,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory; private final TransportKeyManagerFactory transportKeyManagerFactory;
private final TransportCrypto transportCrypto;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
KeyManagerImpl(DatabaseComponent db, @DatabaseExecutor Executor dbExecutor, KeyManagerImpl(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor,
PluginConfig pluginConfig, PluginConfig pluginConfig,
TransportKeyManagerFactory transportKeyManagerFactory) { TransportKeyManagerFactory transportKeyManagerFactory,
TransportCrypto transportCrypto) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory; this.transportKeyManagerFactory = transportKeyManagerFactory;
this.transportCrypto = transportCrypto;
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<>();
} }
@@ -90,41 +101,51 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, KeySetId> addContactWithRotationKeys( public Map<TransportId, KeySetId> addRotationKeys(
Transaction txn, ContactId c, SecretKey rootKey, long timestamp, Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
boolean alice, boolean active) throws DbException { boolean alice, boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> 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.addContactWithRotationKeys(txn, c, rootKey, timestamp, ids.put(t, m.addRotationKeys(txn, c, rootKey, timestamp,
alice, active)); alice, active));
} }
return ids; return ids;
} }
@Override @Override
public Map<TransportId, KeySetId> addContactWithHandshakeKeys( public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
Transaction txn, ContactId c, SecretKey rootKey, boolean alice) PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, false);
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> 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.addContactWithHandshakeKeys(txn, c, rootKey, alice)); ids.put(t, m.addHandshakeKeys(txn, c, rootKey, alice));
} }
return ids; return ids;
} }
@Override @Override
public Map<TransportId, KeySetId> addPendingContact(Transaction txn, public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> 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.addPendingContact(txn, p, rootKey, alice)); ids.put(t, m.addHandshakeKeys(txn, p, rootKey, alice));
} }
return ids; return ids;
} }
@@ -180,15 +201,27 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) { if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId()); removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof PendingContactRemovedEvent) {
PendingContactRemovedEvent p = (PendingContactRemovedEvent) e;
removePendingContact(p.getId());
} }
} }
@EventExecutor
private void removeContact(ContactId c) { private void removeContact(ContactId c) {
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values()) m.removeContact(c); for (TransportKeyManager m : managers.values()) m.removeContact(c);
}); });
} }
@EventExecutor
private void removePendingContact(PendingContactId p) {
dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values())
m.removePendingContact(p);
});
}
@Nullable @Nullable
private <T> T withManager(TransportId t, ManagerTask<T> task) private <T> T withManager(TransportId t, ManagerTask<T> task)
throws DbException { throws DbException {

View File

@@ -16,14 +16,14 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
KeySetId addContactWithRotationKeys(Transaction txn, ContactId c, KeySetId addRotationKeys(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException; throws DbException;
KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c, KeySetId addHandshakeKeys(Transaction txn, ContactId c,
SecretKey rootKey, boolean alice) throws DbException; SecretKey rootKey, boolean alice) throws DbException;
KeySetId addPendingContact(Transaction txn, PendingContactId p, KeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException; SecretKey rootKey, boolean alice) throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException; void activateKeys(Transaction txn, KeySetId k) throws DbException;

View File

@@ -115,11 +115,14 @@ class TransportKeyManagerImpl implements TransportKeyManager {
TransportKeys k = ks.getKeys(); TransportKeys k = ks.getKeys();
TransportKeys k1 = transportCrypto.updateTransportKeys(k, TransportKeys k1 = transportCrypto.updateTransportKeys(k,
timePeriod); timePeriod);
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(), if (k1.getTimePeriod() > k.getTimePeriod()) {
ks.getContactId(), null, k1); TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
if (k1.getTimePeriod() > k.getTimePeriod()) ks.getContactId(), ks.getPendingContactId(), k1);
updateResult.updated.add(ks1); updateResult.updated.add(ks1);
updateResult.current.add(ks1); updateResult.current.add(ks1);
} else {
updateResult.current.add(ks);
}
} }
return updateResult; return updateResult;
} }
@@ -207,7 +210,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c, public KeySetId addRotationKeys(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException { throws DbException {
lock.lock(); lock.lock();
@@ -222,7 +225,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
k = transportCrypto.updateTransportKeys(k, timePeriod); k = transportCrypto.updateTransportKeys(k, timePeriod);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k); KeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the keys
addKeys(keySetId, c, null, new MutableTransportKeys(k)); addKeys(keySetId, c, null, new MutableTransportKeys(k));
return keySetId; return keySetId;
} finally { } finally {
@@ -231,7 +234,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c, public KeySetId addHandshakeKeys(Transaction txn, ContactId c,
SecretKey rootKey, boolean alice) throws DbException { SecretKey rootKey, boolean alice) throws DbException {
lock.lock(); lock.lock();
try { try {
@@ -242,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
rootKey, timePeriod, alice); rootKey, timePeriod, alice);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k); KeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the keys
addKeys(keySetId, c, null, new MutableTransportKeys(k)); addKeys(keySetId, c, null, new MutableTransportKeys(k));
return keySetId; return keySetId;
} finally { } finally {
@@ -251,7 +254,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addPendingContact(Transaction txn, PendingContactId p, public KeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException { SecretKey rootKey, boolean alice) throws DbException {
lock.lock(); lock.lock();
try { try {
@@ -262,7 +265,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
rootKey, timePeriod, alice); rootKey, timePeriod, alice);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, p, k); KeySetId keySetId = db.addTransportKeys(txn, p, k);
// Initialise mutable state for the pending contact // Initialise mutable state for the keys
addKeys(keySetId, null, p, new MutableTransportKeys(k)); addKeys(keySetId, null, p, new MutableTransportKeys(k));
return keySetId; return keySetId;
} finally { } finally {

View File

@@ -71,10 +71,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified); oneOf(db).addContact(txn, remote, local, null, verified);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContactWithRotationKeys(txn, contactId, oneOf(keyManager).addRotationKeys(txn, contactId, rootKey,
rootKey, timestamp, alice, active); timestamp, alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
}}); }});

View File

@@ -176,7 +176,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).containsContact(txn, author.getId(), oneOf(database).containsContact(txn, author.getId(),
localAuthor.getId()); localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, localAuthor.getId(), true); oneOf(database).addContact(txn, author, localAuthor.getId(),
null, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// getContacts() // getContacts()
@@ -224,7 +225,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.addIdentity(transaction, identity); db.addIdentity(transaction, identity);
assertEquals(contactId, db.addContact(transaction, author, assertEquals(contactId, db.addContact(transaction, author,
localAuthor.getId(), true)); localAuthor.getId(), null, true));
assertEquals(singletonList(contact), assertEquals(singletonList(contact),
db.getContacts(transaction)); db.getContacts(transaction));
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
@@ -445,7 +446,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, localAuthor.getId(),
true)); null, true));
fail(); fail();
} catch (NoSuchIdentityException expected) { } catch (NoSuchIdentityException expected) {
// Expected // Expected
@@ -763,25 +764,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the pending contact is in the DB (which it's not) // Check whether the pending contact is in the DB (which it's not)
exactly(4).of(database).startTransaction(); exactly(3).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(4).of(database).containsPendingContact(txn, exactly(3).of(database).containsPendingContact(txn,
pendingContactId); pendingContactId);
will(returnValue(false)); will(returnValue(false));
exactly(4).of(database).abortTransaction(txn); exactly(3).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addTransportKeys(transaction, pendingContactId, db.addTransportKeys(transaction, pendingContactId,
@@ -1473,7 +1465,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, localAuthor.getId(),
true)); null, true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId()); assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
@@ -1503,70 +1495,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, localAuthor.getId(),
true)); null, true));
fail();
} catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
assertEquals(author, expected.getRemoteAuthor());
}
}
@Test
public void testCannotAddLocalIdentityAsContactFromPendingContact()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsPendingContact(txn, pendingContactId);
will(returnValue(true));
oneOf(database).containsIdentity(txn, localAuthor.getId());
will(returnValue(true));
// Contact is a local identity
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(true));
oneOf(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail();
} catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
assertEquals(author, expected.getRemoteAuthor());
}
}
@Test
public void testCannotAddDuplicateContactFromPendingContact()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsPendingContact(txn, pendingContactId);
will(returnValue(true));
oneOf(database).containsIdentity(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(false));
// Contact already exists for this local identity
oneOf(database).containsContact(txn, author.getId(),
localAuthor.getId());
will(returnValue(true));
oneOf(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId()); assertEquals(localAuthor.getId(), expected.getLocalAuthorId());

View File

@@ -548,7 +548,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
for (int i = 0; i < CONTACTS; i++) { for (int i = 0; i < CONTACTS; i++) {
ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(), ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(),
random.nextBoolean()); null, random.nextBoolean());
contacts.add(db.getContact(txn, c)); contacts.add(db.getContact(txn, c));
contactGroups.put(c, new ArrayList<>()); contactGroups.put(c, new ArrayList<>());
for (int j = 0; j < GROUPS_PER_CONTACT; j++) { for (int j = 0; j < GROUPS_PER_CONTACT; j++) {

View File

@@ -147,7 +147,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsGroup(txn, groupId)); assertFalse(db.containsGroup(txn, groupId));
db.addGroup(txn, group); db.addGroup(txn, group);
@@ -210,7 +210,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -241,7 +241,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared but unvalidated message // Add a contact, a shared group and a shared but unvalidated message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true, null); db.addMessage(txn, message, UNKNOWN, true, null);
@@ -286,7 +286,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, an invisible group and a shared message // Add a contact, an invisible group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -337,7 +337,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and an unshared message // Add a contact, a shared group and an unshared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false, null); db.addMessage(txn, message, DELIVERED, false, null);
@@ -368,7 +368,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -395,7 +395,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact and a visible group // Add a contact and a visible group
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
@@ -436,7 +436,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -568,7 +568,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact and a shared group // Add a contact and a shared group
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
@@ -588,7 +588,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact // Add a contact
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
// The group is not in the database // The group is not in the database
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -606,7 +606,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, an invisible group and a message // Add a contact, an invisible group and a message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -625,7 +625,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact and a group // Add a contact and a group
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
@@ -677,7 +677,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
@@ -786,7 +786,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, the transport and the handshake keys // Add the contact, the transport and the handshake keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
@@ -920,7 +920,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -964,7 +964,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, transport and handshake keys // Add the contact, transport and handshake keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -1011,7 +1011,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -1058,7 +1058,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, transport and handshake keys // Add the contact, transport and handshake keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -1104,7 +1104,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact associated with the local author // Add a contact associated with the local author
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
// Ensure contact is returned from database by author ID // Ensure contact is returned from database by author ID
Collection<Contact> contacts = Collection<Contact> contacts =
@@ -1135,7 +1135,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact associated with the local author // Add a contact associated with the local author
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
contacts = db.getContacts(txn, localAuthor.getId()); contacts = db.getContacts(txn, localAuthor.getId());
assertEquals(singletonList(contactId), contacts); assertEquals(singletonList(contactId), contacts);
@@ -1157,7 +1157,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact - initially there should be no offered messages // Add a contact - initially there should be no offered messages
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
assertEquals(0, db.countOfferedMessages(txn, contactId)); assertEquals(0, db.countOfferedMessages(txn, contactId));
// Add some offered messages and count them // Add some offered messages and count them
@@ -1741,7 +1741,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1853,9 +1853,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the same contact for each local author // Add the same contact for each local author
ContactId contactId = ContactId contactId =
db.addContact(txn, author, localAuthor.getId(), true); db.addContact(txn, author, localAuthor.getId(), null, true);
ContactId contactId1 = ContactId contactId1 =
db.addContact(txn, author, localAuthor1.getId(), true); db.addContact(txn, author, localAuthor1.getId(), null, true);
// The contacts should be distinct // The contacts should be distinct
assertNotEquals(contactId, contactId1); assertNotEquals(contactId, contactId1);
@@ -1875,7 +1875,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1929,7 +1929,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact // Add a contact
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
// The contact should have no alias // The contact should have no alias
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
@@ -1986,7 +1986,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false, null); db.addMessage(txn, message, UNKNOWN, false, null);
@@ -2070,7 +2070,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -2115,7 +2115,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -2229,60 +2229,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(expected.getTimestamp(), actual.getTimestamp()); assertEquals(expected.getTimestamp(), actual.getTimestamp());
} }
@Test
public void testTransferKeys() throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys =
createHandshakeKeys(1000, getSecretKey(), alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the pending contact, the transport and the handshake keys
db.addPendingContact(txn, pendingContact);
db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, pendingContact.getId(),
transportKeys));
Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, allKeys.size());
TransportKeySet ks = allKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
// Add a contact
db.addIdentity(txn, identity);
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
// The contact shouldn't have a handshake public key
Contact contact = db.getContact(txn, contactId);
assertNull(contact.getHandshakePublicKey());
// Transfer the keys to the contact
db.transferKeys(txn, pendingContact.getId(), contactId);
// The handshake public key should have been copied to the contact
contact = db.getContact(txn, contactId);
PublicKey handshakePublicKey = contact.getHandshakePublicKey();
assertNotNull(handshakePublicKey);
assertArrayEquals(pendingContact.getPublicKey().getEncoded(),
handshakePublicKey.getEncoded());
// The transport keys should have been transferred to the contact
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(1, allKeys.size());
ks = allKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testSetHandshakeKeyPair() throws Exception { public void testSetHandshakeKeyPair() throws Exception {
Identity withoutKeys = new Identity(localAuthor, null, null, Identity withoutKeys = new Identity(localAuthor, null, null,

View File

@@ -3,7 +3,10 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
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.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -25,6 +28,8 @@ import java.util.Random;
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 org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
@@ -41,6 +46,8 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
context.mock(TransportKeyManagerFactory.class); context.mock(TransportKeyManagerFactory.class);
private final TransportKeyManager transportKeyManager = private final TransportKeyManager transportKeyManager =
context.mock(TransportKeyManager.class); context.mock(TransportKeyManager.class);
private final TransportCrypto transportCrypto =
context.mock(TransportCrypto.class);
private final DeterministicExecutor executor = new DeterministicExecutor(); private final DeterministicExecutor executor = new DeterministicExecutor();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
@@ -57,10 +64,15 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
new StreamContext(null, pendingContactId, transportId, new StreamContext(null, pendingContactId, transportId,
getSecretKey(), getSecretKey(), 1, true); getSecretKey(), getSecretKey(), 1, true);
private final byte[] tag = getRandomBytes(TAG_LENGTH); private final byte[] tag = getRandomBytes(TAG_LENGTH);
private final PublicKey theirPublicKey = getAgreementPublicKey();
private final KeyPair ourKeyPair =
new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey());
private final SecretKey staticMasterKey = getSecretKey();
private final SecretKey rootKey = getSecretKey();
private final Random random = new Random(); private final Random random = new Random();
private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor, private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
pluginConfig, transportKeyManagerFactory); pluginConfig, transportKeyManagerFactory, transportCrypto);
@Before @Before
public void testStartService() throws Exception { public void testStartService() throws Exception {
@@ -98,45 +110,59 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportKeyManager).addContactWithRotationKeys(txn, oneOf(transportKeyManager).addRotationKeys(txn,
contactId, secretKey, timestamp, alice, active); contactId, secretKey, timestamp, alice, active);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addContactWithRotationKeys( Map<TransportId, KeySetId> ids = keyManager.addRotationKeys(
txn, contactId, secretKey, timestamp, alice, active); txn, contactId, secretKey, timestamp, alice, active);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }
@Test @Test
public void testAddContactWithHandshakeModeKeys() throws Exception { public void testAddContactWithHandshakePublicKey() throws Exception {
SecretKey secretKey = getSecretKey();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportKeyManager).addContactWithHandshakeKeys( oneOf(transportCrypto)
txn, contactId, secretKey, alice); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
will(returnValue(staticMasterKey));
oneOf(transportCrypto)
.deriveHandshakeRootKey(staticMasterKey, false);
will(returnValue(rootKey));
oneOf(transportCrypto).isAlice(theirPublicKey, ourKeyPair);
will(returnValue(alice));
oneOf(transportKeyManager).addHandshakeKeys(txn, contactId,
rootKey, alice);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addContactWithHandshakeKeys( Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
txn, contactId, secretKey, alice); theirPublicKey, ourKeyPair);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }
@Test @Test
public void testAddPendingContact() throws Exception { public void testAddPendingContact() throws Exception {
SecretKey secretKey = getSecretKey();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportKeyManager).addPendingContact(txn, pendingContactId, oneOf(transportCrypto)
secretKey, alice); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
will(returnValue(staticMasterKey));
oneOf(transportCrypto)
.deriveHandshakeRootKey(staticMasterKey, true);
will(returnValue(rootKey));
oneOf(transportCrypto).isAlice(theirPublicKey, ourKeyPair);
will(returnValue(alice));
oneOf(transportKeyManager).addHandshakeKeys(txn, pendingContactId,
rootKey, alice);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn, Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
pendingContactId, secretKey, alice); pendingContactId, theirPublicKey, ourKeyPair);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }

View File

@@ -21,6 +21,7 @@ import org.hamcrest.Description;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.api.Action; import org.jmock.api.Action;
import org.jmock.api.Invocation; import org.jmock.api.Invocation;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
@@ -72,6 +73,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final SecretKey rootKey = getSecretKey(); private final SecretKey rootKey = getSecretKey();
private final Random random = new Random(); private final Random random = new Random();
private TransportKeyManager transportKeyManager;
@Before
public void setUp() {
transportKeyManager = new TransportKeyManagerImpl(db, transportCrypto,
dbExecutor, scheduler, clock, transportId, maxLatency);
}
@Test @Test
public void testKeysAreUpdatedAtStartup() throws Exception { public void testKeysAreUpdatedAtStartup() throws Exception {
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
@@ -112,16 +121,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(timePeriodLength - 1), with(MILLISECONDS)); with(timePeriodLength - 1), with(MILLISECONDS));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertEquals(active, assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId)); transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
public void testRotationKeysAreDerivedAndUpdatedWhenAddingContact() public void testRotationKeysForContactAreDerivedAndUpdatedWhenAdded()
throws Exception { throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
@@ -156,14 +162,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is 1 ms before the start of time period 1000 // The timestamp is 1 ms before the start of time period 1000
long timestamp = timePeriodLength * 1000 - 1; long timestamp = timePeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(txn,
txn, contactId, rootKey, timestamp, alice, active)); contactId, rootKey, timestamp, alice, active));
assertEquals(active, assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId)); transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
public void testHandshakeKeysAreDerivedWhenAddingContact() public void testHandshakeKeysForContactAreDerivedWhenAdded()
throws Exception { throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice); TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
@@ -189,16 +195,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( assertEquals(keySetId, transportKeyManager.addHandshakeKeys(txn,
db, transportCrypto, dbExecutor, scheduler, clock, transportId, contactId, rootKey, alice));
maxLatency);
assertEquals(keySetId, transportKeyManager.addContactWithHandshakeKeys(
txn, contactId, rootKey, alice));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
public void testHandshakeKeysAreDerivedWhenAddingPendingContact() public void testHandshakeKeysForPendingContactAreDerivedWhenAdded()
throws Exception { throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice); TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
@@ -224,10 +227,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( assertEquals(keySetId, transportKeyManager.addHandshakeKeys(txn,
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertEquals(keySetId, transportKeyManager.addPendingContact(txn,
pendingContactId, rootKey, alice)); pendingContactId, rootKey, alice));
assertTrue(transportKeyManager.canSendOutgoingStreams( assertTrue(transportKeyManager.canSendOutgoingStreams(
pendingContactId)); pendingContactId));
@@ -269,12 +269,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
expectAddContactKeysNotUpdated(alice, true, transportKeys, txn); expectAddContactKeysNotUpdated(alice, true, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, true)); txn, contactId, rootKey, timestamp, alice, true));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -295,12 +292,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
oneOf(db).incrementStreamCounter(txn, transportId, keySetId); oneOf(db).incrementStreamCounter(txn, transportId, keySetId);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, true)); txn, contactId, 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));
@@ -327,12 +321,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
expectAddContactKeysNotUpdated(alice, active, transportKeys, txn); expectAddContactKeysNotUpdated(alice, active, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, active)); txn, contactId, rootKey, timestamp, alice, active));
assertEquals(active, assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId)); transportKeyManager.canSendOutgoingStreams(contactId));
@@ -380,12 +371,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
1, new byte[REORDERING_WINDOW_SIZE / 8]); 1, new byte[REORDERING_WINDOW_SIZE / 8]);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, true)); txn, contactId, rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous time period, stream number 0) // Use the first tag (previous time period, stream number 0)
@@ -461,9 +449,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(timePeriodLength), with(MILLISECONDS)); with(timePeriodLength), with(MILLISECONDS));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@@ -483,12 +468,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
oneOf(db).incrementStreamCounter(txn, transportId, keySetId); oneOf(db).incrementStreamCounter(txn, transportId, keySetId);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, false)); txn, contactId, 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));
@@ -549,12 +531,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
oneOf(db).incrementStreamCounter(txn, transportId, keySetId); oneOf(db).incrementStreamCounter(txn, transportId, keySetId);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, false)); txn, contactId, 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));

View File

@@ -16,6 +16,7 @@ import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import java.security.GeneralSecurityException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -102,14 +103,15 @@ public class AddContactViewModel extends AndroidViewModel {
} catch (UnsupportedVersionException e) { } catch (UnsupportedVersionException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
addContactResult.postValue(new LiveResult<>(e)); addContactResult.postValue(new LiveResult<>(e));
} catch (DbException | FormatException e) { } catch (DbException | FormatException
| GeneralSecurityException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
addContactResult.postValue(new LiveResult<>(e)); addContactResult.postValue(new LiveResult<>(e));
} }
}); });
} }
public LiveData<LiveResult<Boolean>> getAddContactResult() { LiveData<LiveResult<Boolean>> getAddContactResult() {
return addContactResult; return addContactResult;
} }

View File

@@ -444,7 +444,7 @@ class IntroduceeProtocolEngine
// add the keys to the new contact // add the keys to the new contact
//noinspection ConstantConditions //noinspection ConstantConditions
keys = keyManager.addContactWithRotationKeys(txn, c.getId(), keys = keyManager.addRotationKeys(txn, c.getId(),
new SecretKey(s.getMasterKey()), timestamp, new SecretKey(s.getMasterKey()), timestamp,
s.getLocal().alice, false); s.getLocal().alice, false);