Skip to content
Snippets Groups Projects
Verified Commit faba9a6b authored by akwizgran's avatar akwizgran
Browse files

Generate handshake keys on demand, store when DB is opened.

parent 891c82b2
No related branches found
No related tags found
1 merge request!1082Generate and store handshake key pair at startup if necessary
......@@ -38,7 +38,18 @@ public interface IdentityManager {
/**
* Returns the cached local identity or loads it from the database.
* <p/>
* Read-only.
*/
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/**
* Returns the cached handshake keys or loads them from the database.
* <p/>
* Read-only.
*
* @return A two-element array containing the public key in the first
* element and the private key in the second
*/
byte[][] getHandshakeKeys(Transaction txn) throws DbException;
}
......@@ -21,6 +21,7 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now;
......@@ -36,8 +37,27 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
private final AuthorFactory authorFactory;
private final Clock clock;
/**
* The user's account, or null if no account has been registered or loaded.
* If non-null, this account always has handshake keys.
*/
@Nullable
private volatile Account cachedAccount;
private volatile Account cachedAccount = null;
/**
* True if {@code cachedAccount} was registered via
* {@link #registerAccount(Account)} and should be stored when
* {@link #onDatabaseOpened(Transaction)} is called.
*/
private volatile boolean shouldStoreAccount = false;
/**
* True if the handshake keys in {@code cachedAccount} were generated when
* the account was loaded and should be stored when
* {@link #onDatabaseOpened(Transaction)} is called.
*/
private volatile boolean shouldStoreKeys = false;
@Inject
IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto,
......@@ -72,29 +92,24 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
public void registerAccount(Account a) {
if (!a.hasHandshakeKeyPair()) throw new IllegalArgumentException();
cachedAccount = a;
shouldStoreAccount = true;
LOG.info("Account registered");
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
Account cached = cachedAccount;
if (cached == null) {
cached = loadAccount(txn);
if (cached.hasHandshakeKeyPair()) {
cachedAccount = cached;
LOG.info("Account loaded");
} else {
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
byte[] handshakePriv =
handshakeKeyPair.getPrivate().getEncoded();
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
handshakePriv);
LOG.info("Handshake key pair stored");
}
} else {
if (cached == null)
cachedAccount = cached = loadAccountWithKeyPair(txn);
if (shouldStoreAccount) {
db.addAccount(txn, cached);
LOG.info("Account stored");
} else if (shouldStoreKeys) {
requireNonNull(cached);
byte[] publicKey = requireNonNull(cached.getHandshakePublicKey());
byte[] privateKey = requireNonNull(cached.getHandshakePrivateKey());
db.setHandshakeKeyPair(txn, cached.getId(), publicKey, privateKey);
LOG.info("Handshake key pair stored");
}
}
......@@ -102,9 +117,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
public LocalAuthor getLocalAuthor() throws DbException {
Account cached = cachedAccount;
if (cached == null) {
cachedAccount = cached =
db.transactionWithResult(true, this::loadAccount);
LOG.info("Account loaded");
cachedAccount = cached = db.transactionWithResult(true,
this::loadAccountWithKeyPair);
}
return cached.getLocalAuthor();
}
......@@ -112,13 +126,35 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
@Override
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
Account cached = cachedAccount;
if (cached == null) {
cachedAccount = cached = loadAccount(txn);
LOG.info("Account loaded");
}
if (cached == null)
cachedAccount = cached = loadAccountWithKeyPair(txn);
return cached.getLocalAuthor();
}
@Override
public byte[][] getHandshakeKeys(Transaction txn) throws DbException {
Account cached = cachedAccount;
if (cached == null)
cachedAccount = cached = loadAccountWithKeyPair(txn);
return new byte[][] {
cached.getHandshakePublicKey(),
cached.getHandshakePrivateKey()
};
}
private Account loadAccountWithKeyPair(Transaction txn) throws DbException {
Account a = loadAccount(txn);
LOG.info("Account loaded");
if (a.hasHandshakeKeyPair()) return a;
KeyPair keyPair = crypto.generateAgreementKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
LOG.info("Handshake key pair generated");
shouldStoreKeys = true;
return new Account(a.getLocalAuthor(), publicKey, privateKey,
a.getTimeCreated());
}
private Account loadAccount(Transaction txn) throws DbException {
Collection<Account> accounts = db.getAccounts(txn);
if (accounts.size() != 1) throw new DbException();
......
......@@ -53,7 +53,7 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
}
@Test
public void testOpenDatabaseHookAccountRegistered() throws Exception {
public void testOpenDatabaseAccountRegistered() throws Exception {
context.checking(new Expectations() {{
oneOf(db).addAccount(txn, accountWithKeys);
}});
......@@ -63,20 +63,43 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
}
@Test
public void testOpenDatabaseHookNoAccountRegisteredHandshakeKeys()
throws Exception {
public void testOpenDatabaseHandshakeKeysGenerated() throws Exception {
context.checking(new Expectations() {{
oneOf(db).getAccounts(txn);
will(returnValue(singletonList(accountWithKeys)));
will(returnValue(singletonList(accountWithoutKeys)));
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);
}
@Test
public void testOpenDatabaseHookNoAccountRegisteredNoHandshakeKeys()
throws Exception {
public void testOpenDatabaseNoHandshakeKeysGenerated() throws Exception {
context.checking(new Expectations() {{
oneOf(db).getAccounts(txn);
will(returnValue(singletonList(accountWithKeys)));
}});
identityManager.onDatabaseOpened(txn);
}
@Test
public void testGetLocalAuthorAccountRegistered() throws DbException {
identityManager.registerAccount(accountWithKeys);
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
@Test
public void testGetLocalAuthorHandshakeKeysGenerated() throws Exception {
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getAccounts(txn);
will(returnValue(singletonList(accountWithoutKeys)));
oneOf(crypto).generateAgreementKeyPair();
......@@ -85,26 +108,19 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
will(returnValue(handshakePublicKeyBytes));
oneOf(handshakePrivateKey).getEncoded();
will(returnValue(handshakePrivateKeyBytes));
oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(),
handshakePublicKeyBytes, handshakePrivateKeyBytes);
}});
identityManager.onDatabaseOpened(txn);
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
@Test
public void testGetLocalAuthor() throws Exception {
public void testGetLocalAuthorNoHandshakeKeysGenerated() throws Exception {
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getAccounts(txn);
will(returnValue(singletonList(accountWithKeys)));
}});
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
@Test
public void testGetCachedLocalAuthor() throws DbException {
identityManager.registerAccount(accountWithKeys);
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment