mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Generate and store handshake keys at startup if needed.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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<LocalAuthor> authors = db.getLocalAuthors(txn);
|
||||
if (authors.size() != 1) throw new IllegalStateException();
|
||||
return authors.iterator().next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LocalAuthor> 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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user