mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
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:
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
@@ -16,8 +15,6 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
@NotNullByDefault
|
||||
public class Bytes implements Comparable<Bytes> {
|
||||
|
||||
public static final BytesComparator COMPARATOR = new BytesComparator();
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
private int hashCode = -1;
|
||||
@@ -45,14 +42,7 @@ public class Bytes implements Comparable<Bytes> {
|
||||
|
||||
@Override
|
||||
public int compareTo(Bytes other) {
|
||||
byte[] aBytes = bytes, bBytes = 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;
|
||||
return compare(bytes, other.bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,11 +51,13 @@ public class Bytes implements Comparable<Bytes> {
|
||||
"(" + StringUtils.toHexString(getBytes()) + ")";
|
||||
}
|
||||
|
||||
public static class BytesComparator implements Comparator<Bytes> {
|
||||
|
||||
@Override
|
||||
public int compare(Bytes a, Bytes b) {
|
||||
return a.compareTo(b);
|
||||
public static int compare(byte[] a, byte[] b) {
|
||||
int length = Math.min(a.length, b.length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
int aUnsigned = a[i] & 0xFF, bUnsigned = b[i] & 0xFF;
|
||||
if (aUnsigned < bUnsigned) return -1;
|
||||
if (aUnsigned > bUnsigned) return 1;
|
||||
}
|
||||
return a.length - b.length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -29,10 +30,18 @@ public interface ContactManager {
|
||||
|
||||
/**
|
||||
* Stores a contact associated with the given local and remote pseudonyms,
|
||||
* derives and stores transport keys for each transport, and returns an ID
|
||||
* for the contact.
|
||||
* derives and stores 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 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,
|
||||
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,
|
||||
* replacing the given pending contact, derives and stores transport keys
|
||||
* for each transport, and returns an ID for the contact.
|
||||
* replacing the given pending contact, derives and stores handshake mode
|
||||
* 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 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,
|
||||
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
|
||||
* 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,
|
||||
boolean verified) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a contact associated with the given local and remote pseudonyms,
|
||||
* derives and stores transport keys for each transport, and returns an ID
|
||||
* for the contact.
|
||||
* derives and stores 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 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,
|
||||
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
|
||||
* alias, adds it to the database and returns it.
|
||||
*
|
||||
* @param link The handshake link received from the contact we want to add
|
||||
* @param alias The alias the user has given this contact
|
||||
* @return A PendingContact representing the contact to be added
|
||||
* @param link The handshake link received from the pending contact
|
||||
* @param alias The alias the user has given this pending contact
|
||||
* @throws UnsupportedVersionException If the link uses a format version
|
||||
* that is not supported
|
||||
* @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)
|
||||
throws DbException, FormatException;
|
||||
throws DbException, FormatException, GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Returns the pending contact with the given ID.
|
||||
@@ -116,8 +150,8 @@ public interface ContactManager {
|
||||
Contact getContact(Transaction txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the contact with the given remoteAuthorId
|
||||
* that was added by the LocalAuthor with the given localAuthorId
|
||||
* Returns the contact with the given {@code remoteAuthorId}
|
||||
* that belongs to the local pseudonym with the given {@code localAuthorId}.
|
||||
*
|
||||
* @throws NoSuchContactException If the contact is not in the database
|
||||
*/
|
||||
@@ -125,8 +159,8 @@ public interface ContactManager {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the contact with the given remoteAuthorId
|
||||
* that was added by the LocalAuthor with the given localAuthorId
|
||||
* Returns the contact with the given {@code remoteAuthorId}
|
||||
* that belongs to the local pseudonym with the given {@code localAuthorId}.
|
||||
*
|
||||
* @throws NoSuchContactException If the contact is not in the database
|
||||
*/
|
||||
@@ -134,7 +168,7 @@ public interface ContactManager {
|
||||
AuthorId localAuthorId) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all active contacts.
|
||||
* Returns all contacts.
|
||||
*/
|
||||
Collection<Contact> getContacts() throws DbException;
|
||||
|
||||
@@ -149,25 +183,27 @@ public interface ContactManager {
|
||||
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)
|
||||
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)
|
||||
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,
|
||||
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)
|
||||
throws DbException;
|
||||
|
||||
@@ -3,12 +3,37 @@ package org.briarproject.bramble.api.crypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Crypto operations for the transport security protocol - see
|
||||
* https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md
|
||||
*/
|
||||
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
|
||||
* the given time period from the given root key.
|
||||
|
||||
@@ -61,14 +61,7 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
* and returns an ID for the contact.
|
||||
*/
|
||||
ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||
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;
|
||||
@Nullable PublicKey handshake, boolean verified) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a group.
|
||||
|
||||
@@ -2,11 +2,14 @@ package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -18,9 +21,9 @@ import javax.annotation.Nullable;
|
||||
public interface KeyManager {
|
||||
|
||||
/**
|
||||
* Informs the key manager that a new contact has been added. Derives and
|
||||
* stores a set of rotation mode transport keys for communicating with the
|
||||
* contact over each transport and returns the key set IDs.
|
||||
* Derives and stores a set of rotation mode transport keys for
|
||||
* communicating with the given contact over each transport and returns the
|
||||
* key set IDs.
|
||||
* <p/>
|
||||
* {@link StreamContext StreamContexts} for the contact can be created
|
||||
* after this method has returned.
|
||||
@@ -28,7 +31,7 @@ public interface KeyManager {
|
||||
* @param alice True if the local party is Alice
|
||||
* @param active Whether the derived keys can be used for outgoing streams
|
||||
*/
|
||||
Map<TransportId, KeySetId> addContactWithRotationKeys(Transaction txn,
|
||||
Map<TransportId, KeySetId> addRotationKeys(Transaction txn,
|
||||
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
||||
boolean active) throws DbException;
|
||||
|
||||
@@ -39,11 +42,10 @@ public interface KeyManager {
|
||||
* <p/>
|
||||
* {@link StreamContext StreamContexts} for the contact can be created
|
||||
* after this method has returned.
|
||||
*
|
||||
* @param alice True if the local party is Alice
|
||||
*/
|
||||
Map<TransportId, KeySetId> addContactWithHandshakeKeys(Transaction txn,
|
||||
ContactId c, SecretKey rootKey, boolean alice) throws DbException;
|
||||
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||
PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
throws DbException, GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Informs the key manager that a new pending contact has been added.
|
||||
@@ -53,12 +55,10 @@ public interface KeyManager {
|
||||
* <p/>
|
||||
* {@link StreamContext StreamContexts} for the pending contact can be
|
||||
* created after this method has returned.
|
||||
*
|
||||
* @param alice True if the local party is Alice
|
||||
*/
|
||||
Map<TransportId, KeySetId> addPendingContact(Transaction txn,
|
||||
PendingContactId p, SecretKey rootKey, boolean alice)
|
||||
throws DbException;
|
||||
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
throws DbException, GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
|
||||
@@ -63,14 +63,6 @@ public interface TransportConstants {
|
||||
int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - FRAME_HEADER_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.
|
||||
*/
|
||||
@@ -81,6 +73,26 @@ public interface TransportConstants {
|
||||
*/
|
||||
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
|
||||
* rotation mode.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.client;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||
@@ -55,7 +54,7 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
||||
|
||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||
try {
|
||||
if (Bytes.COMPARATOR.compare(local, remote) < 0)
|
||||
if (local.compareTo(remote) < 0)
|
||||
return clientHelper.toByteArray(BdfList.of(local, remote));
|
||||
else return clientHelper.toByteArray(BdfList.of(remote, local));
|
||||
} catch (FormatException e) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
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.transport.KeyManager;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -51,6 +54,7 @@ class ContactManagerImpl implements ContactManager {
|
||||
private final KeyManager keyManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final PendingContactFactory pendingContactFactory;
|
||||
|
||||
private final List<ContactHook> hooks;
|
||||
|
||||
@Inject
|
||||
@@ -73,9 +77,8 @@ class ContactManagerImpl implements ContactManager {
|
||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
|
||||
boolean active) throws DbException {
|
||||
ContactId c = db.addContact(txn, remote, local, verified);
|
||||
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||
alice, active);
|
||||
ContactId c = db.addContact(txn, remote, local, null, verified);
|
||||
keyManager.addRotationKeys(txn, c, rootKey, timestamp, alice, active);
|
||||
Contact contact = db.getContact(txn, c);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
@@ -85,10 +88,14 @@ class ContactManagerImpl implements ContactManager {
|
||||
public ContactId addContact(Transaction txn, PendingContactId p,
|
||||
Author remote, AuthorId local, SecretKey rootKey, long timestamp,
|
||||
boolean alice, boolean verified, boolean active)
|
||||
throws DbException {
|
||||
ContactId c = db.addContact(txn, p, remote, local, verified);
|
||||
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||
alice, active);
|
||||
throws DbException, GeneralSecurityException {
|
||||
PublicKey theirPublicKey = db.getPendingContact(txn, p).getPublicKey();
|
||||
db.removePendingContact(txn, p);
|
||||
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);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
@@ -97,7 +104,7 @@ class ContactManagerImpl implements ContactManager {
|
||||
@Override
|
||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||
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);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
@@ -120,10 +127,19 @@ class ContactManagerImpl implements ContactManager {
|
||||
|
||||
@Override
|
||||
public PendingContact addPendingContact(String link, String alias)
|
||||
throws DbException, FormatException {
|
||||
throws DbException, FormatException, GeneralSecurityException {
|
||||
PendingContact p =
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
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.TransportCrypto;
|
||||
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.digests.Blake2bDigest;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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_TAG_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_HEADER_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.STATIC_MASTER_KEY_LABEL;
|
||||
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_64_BYTES;
|
||||
@@ -40,6 +48,36 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
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
|
||||
public TransportKeys deriveRotationKeys(TransportId t,
|
||||
SecretKey rootKey, long timePeriod, boolean weAreAlice,
|
||||
|
||||
@@ -88,8 +88,8 @@ interface Database<T> {
|
||||
* Stores a contact associated with the given local and remote pseudonyms,
|
||||
* and returns an ID for the contact.
|
||||
*/
|
||||
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified)
|
||||
throws DbException;
|
||||
ContactId addContact(T txn, Author remote, AuthorId local,
|
||||
@Nullable PublicKey handshake, boolean verified) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a group.
|
||||
@@ -695,14 +695,6 @@ interface Database<T> {
|
||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||
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
|
||||
* of the given message with respect to the given contact, using the latency
|
||||
|
||||
@@ -85,6 +85,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.SHARED;
|
||||
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 {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DatabaseComponentImpl.class.getName());
|
||||
getLogger(DatabaseComponentImpl.class.getName());
|
||||
|
||||
private final Database<T> db;
|
||||
private final Class<T> txnClass;
|
||||
@@ -234,39 +235,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public ContactId addContact(Transaction transaction, Author remote,
|
||||
AuthorId local, boolean verified) throws DbException {
|
||||
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)
|
||||
AuthorId local, @Nullable PublicKey handshake, boolean verified)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsPendingContact(txn, p))
|
||||
throw new NoSuchPendingContactException();
|
||||
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);
|
||||
db.transferKeys(txn, p, c);
|
||||
db.removePendingContact(txn, p);
|
||||
ContactId c = db.addContact(txn, remote, local, handshake, verified);
|
||||
transaction.attach(new ContactAddedEvent(c, verified));
|
||||
transaction.attach(new PendingContactRemovedEvent(p));
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@@ -629,22 +629,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
@Override
|
||||
public ContactId addContact(Connection txn, Author remote, AuthorId local,
|
||||
boolean verified) throws DbException {
|
||||
@Nullable PublicKey handshake, boolean verified)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Create a contact row
|
||||
String sql = "INSERT INTO contacts"
|
||||
+ " (authorId, formatVersion, name, publicKey,"
|
||||
+ " localAuthorId, verified)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
+ " localAuthorId, handshakePublicKey, verified)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, remote.getId().getBytes());
|
||||
ps.setInt(2, remote.getFormatVersion());
|
||||
ps.setString(3, remote.getName());
|
||||
ps.setBytes(4, remote.getPublicKey().getEncoded());
|
||||
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();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
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
|
||||
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
||||
int maxLatency) throws DbException {
|
||||
|
||||
@@ -3,12 +3,17 @@ package org.briarproject.bramble.transport;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.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.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
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.lifecycle.Service;
|
||||
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.StreamContext;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -46,17 +52,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
private final Executor dbExecutor;
|
||||
private final PluginConfig pluginConfig;
|
||||
private final TransportKeyManagerFactory transportKeyManagerFactory;
|
||||
private final TransportCrypto transportCrypto;
|
||||
|
||||
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
@Inject
|
||||
KeyManagerImpl(DatabaseComponent db, @DatabaseExecutor Executor dbExecutor,
|
||||
KeyManagerImpl(DatabaseComponent db,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
PluginConfig pluginConfig,
|
||||
TransportKeyManagerFactory transportKeyManagerFactory) {
|
||||
TransportKeyManagerFactory transportKeyManagerFactory,
|
||||
TransportCrypto transportCrypto) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.pluginConfig = pluginConfig;
|
||||
this.transportKeyManagerFactory = transportKeyManagerFactory;
|
||||
this.transportCrypto = transportCrypto;
|
||||
managers = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@@ -90,41 +101,51 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addContactWithRotationKeys(
|
||||
public Map<TransportId, KeySetId> addRotationKeys(
|
||||
Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
|
||||
boolean alice, boolean active) throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||
ids.put(t, m.addRotationKeys(txn, c, rootKey, timestamp,
|
||||
alice, active));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addContactWithHandshakeKeys(
|
||||
Transaction txn, ContactId c, SecretKey rootKey, boolean alice)
|
||||
throws DbException {
|
||||
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||
PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
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<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addContactWithHandshakeKeys(txn, c, rootKey, alice));
|
||||
ids.put(t, m.addHandshakeKeys(txn, c, rootKey, alice));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
|
||||
PendingContactId p, SecretKey rootKey, boolean alice)
|
||||
throws DbException {
|
||||
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
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<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addPendingContact(txn, p, rootKey, alice));
|
||||
ids.put(t, m.addHandshakeKeys(txn, p, rootKey, alice));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
@@ -180,15 +201,27 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactRemovedEvent) {
|
||||
removeContact(((ContactRemovedEvent) e).getContactId());
|
||||
} else if (e instanceof PendingContactRemovedEvent) {
|
||||
PendingContactRemovedEvent p = (PendingContactRemovedEvent) e;
|
||||
removePendingContact(p.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void removeContact(ContactId c) {
|
||||
dbExecutor.execute(() -> {
|
||||
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
|
||||
private <T> T withManager(TransportId t, ManagerTask<T> task)
|
||||
throws DbException {
|
||||
|
||||
@@ -16,14 +16,14 @@ interface TransportKeyManager {
|
||||
|
||||
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)
|
||||
throws DbException;
|
||||
|
||||
KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
|
||||
KeySetId addHandshakeKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, boolean alice) throws DbException;
|
||||
|
||||
KeySetId addPendingContact(Transaction txn, PendingContactId p,
|
||||
KeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
|
||||
SecretKey rootKey, boolean alice) throws DbException;
|
||||
|
||||
void activateKeys(Transaction txn, KeySetId k) throws DbException;
|
||||
|
||||
@@ -115,11 +115,14 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
TransportKeys k = ks.getKeys();
|
||||
TransportKeys k1 = transportCrypto.updateTransportKeys(k,
|
||||
timePeriod);
|
||||
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
||||
ks.getContactId(), null, k1);
|
||||
if (k1.getTimePeriod() > k.getTimePeriod())
|
||||
if (k1.getTimePeriod() > k.getTimePeriod()) {
|
||||
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
||||
ks.getContactId(), ks.getPendingContactId(), k1);
|
||||
updateResult.updated.add(ks1);
|
||||
updateResult.current.add(ks1);
|
||||
updateResult.current.add(ks1);
|
||||
} else {
|
||||
updateResult.current.add(ks);
|
||||
}
|
||||
}
|
||||
return updateResult;
|
||||
}
|
||||
@@ -207,7 +210,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
|
||||
public KeySetId addRotationKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||
throws DbException {
|
||||
lock.lock();
|
||||
@@ -222,7 +225,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
k = transportCrypto.updateTransportKeys(k, timePeriod);
|
||||
// Write the keys back to the DB
|
||||
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));
|
||||
return keySetId;
|
||||
} finally {
|
||||
@@ -231,7 +234,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
|
||||
public KeySetId addHandshakeKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, boolean alice) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -242,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
rootKey, timePeriod, alice);
|
||||
// Write the keys back to the DB
|
||||
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));
|
||||
return keySetId;
|
||||
} finally {
|
||||
@@ -251,7 +254,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addPendingContact(Transaction txn, PendingContactId p,
|
||||
public KeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
|
||||
SecretKey rootKey, boolean alice) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -262,7 +265,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
rootKey, timePeriod, alice);
|
||||
// Write the keys back to the DB
|
||||
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));
|
||||
return keySetId;
|
||||
} finally {
|
||||
|
||||
@@ -71,10 +71,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
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));
|
||||
oneOf(keyManager).addContactWithRotationKeys(txn, contactId,
|
||||
rootKey, timestamp, alice, active);
|
||||
oneOf(keyManager).addRotationKeys(txn, contactId, rootKey,
|
||||
timestamp, alice, active);
|
||||
oneOf(db).getContact(txn, contactId);
|
||||
will(returnValue(contact));
|
||||
}});
|
||||
|
||||
@@ -176,7 +176,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
oneOf(database).containsContact(txn, author.getId(),
|
||||
localAuthor.getId());
|
||||
will(returnValue(false));
|
||||
oneOf(database).addContact(txn, author, localAuthor.getId(), true);
|
||||
oneOf(database).addContact(txn, author, localAuthor.getId(),
|
||||
null, true);
|
||||
will(returnValue(contactId));
|
||||
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
|
||||
// getContacts()
|
||||
@@ -224,7 +225,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
db.transaction(false, transaction -> {
|
||||
db.addIdentity(transaction, identity);
|
||||
assertEquals(contactId, db.addContact(transaction, author,
|
||||
localAuthor.getId(), true));
|
||||
localAuthor.getId(), null, true));
|
||||
assertEquals(singletonList(contact),
|
||||
db.getContacts(transaction));
|
||||
db.addGroup(transaction, group); // First time - listeners called
|
||||
@@ -445,7 +446,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.addContact(transaction, author, localAuthor.getId(),
|
||||
true));
|
||||
null, true));
|
||||
fail();
|
||||
} catch (NoSuchIdentityException expected) {
|
||||
// Expected
|
||||
@@ -763,25 +764,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// 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));
|
||||
exactly(4).of(database).containsPendingContact(txn,
|
||||
exactly(3).of(database).containsPendingContact(txn,
|
||||
pendingContactId);
|
||||
will(returnValue(false));
|
||||
exactly(4).of(database).abortTransaction(txn);
|
||||
exactly(3).of(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 (NoSuchPendingContactException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.addTransportKeys(transaction, pendingContactId,
|
||||
@@ -1473,7 +1465,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.addContact(transaction, author, localAuthor.getId(),
|
||||
true));
|
||||
null, true));
|
||||
fail();
|
||||
} catch (ContactExistsException expected) {
|
||||
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
|
||||
@@ -1503,70 +1495,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.addContact(transaction, author, localAuthor.getId(),
|
||||
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));
|
||||
null, true));
|
||||
fail();
|
||||
} catch (ContactExistsException expected) {
|
||||
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
|
||||
|
||||
@@ -548,7 +548,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
||||
db.addIdentity(txn, identity);
|
||||
for (int i = 0; i < CONTACTS; i++) {
|
||||
ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(),
|
||||
random.nextBoolean());
|
||||
null, random.nextBoolean());
|
||||
contacts.add(db.getContact(txn, c));
|
||||
contactGroups.put(c, new ArrayList<>());
|
||||
for (int j = 0; j < GROUPS_PER_CONTACT; j++) {
|
||||
|
||||
@@ -147,7 +147,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertFalse(db.containsContact(txn, contactId));
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
assertTrue(db.containsContact(txn, contactId));
|
||||
assertFalse(db.containsGroup(txn, groupId));
|
||||
db.addGroup(txn, group);
|
||||
@@ -210,7 +210,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact, a shared group and a shared message
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
db.addMessage(txn, message, DELIVERED, true, null);
|
||||
@@ -395,7 +395,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact and a visible group
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
db.addMessage(txn, message, DELIVERED, true, null);
|
||||
@@ -568,7 +568,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact and a shared group
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
|
||||
@@ -588,7 +588,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
|
||||
// The group is not in the database
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addMessage(txn, message, DELIVERED, true, null);
|
||||
|
||||
@@ -625,7 +625,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact and a group
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// 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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
|
||||
@@ -920,7 +920,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, transport and transport keys
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
|
||||
@@ -964,7 +964,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, transport and handshake keys
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
|
||||
@@ -1011,7 +1011,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, transport and transport keys
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
|
||||
@@ -1058,7 +1058,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, transport and handshake keys
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
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
|
||||
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
|
||||
Collection<Contact> contacts =
|
||||
@@ -1135,7 +1135,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
|
||||
// Add a contact associated with the local author
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
contacts = db.getContacts(txn, localAuthor.getId());
|
||||
assertEquals(singletonList(contactId), contacts);
|
||||
|
||||
@@ -1157,7 +1157,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact - initially there should be no offered messages
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
assertEquals(0, db.countOfferedMessages(txn, contactId));
|
||||
|
||||
// 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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
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
|
||||
ContactId contactId =
|
||||
db.addContact(txn, author, localAuthor.getId(), true);
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true);
|
||||
ContactId contactId1 =
|
||||
db.addContact(txn, author, localAuthor1.getId(), true);
|
||||
db.addContact(txn, author, localAuthor1.getId(), null, true);
|
||||
|
||||
// The contacts should be distinct
|
||||
assertNotEquals(contactId, contactId1);
|
||||
@@ -1875,7 +1875,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact, a shared group and a shared message
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
db.addMessage(txn, message, DELIVERED, true, null);
|
||||
@@ -1929,7 +1929,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
|
||||
// The contact should have no alias
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
@@ -1986,7 +1986,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add a contact, a group and a message
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
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
|
||||
db.addIdentity(txn, identity);
|
||||
assertEquals(contactId,
|
||||
db.addContact(txn, author, localAuthor.getId(), true));
|
||||
db.addContact(txn, author, localAuthor.getId(), null, true));
|
||||
db.addGroup(txn, group);
|
||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||
db.addMessage(txn, message, DELIVERED, true, null);
|
||||
@@ -2229,60 +2229,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSetHandshakeKeyPair() throws Exception {
|
||||
Identity withoutKeys = new Identity(localAuthor, null, null,
|
||||
|
||||
@@ -3,7 +3,10 @@ package org.briarproject.bramble.transport;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
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.Transaction;
|
||||
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.singletonMap;
|
||||
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.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
@@ -41,6 +46,8 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||
context.mock(TransportKeyManagerFactory.class);
|
||||
private final TransportKeyManager transportKeyManager =
|
||||
context.mock(TransportKeyManager.class);
|
||||
private final TransportCrypto transportCrypto =
|
||||
context.mock(TransportCrypto.class);
|
||||
|
||||
private final DeterministicExecutor executor = new DeterministicExecutor();
|
||||
private final Transaction txn = new Transaction(null, false);
|
||||
@@ -57,10 +64,15 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||
new StreamContext(null, pendingContactId, transportId,
|
||||
getSecretKey(), getSecretKey(), 1, true);
|
||||
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 KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
|
||||
pluginConfig, transportKeyManagerFactory);
|
||||
pluginConfig, transportKeyManagerFactory, transportCrypto);
|
||||
|
||||
@Before
|
||||
public void testStartService() throws Exception {
|
||||
@@ -98,45 +110,59 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||
boolean active = random.nextBoolean();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportKeyManager).addContactWithRotationKeys(txn,
|
||||
oneOf(transportKeyManager).addRotationKeys(txn,
|
||||
contactId, secretKey, timestamp, alice, active);
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
|
||||
Map<TransportId, KeySetId> ids = keyManager.addContactWithRotationKeys(
|
||||
Map<TransportId, KeySetId> ids = keyManager.addRotationKeys(
|
||||
txn, contactId, secretKey, timestamp, alice, active);
|
||||
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddContactWithHandshakeModeKeys() throws Exception {
|
||||
SecretKey secretKey = getSecretKey();
|
||||
public void testAddContactWithHandshakePublicKey() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportKeyManager).addContactWithHandshakeKeys(
|
||||
txn, contactId, secretKey, alice);
|
||||
oneOf(transportCrypto)
|
||||
.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));
|
||||
}});
|
||||
|
||||
Map<TransportId, KeySetId> ids = keyManager.addContactWithHandshakeKeys(
|
||||
txn, contactId, secretKey, alice);
|
||||
Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
|
||||
theirPublicKey, ourKeyPair);
|
||||
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPendingContact() throws Exception {
|
||||
SecretKey secretKey = getSecretKey();
|
||||
boolean alice = random.nextBoolean();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportKeyManager).addPendingContact(txn, pendingContactId,
|
||||
secretKey, alice);
|
||||
oneOf(transportCrypto)
|
||||
.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));
|
||||
}});
|
||||
|
||||
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
|
||||
pendingContactId, secretKey, alice);
|
||||
pendingContactId, theirPublicKey, ourKeyPair);
|
||||
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.hamcrest.Description;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.api.Action;
|
||||
import org.jmock.api.Invocation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -72,6 +73,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
private final SecretKey rootKey = getSecretKey();
|
||||
private final Random random = new Random();
|
||||
|
||||
private TransportKeyManager transportKeyManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
transportKeyManager = new TransportKeyManagerImpl(db, transportCrypto,
|
||||
dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreUpdatedAtStartup() throws Exception {
|
||||
boolean active = random.nextBoolean();
|
||||
@@ -112,16 +121,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
with(timePeriodLength - 1), with(MILLISECONDS));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
assertEquals(active,
|
||||
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotationKeysAreDerivedAndUpdatedWhenAddingContact()
|
||||
public void testRotationKeysForContactAreDerivedAndUpdatedWhenAdded()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
boolean active = random.nextBoolean();
|
||||
@@ -156,14 +162,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is 1 ms before the start of time period 1000
|
||||
long timestamp = timePeriodLength * 1000 - 1;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, active));
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(txn,
|
||||
contactId, rootKey, timestamp, alice, active));
|
||||
assertEquals(active,
|
||||
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeKeysAreDerivedWhenAddingContact()
|
||||
public void testHandshakeKeysForContactAreDerivedWhenAdded()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
|
||||
@@ -189,16 +195,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithHandshakeKeys(
|
||||
txn, contactId, rootKey, alice));
|
||||
assertEquals(keySetId, transportKeyManager.addHandshakeKeys(txn,
|
||||
contactId, rootKey, alice));
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeKeysAreDerivedWhenAddingPendingContact()
|
||||
public void testHandshakeKeysForPendingContactAreDerivedWhenAdded()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
|
||||
@@ -224,10 +227,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
assertEquals(keySetId, transportKeyManager.addPendingContact(txn,
|
||||
assertEquals(keySetId, transportKeyManager.addHandshakeKeys(txn,
|
||||
pendingContactId, rootKey, alice));
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(
|
||||
pendingContactId));
|
||||
@@ -269,12 +269,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, true));
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
@@ -295,12 +292,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, true));
|
||||
// The first request should return a stream context
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
@@ -327,12 +321,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, active));
|
||||
assertEquals(active,
|
||||
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
@@ -380,12 +371,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, true));
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
// Use the first tag (previous time period, stream number 0)
|
||||
@@ -461,9 +449,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
with(timePeriodLength), with(MILLISECONDS));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
@@ -483,12 +468,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, false));
|
||||
// The keys are inactive so no stream context should be returned
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
@@ -549,12 +531,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
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
|
||||
long timestamp = timePeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||
assertEquals(keySetId, transportKeyManager.addRotationKeys(
|
||||
txn, contactId, rootKey, timestamp, alice, false));
|
||||
// The keys are inactive so no stream context should be returned
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -102,14 +103,15 @@ public class AddContactViewModel extends AndroidViewModel {
|
||||
} catch (UnsupportedVersionException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
addContactResult.postValue(new LiveResult<>(e));
|
||||
} catch (DbException | FormatException e) {
|
||||
} catch (DbException | FormatException
|
||||
| GeneralSecurityException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
addContactResult.postValue(new LiveResult<>(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<LiveResult<Boolean>> getAddContactResult() {
|
||||
LiveData<LiveResult<Boolean>> getAddContactResult() {
|
||||
return addContactResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ class IntroduceeProtocolEngine
|
||||
|
||||
// add the keys to the new contact
|
||||
//noinspection ConstantConditions
|
||||
keys = keyManager.addContactWithRotationKeys(txn, c.getId(),
|
||||
keys = keyManager.addRotationKeys(txn, c.getId(),
|
||||
new SecretKey(s.getMasterKey()), timestamp,
|
||||
s.getLocal().alice, false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user