From 8b2b7599f9d41345388d39e90fe00bd1cd55b1ba Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 22 Apr 2019 17:55:20 +0100 Subject: [PATCH] Generate and store handshake keys at startup if needed. --- .../briarproject/bramble/test/TestUtils.java | 25 ++++-- .../bramble/identity/IdentityManagerImpl.java | 52 +++++++++---- .../identity/IdentityManagerImplTest.java | 78 +++++++++++++++---- 3 files changed, 117 insertions(+), 38 deletions(-) diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java index c14cfdb72..15e0d06b6 100644 --- a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java +++ b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java @@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static java.util.Arrays.asList; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; +import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; @@ -100,24 +101,32 @@ public class TestUtils { } public static LocalAuthor getLocalAuthor() { - return getLocalAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH)); + return getLocalAuthor(false); } - public static LocalAuthor getLocalAuthor(int nameLength) { + public static LocalAuthor getLocalAuthor(boolean withHandshakeKeys) { AuthorId id = new AuthorId(getRandomId()); + int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH); String name = getRandomString(nameLength); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); - return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey, - timestamp); + if (withHandshakeKeys) { + byte[] handshakePublicKey = + getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES); + byte[] handshakePrivateKey = + getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES); + return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, + privateKey, handshakePublicKey, handshakePrivateKey, + timestamp); + } else { + return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, + privateKey, timestamp); + } } public static Author getAuthor() { - return getAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH)); - } - - public static Author getAuthor(int nameLength) { AuthorId id = new AuthorId(getRandomId()); + int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH); String name = getRandomString(nameLength); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); return new Author(id, FORMAT_VERSION, name, publicKey); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java index 4207ecb53..7fec3d53d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java @@ -1,5 +1,7 @@ package org.briarproject.bramble.identity; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; @@ -9,6 +11,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import java.util.Collection; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -27,6 +30,7 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook { getLogger(IdentityManagerImpl.class.getName()); private final DatabaseComponent db; + private final CryptoComponent crypto; private final AuthorFactory authorFactory; // The local author is immutable so we can cache it @@ -34,8 +38,10 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook { private volatile LocalAuthor cachedAuthor; @Inject - IdentityManagerImpl(DatabaseComponent db, AuthorFactory authorFactory) { + IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto, + AuthorFactory authorFactory) { this.db = db; + this.crypto = crypto; this.authorFactory = authorFactory; } @@ -57,38 +63,56 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook { public void onDatabaseOpened(Transaction txn) throws DbException { LocalAuthor cached = cachedAuthor; if (cached == null) { - LOG.info("No local author to store"); - return; + LocalAuthor loaded = loadLocalAuthor(txn); + if (loaded.getHandshakePublicKey() == null) { + KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair(); + byte[] handshakePublicKey = + handshakeKeyPair.getPublic().getEncoded(); + byte[] handshakePrivateKey = + handshakeKeyPair.getPrivate().getEncoded(); + db.setHandshakeKeyPair(txn, loaded.getId(), + handshakePublicKey, handshakePrivateKey); + cachedAuthor = new LocalAuthor(loaded.getId(), + loaded.getFormatVersion(), loaded.getName(), + loaded.getPublicKey(), loaded.getPrivateKey(), + handshakePublicKey, handshakePrivateKey, + loaded.getTimeCreated()); + LOG.info("Handshake key pair added"); + } else { + cachedAuthor = loaded; + LOG.info("Local author loaded"); + } + } else { + db.addLocalAuthor(txn, cached); + LOG.info("Local author stored"); } - db.addLocalAuthor(txn, cached); - LOG.info("Local author stored"); } @Override public LocalAuthor getLocalAuthor() throws DbException { - if (cachedAuthor == null) { - cachedAuthor = + LocalAuthor cached = cachedAuthor; + if (cached == null) { + cachedAuthor = cached = db.transactionWithResult(true, this::loadLocalAuthor); LOG.info("Local author loaded"); } - LocalAuthor cached = cachedAuthor; - if (cached == null) throw new AssertionError(); return cached; } @Override public LocalAuthor getLocalAuthor(Transaction txn) throws DbException { - if (cachedAuthor == null) { - cachedAuthor = loadLocalAuthor(txn); + LocalAuthor cached = cachedAuthor; + if (cached == null) { + cachedAuthor = cached = loadLocalAuthor(txn); LOG.info("Local author loaded"); } - LocalAuthor cached = cachedAuthor; - if (cached == null) throw new AssertionError(); return cached; } private LocalAuthor loadLocalAuthor(Transaction txn) throws DbException { - return db.getLocalAuthors(txn).iterator().next(); + Collection authors = db.getLocalAuthors(txn); + if (authors.size() != 1) throw new IllegalStateException(); + return authors.iterator().next(); } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java index 7d8e22059..f1b8e000c 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java @@ -1,5 +1,9 @@ package org.briarproject.bramble.identity; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.PrivateKey; +import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; @@ -7,42 +11,48 @@ import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.DbExpectations; +import org.jmock.Expectations; import org.junit.Before; import org.junit.Test; -import java.util.Collection; - import static java.util.Collections.singletonList; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; public class IdentityManagerImplTest extends BrambleMockTestCase { private final DatabaseComponent db = context.mock(DatabaseComponent.class); + private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final AuthorFactory authorFactory = context.mock(AuthorFactory.class); + private final PublicKey handshakePublicKey = context.mock(PublicKey.class); + private final PrivateKey handshakePrivateKey = + context.mock(PrivateKey.class); private final Transaction txn = new Transaction(null, false); - private final LocalAuthor localAuthor = getLocalAuthor(); - private final Collection localAuthors = - singletonList(localAuthor); + private final LocalAuthor localAuthor = getLocalAuthor(true); + private final LocalAuthor localAuthorWithoutHandshakeKeys = + new LocalAuthor(localAuthor.getId(), localAuthor.getFormatVersion(), + localAuthor.getName(), localAuthor.getPublicKey(), + localAuthor.getPrivateKey(), localAuthor.getTimeCreated()); + private final KeyPair handshakeKeyPair = + new KeyPair(handshakePublicKey, handshakePrivateKey); + private final byte[] handshakePublicKeyBytes = + localAuthor.getHandshakePublicKey(); + private final byte[] handshakePrivateKeyBytes = + localAuthor.getHandshakePrivateKey(); + private IdentityManagerImpl identityManager; @Before public void setUp() { - identityManager = new IdentityManagerImpl(db, authorFactory); + identityManager = new IdentityManagerImpl(db, crypto, authorFactory); } @Test - public void testOpenDatabaseHookWithoutLocalAuthorRegistered() - throws Exception { - identityManager.onDatabaseOpened(txn); - } - - @Test - public void testOpenDatabaseHookWithLocalAuthorRegistered() - throws Exception { - context.checking(new DbExpectations() {{ + public void testOpenDatabaseHookLocalAuthorRegistered() throws Exception { + context.checking(new Expectations() {{ oneOf(db).addLocalAuthor(txn, localAuthor); }}); @@ -50,12 +60,48 @@ public class IdentityManagerImplTest extends BrambleMockTestCase { identityManager.onDatabaseOpened(txn); } + @Test + public void testOpenDatabaseHookNoLocalAuthorRegisteredHandshakeKeys() + throws Exception { + context.checking(new Expectations() {{ + oneOf(db).getLocalAuthors(txn); + will(returnValue(singletonList(localAuthor))); + }}); + + identityManager.onDatabaseOpened(txn); + } + + @Test + public void testOpenDatabaseHookNoLocalAuthorRegisteredNoHandshakeKeys() + throws Exception { + context.checking(new Expectations() {{ + oneOf(db).getLocalAuthors(txn); + will(returnValue(singletonList(localAuthorWithoutHandshakeKeys))); + oneOf(crypto).generateAgreementKeyPair(); + will(returnValue(handshakeKeyPair)); + oneOf(handshakePublicKey).getEncoded(); + will(returnValue(handshakePublicKeyBytes)); + oneOf(handshakePrivateKey).getEncoded(); + will(returnValue(handshakePrivateKeyBytes)); + oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(), + handshakePublicKeyBytes, handshakePrivateKeyBytes); + }}); + + identityManager.onDatabaseOpened(txn); + + LocalAuthor cached = identityManager.getLocalAuthor(); + assertArrayEquals(handshakePublicKeyBytes, + cached.getHandshakePublicKey()); + assertArrayEquals(handshakePrivateKeyBytes, + cached.getHandshakePrivateKey()); + } + @Test public void testGetLocalAuthor() throws Exception { context.checking(new DbExpectations() {{ oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).getLocalAuthors(txn); - will(returnValue(localAuthors)); + will(returnValue(singletonList(localAuthor))); }}); assertEquals(localAuthor, identityManager.getLocalAuthor()); }