Derive handshake root key when adding a pending contact.

This commit is contained in:
akwizgran
2019-05-30 15:48:26 +01:00
parent 9b4f60088f
commit 810d45d6b9
6 changed files with 79 additions and 19 deletions

View File

@@ -11,6 +11,12 @@ import java.security.GeneralSecurityException;
*/ */
public interface TransportCrypto { public interface TransportCrypto {
/**
* Returns true if the local peer is Alice.
*/
boolean isAlice(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair);
/** /**
* Derives the static master key shared with a contact or pending contact. * 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. * Derives the handshake mode root key from the static master key.
*
* @param pendingContact Whether the static master key is shared with a * @param pendingContact Whether the static master key is shared with a
* pending contact or a contact * pending contact or a contact
*/ */

View File

@@ -1,12 +1,15 @@
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.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import java.security.GeneralSecurityException;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -53,12 +56,10 @@ public interface KeyManager {
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the pending contact can be * {@link StreamContext StreamContexts} for the pending contact can be
* created after this method has returned. * created after this method has returned.
*
* @param alice True if the local party is Alice
*/ */
Map<TransportId, KeySetId> addPendingContact(Transaction txn, Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice) PendingContact p, KeyPair ourKeyPair)
throws DbException; throws DbException, GeneralSecurityException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -21,6 +22,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import java.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -51,6 +53,7 @@ class ContactManagerImpl implements ContactManager {
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PendingContactFactory pendingContactFactory; private final PendingContactFactory pendingContactFactory;
private final List<ContactHook> hooks; private final List<ContactHook> hooks;
@Inject @Inject
@@ -123,7 +126,15 @@ class ContactManagerImpl implements ContactManager {
throws DbException, FormatException { throws DbException, FormatException {
PendingContact p = PendingContact p =
pendingContactFactory.createPendingContact(link, alias); 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; return p;
} }

View File

@@ -48,6 +48,14 @@ class TransportCryptoImpl implements TransportCrypto {
this.crypto = crypto; this.crypto = crypto;
} }
@Override
public boolean isAlice(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair) {
byte[] theirPublic = theirHandshakePublicKey.getEncoded();
byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded();
return compare(ourPublic, theirPublic) < 0;
}
@Override @Override
public SecretKey deriveStaticMasterKey(PublicKey theirHandshakePublicKey, public SecretKey deriveStaticMasterKey(PublicKey theirHandshakePublicKey,
KeyPair ourHandshakeKeyPair) throws GeneralSecurityException { KeyPair ourHandshakeKeyPair) throws GeneralSecurityException {
@@ -55,8 +63,8 @@ class TransportCryptoImpl implements TransportCrypto {
byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded(); byte[] ourPublic = ourHandshakeKeyPair.getPublic().getEncoded();
boolean alice = compare(ourPublic, theirPublic) < 0; boolean alice = compare(ourPublic, theirPublic) < 0;
byte[][] inputs = { byte[][] inputs = {
alice ? ourPublic : theirPublic, alice ? ourPublic : theirPublic,
alice ? theirPublic : ourPublic alice ? theirPublic : ourPublic
}; };
return crypto.deriveSharedSecret(STATIC_MASTER_KEY_LABEL, return crypto.deriveSharedSecret(STATIC_MASTER_KEY_LABEL,
theirHandshakePublicKey, ourHandshakeKeyPair, inputs); theirHandshakePublicKey, ourHandshakeKeyPair, inputs);

View File

@@ -1,9 +1,12 @@
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.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -21,6 +24,7 @@ import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import java.security.GeneralSecurityException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -46,17 +50,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory; private final TransportKeyManagerFactory transportKeyManagerFactory;
private final TransportCrypto transportCrypto;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
KeyManagerImpl(DatabaseComponent db, @DatabaseExecutor Executor dbExecutor, KeyManagerImpl(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor,
PluginConfig pluginConfig, PluginConfig pluginConfig,
TransportKeyManagerFactory transportKeyManagerFactory) { TransportKeyManagerFactory transportKeyManagerFactory,
TransportCrypto transportCrypto) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory; this.transportKeyManagerFactory = transportKeyManagerFactory;
this.transportCrypto = transportCrypto;
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<>();
} }
@@ -118,13 +127,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override @Override
public Map<TransportId, KeySetId> addPendingContact(Transaction txn, public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice) PendingContact p, KeyPair ourKeyPair)
throws DbException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(p.getPublicKey(), ourKeyPair);
SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(p.getPublicKey(), ourKeyPair);
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addPendingContact(txn, p, rootKey, alice)); ids.put(t, m.addPendingContact(txn, p.getId(), rootKey, alice));
} }
return ids; return ids;
} }

View File

@@ -1,9 +1,12 @@
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.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -25,9 +28,11 @@ import java.util.Random;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.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;
@@ -41,12 +46,14 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
context.mock(TransportKeyManagerFactory.class); context.mock(TransportKeyManagerFactory.class);
private final TransportKeyManager transportKeyManager = private final TransportKeyManager transportKeyManager =
context.mock(TransportKeyManager.class); context.mock(TransportKeyManager.class);
private final TransportCrypto transportCrypto =
context.mock(TransportCrypto.class);
private final DeterministicExecutor executor = new DeterministicExecutor(); private final DeterministicExecutor executor = new DeterministicExecutor();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final PendingContactId pendingContactId = private final PendingContact pendingContact = getPendingContact();
new PendingContactId(getRandomId()); private final PendingContactId pendingContactId = pendingContact.getId();
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();
@@ -60,7 +67,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
private final Random random = new Random(); private final Random random = new Random();
private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor, private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
pluginConfig, transportKeyManagerFactory); pluginConfig, transportKeyManagerFactory, transportCrypto);
@Before @Before
public void testStartService() throws Exception { public void testStartService() throws Exception {
@@ -126,17 +133,29 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testAddPendingContact() throws Exception { public void testAddPendingContact() throws Exception {
SecretKey secretKey = getSecretKey(); 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(
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, oneOf(transportKeyManager).addPendingContact(txn, pendingContactId,
secretKey, alice); rootKey, alice);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn, Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
pendingContactId, secretKey, alice); pendingContact, ourKeyPair);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }