Derive handshake root key when converting pending contact.

This commit is contained in:
akwizgran
2019-05-30 17:21:46 +01:00
parent 4a2936c685
commit 430b530ca5
11 changed files with 125 additions and 122 deletions

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;
@@ -44,10 +45,13 @@ public interface ContactManager {
* for each transport, and returns an ID for the contact. * for each transport, and returns an ID for the contact.
* *
* @param alice True if the local party is Alice * @param alice True if the local party is Alice
* @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
@@ -83,9 +87,11 @@ public interface ContactManager {
* @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.

View File

@@ -1,9 +1,9 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
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.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;
@@ -21,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.
@@ -31,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;
@@ -42,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.
@@ -58,7 +57,7 @@ public interface KeyManager {
* created after this method has returned. * created after this method has returned.
*/ */
Map<TransportId, KeySetId> addPendingContact(Transaction txn, Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContact p, KeyPair ourKeyPair) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException; throws DbException, GeneralSecurityException;
/** /**

View File

@@ -78,8 +78,7 @@ class ContactManagerImpl implements ContactManager {
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, null, 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;
@@ -89,12 +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 {
PublicKey handshake = db.getPendingContact(txn, p).getPublicKey(); PublicKey theirPublicKey = db.getPendingContact(txn, p).getPublicKey();
db.removePendingContact(txn, p); db.removePendingContact(txn, p);
ContactId c = db.addContact(txn, remote, local, handshake, verified); ContactId c =
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp, db.addContact(txn, remote, local, theirPublicKey, verified);
alice, active); 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;
@@ -126,18 +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 -> { Transaction txn = db.startTransaction(false);
try {
db.addPendingContact(txn, p); db.addPendingContact(txn, p);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn); KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
try { keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
keyManager.addPendingContact(txn, p, ourKeyPair); ourKeyPair);
} catch (GeneralSecurityException e) { db.commitTransaction(txn);
throw new AssertionError(); } finally {
} db.endTransaction(txn);
}); }
return p; return p;
} }

View File

@@ -1,11 +1,11 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
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.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.crypto.KeyPair; 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.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -101,46 +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, 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.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,
PendingContact p, KeyPair ourKeyPair) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(p.getPublicKey(), ourKeyPair); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey = SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true); transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(p.getPublicKey(), ourKeyPair); 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.getId(), rootKey, alice)); ids.put(t, m.addHandshakeKeys(txn, p, rootKey, alice));
} }
return ids; return ids;
} }

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

@@ -73,8 +73,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, null, 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

@@ -1,10 +1,10 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
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.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.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -31,8 +31,8 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey; import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey; 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.getPendingContact;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -52,8 +52,8 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
private final DeterministicExecutor executor = new DeterministicExecutor(); private final DeterministicExecutor executor = new DeterministicExecutor();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final PendingContact pendingContact = getPendingContact(); private final PendingContactId pendingContactId =
private final PendingContactId pendingContactId = pendingContact.getId(); new PendingContactId(getRandomId());
private final KeySetId keySetId = new KeySetId(345); private final KeySetId keySetId = new KeySetId(345);
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final TransportId unknownTransportId = getTransportId(); private final TransportId unknownTransportId = getTransportId();
@@ -64,6 +64,11 @@ 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,
@@ -105,57 +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 {
KeyPair ourKeyPair =
new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey());
SecretKey staticMasterKey = getSecretKey();
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveStaticMasterKey( oneOf(transportCrypto)
pendingContact.getPublicKey(), ourKeyPair); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
will(returnValue(staticMasterKey)); will(returnValue(staticMasterKey));
oneOf(transportCrypto) oneOf(transportCrypto)
.deriveHandshakeRootKey(staticMasterKey, true); .deriveHandshakeRootKey(staticMasterKey, true);
will(returnValue(rootKey)); will(returnValue(rootKey));
oneOf(transportCrypto).isAlice(pendingContact.getPublicKey(), oneOf(transportCrypto).isAlice(theirPublicKey, ourKeyPair);
ourKeyPair);
will(returnValue(alice)); will(returnValue(alice));
oneOf(transportKeyManager).addPendingContact(txn, pendingContactId, oneOf(transportKeyManager).addHandshakeKeys(txn, pendingContactId,
rootKey, alice); rootKey, alice);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn, Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
pendingContact, ourKeyPair); 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);