diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java index cfbdf1e68..6856fbbb8 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java @@ -38,7 +38,18 @@ public interface IdentityManager { /** * Returns the cached local identity or loads it from the database. + *
+ * Read-only. */ LocalAuthor getLocalAuthor(Transaction txn) throws DbException; + /** + * Returns the cached handshake keys or loads them from the database. + * + * 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; } 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 3f2c0a6f9..e1662b570 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 @@ -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