diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java index a58645792..7439c0ec8 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java @@ -11,6 +11,12 @@ import java.security.GeneralSecurityException; */ 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. */ @@ -19,6 +25,7 @@ public interface TransportCrypto { /** * Derives the handshake mode root key from the static master key. + * * @param pendingContact Whether the static master key is shared with a * pending contact or a contact */ diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java index 75ae88d86..898a45e5f 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java @@ -1,12 +1,15 @@ package org.briarproject.bramble.api.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; +import org.briarproject.bramble.api.crypto.KeyPair; 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; @@ -53,12 +56,10 @@ public interface KeyManager { *

* {@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 addPendingContact(Transaction txn, - PendingContactId p, SecretKey rootKey, boolean alice) - throws DbException; + PendingContact p, KeyPair ourKeyPair) + throws DbException, GeneralSecurityException; /** * Marks the given transport keys as usable for outgoing streams. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java index 7bea2930e..30c3b3c42 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java @@ -8,6 +8,7 @@ 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.SecretKey; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; @@ -21,6 +22,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 +53,7 @@ class ContactManagerImpl implements ContactManager { private final KeyManager keyManager; private final IdentityManager identityManager; private final PendingContactFactory pendingContactFactory; + private final List hooks; @Inject @@ -123,7 +126,15 @@ class ContactManagerImpl implements ContactManager { throws DbException, FormatException { PendingContact p = pendingContactFactory.createPendingContact(link, alias); - db.transaction(false, txn -> db.addPendingContact(txn, p)); + db.transaction(false, txn -> { + db.addPendingContact(txn, p); + KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn); + try { + keyManager.addPendingContact(txn, p, ourKeyPair); + } catch (GeneralSecurityException e) { + throw new AssertionError(); + } + }); return p; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java index aa8d4d665..2bf4c7b21 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java @@ -48,6 +48,14 @@ 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 { @@ -55,8 +63,8 @@ class TransportCryptoImpl implements TransportCrypto { byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded(); boolean alice = compare(ourPublic, theirPublic) < 0; byte[][] inputs = { - alice ? ourPublic : theirPublic, - alice ? theirPublic : ourPublic + alice ? ourPublic : theirPublic, + alice ? theirPublic : ourPublic }; return crypto.deriveSharedSecret(STATIC_MASTER_KEY_LABEL, theirHandshakePublicKey, ourHandshakeKeyPair, inputs); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java index a508c2e83..a979e10b5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java @@ -1,9 +1,12 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; +import org.briarproject.bramble.api.crypto.KeyPair; 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; @@ -21,6 +24,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 +50,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 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<>(); } @@ -118,13 +127,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { @Override public Map addPendingContact(Transaction txn, - PendingContactId p, SecretKey rootKey, boolean alice) - throws DbException { + PendingContact p, KeyPair ourKeyPair) + throws DbException, GeneralSecurityException { + SecretKey staticMasterKey = transportCrypto + .deriveStaticMasterKey(p.getPublicKey(), ourKeyPair); + SecretKey rootKey = + transportCrypto.deriveHandshakeRootKey(staticMasterKey, true); + boolean alice = transportCrypto.isAlice(p.getPublicKey(), ourKeyPair); Map ids = new HashMap<>(); for (Entry e : managers.entrySet()) { TransportId t = e.getKey(); TransportKeyManager m = e.getValue(); - ids.put(t, m.addPendingContact(txn, p, rootKey, alice)); + ids.put(t, m.addPendingContact(txn, p.getId(), rootKey, alice)); } return ids; } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java index 0220c358b..95c03a3fd 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/KeyManagerImplTest.java @@ -1,9 +1,12 @@ package org.briarproject.bramble.transport; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; +import org.briarproject.bramble.api.crypto.KeyPair; 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,9 +28,11 @@ 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.getPendingContact; 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.getTransportId; import static org.junit.Assert.assertEquals; @@ -41,12 +46,14 @@ 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); private final ContactId contactId = getContactId(); - private final PendingContactId pendingContactId = - new PendingContactId(getRandomId()); + private final PendingContact pendingContact = getPendingContact(); + private final PendingContactId pendingContactId = pendingContact.getId(); private final KeySetId keySetId = new KeySetId(345); private final TransportId transportId = getTransportId(); private final TransportId unknownTransportId = getTransportId(); @@ -60,7 +67,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase { 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 { @@ -126,17 +133,29 @@ public class KeyManagerImplTest extends BrambleMockTestCase { @Test public void testAddPendingContact() throws Exception { - SecretKey secretKey = getSecretKey(); + KeyPair ourKeyPair = + new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey()); + SecretKey staticMasterKey = getSecretKey(); + SecretKey rootKey = getSecretKey(); boolean alice = random.nextBoolean(); context.checking(new Expectations() {{ + oneOf(transportCrypto).deriveStaticMasterKey( + pendingContact.getPublicKey(), ourKeyPair); + will(returnValue(staticMasterKey)); + oneOf(transportCrypto) + .deriveHandshakeRootKey(staticMasterKey, true); + will(returnValue(rootKey)); + oneOf(transportCrypto).isAlice(pendingContact.getPublicKey(), + ourKeyPair); + will(returnValue(alice)); oneOf(transportKeyManager).addPendingContact(txn, pendingContactId, - secretKey, alice); + rootKey, alice); will(returnValue(keySetId)); }}); Map ids = keyManager.addPendingContact(txn, - pendingContactId, secretKey, alice); + pendingContact, ourKeyPair); assertEquals(singletonMap(transportId, keySetId), ids); }