Move handshake keys from LocalAuthor to Account.

parent 251eb9e7
......@@ -5,9 +5,9 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Account;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings;
......@@ -101,6 +101,11 @@ public interface DatabaseComponent {
<R, E extends Exception> R transactionWithNullableResult(boolean readOnly,
NullableDbCallable<R, E> task) throws DbException, E;
/**
* Stores an account.
*/
void addAccount(Transaction txn, Account a) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
......@@ -127,11 +132,6 @@ public interface DatabaseComponent {
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/**
* Stores a local pseudonym.
*/
void addLocalAuthor(Transaction txn, LocalAuthor a) throws DbException;
/**
* Stores a local message.
*/
......@@ -157,6 +157,13 @@ public interface DatabaseComponent {
TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException;
/**
* Returns true if the database contains an account for the given pseudonym.
* <p/>
* Read-only.
*/
boolean containsAccount(Transaction txn, AuthorId local) throws DbException;
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
......@@ -173,14 +180,6 @@ public interface DatabaseComponent {
*/
boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given local author.
* <p/>
* Read-only.
*/
boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
......@@ -247,6 +246,20 @@ public interface DatabaseComponent {
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
int maxLength, int maxLatency) throws DbException;
/**
* Returns the account for the local pseudonym with the given ID.
* <p/>
* Read-only.
*/
Account getAccount(Transaction txn, AuthorId a) throws DbException;
/**
* Returns the accounts for all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<Account> getAccounts(Transaction txn) throws DbException;
/**
* Returns the contact with the given ID.
* <p/>
......@@ -316,20 +329,6 @@ public interface DatabaseComponent {
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
throws DbException;
/**
* Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/**
* Returns all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/**
* Returns the message with the given ID.
* <p/>
......@@ -542,6 +541,11 @@ public interface DatabaseComponent {
void receiveRequest(Transaction txn, ContactId c, Request r)
throws DbException;
/**
* Removes an account (and all associated state) from the database.
*/
void removeAccount(Transaction txn, AuthorId a) throws DbException;
/**
* Removes a contact (and all associated state) from the database.
*/
......@@ -558,11 +562,6 @@ public interface DatabaseComponent {
void removeHandshakeKeys(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/**
* Removes a local pseudonym (and all associated state) from the database.
*/
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/**
* Removes a message (and all associated state) from the database.
*/
......@@ -620,7 +619,7 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException;
/**
* Sets the handshake key pair for the local pseudonym with the given ID.
* Sets the handshake key pair for the account with the given ID.
*/
void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey,
byte[] privateKey) throws DbException;
......
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
@Immutable
@NotNullByDefault
public class Account {
private final LocalAuthor localAuthor;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created;
public Account(LocalAuthor localAuthor,
@Nullable byte[] handshakePublicKey,
@Nullable byte[] handshakePrivateKey, long created) {
if (handshakePublicKey != null) {
int keyLength = handshakePublicKey.length;
if (keyLength == 0 || keyLength > MAX_AGREEMENT_PUBLIC_KEY_BYTES)
throw new IllegalArgumentException();
}
this.localAuthor = localAuthor;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
}
/**
* Returns the ID of the user's pseudonym.
*/
public AuthorId getId() {
return localAuthor.getId();
}
/**
* Returns the user's pseudonym.
*/
public LocalAuthor getLocalAuthor() {
return localAuthor;
}
/**
* Returns true if the account has a handshake key pair.
*/
public boolean hasHandshakeKeyPair() {
return handshakePublicKey != null && handshakePrivateKey != null;
}
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/**
* Returns the time the account was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
@Override
public int hashCode() {
return localAuthor.getId().hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof Account) {
Account a = (Account) o;
return created == a.created &&
localAuthor.equals(a.localAuthor) &&
Arrays.equals(handshakePublicKey, a.handshakePublicKey) &&
Arrays.equals(handshakePrivateKey, a.handshakePrivateKey);
}
return false;
}
}
......@@ -19,9 +19,6 @@ public interface AuthorFactory {
/**
* Creates a local author with the current format version and the given
* name.
*
* @param handshakeKeys true if the local author should include handshake
* keys.
*/
LocalAuthor createLocalAuthor(String name, boolean handshakeKeys);
LocalAuthor createLocalAuthor(String name);
}
......@@ -17,12 +17,19 @@ public interface IdentityManager {
LocalAuthor createLocalAuthor(String name);
/**
* Registers the given local identity with the manager. This method should
* be called before {@link LifecycleManager#startServices(SecretKey)}. The
* identity is stored when {@link LifecycleManager#startServices(SecretKey)}
* is called.
* Creates an account with the given name. The account includes a handshake
* key pair.
*/
void registerLocalAuthor(LocalAuthor a);
@CryptoExecutor
Account createAccount(String name);
/**
* Registers the given account with the manager. This method should be
* called before {@link LifecycleManager#startServices(SecretKey)}. The
* account is stored when {@link LifecycleManager#startServices(SecretKey)}
* is called. The account must include a handshake key pair.
*/
void registerAccount(Account a);
/**
* Returns the cached local identity or loads it from the database.
......
......@@ -2,11 +2,8 @@ package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/**
* A pseudonym for the local user.
*/
......@@ -15,31 +12,11 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
public class LocalAuthor extends Author {
private final byte[] privateKey;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, long created) {
byte[] publicKey, byte[] privateKey) {
super(id, formatVersion, name, publicKey);
this.privateKey = privateKey;
this.created = created;
handshakePublicKey = null;
handshakePrivateKey = null;
}
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, byte[] handshakePublicKey,
byte[] handshakePrivateKey, long created) {
super(id, formatVersion, name, publicKey);
if (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH) {
throw new IllegalArgumentException();
}
this.privateKey = privateKey;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
}
/**
......@@ -48,28 +25,4 @@ public class LocalAuthor extends Author {
public byte[] getPrivateKey() {
return privateKey;
}
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/**
* Returns the time the pseudonym was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
}
......@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Account;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
......@@ -100,28 +101,20 @@ public class TestUtils {
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
}
public static LocalAuthor getLocalAuthor() {
return getLocalAuthor(false);
public static Account getAccount() {
LocalAuthor localAuthor = getLocalAuthor();
byte[] handshakePub = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] handshakePriv = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new Account(localAuthor, handshakePub, handshakePriv, timestamp);
}
public static LocalAuthor getLocalAuthor(boolean withHandshakeKeys) {
public static LocalAuthor getLocalAuthor() {
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);
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);
}
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
}
public static Author getAuthor() {
......
......@@ -4,8 +4,8 @@ import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Account;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.util.IoUtils;
......@@ -161,8 +161,8 @@ class AccountManagerImpl implements AccountManager {
synchronized (stateChangeLock) {
if (hasDatabaseKey())
throw new AssertionError("Already have a database key");
LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
identityManager.registerLocalAuthor(localAuthor);
Account account = identityManager.createAccount(name);
identityManager.registerAccount(account);
SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key;
......
......@@ -13,9 +13,9 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.identity.Account;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings;
......@@ -86,6 +86,11 @@ interface Database<T> {
*/
void commitTransaction(T txn) throws DbException;
/**
* Stores an account.
*/
void addAccount(T txn, Account a) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
......@@ -119,11 +124,6 @@ interface Database<T> {
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/**
* Stores a local pseudonym.
*/
void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
/**
* Stores a message.
*
......@@ -163,6 +163,13 @@ interface Database<T> {
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Returns true if the database contains an account for the given pseudonym.
* <p/>
* Read-only.
*/
boolean containsAccount(T txn, AuthorId a) throws DbException;
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
......@@ -186,13 +193,6 @@ interface Database<T> {
*/
boolean containsGroup(T txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given local pseudonym.
* <p/>
* Read-only.
*/
boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns true if the database contains the given message.
* <p/>
......@@ -245,6 +245,20 @@ interface Database<T> {
*/
void deleteMessageMetadata(T txn, MessageId m) throws DbException;
/**
* Returns the account for local pseudonym with the given ID.
* <p/>
* Read-only.
*/
Account getAccount(T txn, AuthorId a) throws DbException;
/**
* Returns the accounts for all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<Account> getAccounts(T txn) throws DbException;
/**
* Returns the contact with the given ID.
* <p/>
......@@ -322,20 +336,6 @@ interface Database<T> {
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
throws DbException;
/**
* Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/
LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/**
* Returns the message with the given ID.
* <p/>
......@@ -605,6 +605,11 @@ interface Database<T> {
*/
void raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
/**
* Removes an account (and all associated state) from the database.
*/
void removeAccount(T txn, AuthorId a) throws DbException;
/**
* Removes a contact from the database.
*/
......@@ -628,11 +633,6 @@ interface Database<T> {
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/**
* Removes a local pseudonym (and all associated state) from the database.
*/
void removeLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Removes a message (and all associated state) from the database.
*/
......@@ -686,7 +686,7 @@ interface Database<T> {
throws DbException;
/**
* Sets the handshake key pair for the local pseudonym with the given ID.
* Sets the handshake key pair for the account with the given ID.
*/
void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey,
byte[] privateKey) throws DbException;
......
......@@ -30,9 +30,9 @@ import org.briarproject.bramble.api.db.TaskAction;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.identity.Account;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent;
import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
......@@ -231,15 +231,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return txnClass.cast(transaction.unbox());
}
@Override
public void addAccount(Transaction transaction, Account a)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsAccount(txn, a.getId())) {
db.addAccount(txn, a);
transaction.attach(new LocalAuthorAddedEvent(a.getId()));
}
}
@Override
public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean verified)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local))
if (!db.containsAccount(txn, local))
throw new NoSuchLocalAuthorException();
if (db.containsLocalAuthor(txn, remote.getId()))
if (db.containsAccount(txn, remote.getId()))
throw new ContactExistsException();
if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException();
......@@ -282,17 +293,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.addHandshakeKeys(txn, p, k);
}
@Override
public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a.getId())) {
db.addLocalAuthor(txn, a);
transaction.attach(new LocalAuthorAddedEvent(a.getId()));
}
}
@Override
public void addLocalMessage(Transaction transaction, Message m,
Metadata meta, boolean shared) throws DbException {
......@@ -341,11 +341,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.addTransportKeys(txn, c, k);
}
@Override
public boolean containsAccount(Transaction transaction, AuthorId local)
throws DbException {
T txn = unbox(transaction);
return db.containsAccount(txn, local);
}
@Override
public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local))
if (!db.containsAccount(txn, local))
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
}
......@@ -357,13 +364,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsGroup(txn, g);
}
@Override
public boolean containsLocalAuthor(Transaction transaction, AuthorId local)
throws DbException {
T txn = unbox(transaction);
return db.containsLocalAuthor(txn, local);
}
@Override
public boolean containsPendingContact(Transaction transaction,
PendingContactId p) throws DbException {
......@@ -478,6 +478,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return messages;
}
@Override
public Account getAccount(Transaction transaction, AuthorId a)
throws DbException {
T txn = unbox(transaction);
if (!db.containsAccount(txn, a))
throw new NoSuchLocalAuthorException();
return db.getAccount(txn, a);
}
@Override
public Collection<Account> getAccounts(Transaction transaction)
throws DbException {
T txn = unbox(transaction);
return db.getAccounts(txn);
}
@Override
public Contact getContact(Transaction transaction, ContactId c)
throws DbException {
......@@ -505,7 +521,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Collection<ContactId> getContacts(Transaction transaction,
AuthorId a) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a))
if (!db.containsAccount(txn, a))
throw new NoSuchLocalAuthorException();
return db.getContacts(txn, a);
}
......@@ -553,22 +569,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getHandshakeKeys(txn, t);
}
@Override
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
return db.getLocalAuthor(txn, a);
}
@Override
public Collection<LocalAuthor> getLocalAuthors(Transaction transaction)
throws DbException {
T txn = unbox(transaction);
return db.getLocalAuthors(txn);
}
@Override
public Message getMessage(Transaction transaction, MessageId m)
throws DbException {
......@@ -868,6 +868,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (requested) transaction.attach(new MessageRequestedEvent(c));
}