mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Generate handshake keys on demand, store when DB is opened.
This commit is contained in:
@@ -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,19 +63,7 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenDatabaseHookNoAccountRegisteredHandshakeKeys()
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getAccounts(txn);
|
||||
will(returnValue(singletonList(accountWithKeys)));
|
||||
}});
|
||||
|
||||
identityManager.onDatabaseOpened(txn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenDatabaseHookNoAccountRegisteredNoHandshakeKeys()
|
||||
throws Exception {
|
||||
public void testOpenDatabaseHandshakeKeysGenerated() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getAccounts(txn);
|
||||
will(returnValue(singletonList(accountWithoutKeys)));
|
||||
@@ -93,18 +81,46 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocalAuthor() 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();
|
||||
will(returnValue(handshakeKeyPair));
|
||||
oneOf(handshakePublicKey).getEncoded();
|
||||
will(returnValue(handshakePublicKeyBytes));
|
||||
oneOf(handshakePrivateKey).getEncoded();
|
||||
will(returnValue(handshakePrivateKeyBytes));
|
||||
}});
|
||||
|
||||
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
||||
}
|
||||
|
||||
@Test
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user