diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeManager.java index 0981158f0..dad6ae6b1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactExchangeManager.java @@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import java.io.IOException; @@ -13,12 +12,26 @@ import java.io.IOException; public interface ContactExchangeManager { /** - * Exchanges contact information with a remote peer. + * Exchanges contact information with a remote peer and adds the peer + * as a contact. * * @param alice Whether the local peer takes the role of Alice * @return The newly added contact * @throws ContactExistsException If the contact already exists */ - Contact exchangeContacts(TransportId t, DuplexTransportConnection conn, - SecretKey masterKey, boolean alice) throws IOException, DbException; + Contact exchangeContacts(DuplexTransportConnection conn, + SecretKey masterKey, boolean alice, boolean verified) + throws IOException, DbException; + + /** + * Exchanges contact information with a remote peer and adds the peer + * as a contact, replacing the given pending contact. + * + * @param alice Whether the local peer takes the role of Alice + * @return The newly added contact + * @throws ContactExistsException If the contact already exists + */ + Contact exchangeContacts(PendingContactId p, DuplexTransportConnection conn, + SecretKey masterKey, boolean alice, boolean verified) + throws IOException, DbException; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java index 49ed255f2..9e3bd03a7 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java @@ -7,17 +7,18 @@ import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.identity.Author; 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.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; @@ -36,20 +37,23 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.GeneralSecurityException; import java.util.Map; import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import javax.inject.Inject; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.contact.RecordTypes.CONTACT_INFO; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION; +import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO; import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkSize; -@MethodsNotNullByDefault -@ParametersNotNullByDefault +@Immutable +@NotNullByDefault class ContactExchangeManagerImpl implements ContactExchangeManager { private static final Logger LOG = @@ -104,9 +108,22 @@ class ContactExchangeManagerImpl implements ContactExchangeManager { } @Override - public Contact exchangeContacts(TransportId t, - DuplexTransportConnection conn, SecretKey masterKey, boolean alice) - throws IOException, DbException { + public Contact exchangeContacts(DuplexTransportConnection conn, + SecretKey masterKey, boolean alice, + boolean verified) throws IOException, DbException { + return exchange(null, conn, masterKey, alice, verified); + } + + @Override + public Contact exchangeContacts(PendingContactId p, + DuplexTransportConnection conn, SecretKey masterKey, boolean alice, + boolean verified) throws IOException, DbException { + return exchange(p, conn, masterKey, alice, verified); + } + + private Contact exchange(@Nullable PendingContactId p, + DuplexTransportConnection conn, SecretKey masterKey, boolean alice, + boolean verified) throws IOException, DbException { // Get the transport connection's input and output streams InputStream in = conn.getReader().getInputStream(); OutputStream out = conn.getWriter().getOutputStream(); @@ -169,8 +186,8 @@ class ContactExchangeManagerImpl implements ContactExchangeManager { long timestamp = Math.min(localTimestamp, remoteInfo.timestamp); // Add the contact - Contact contact = addContact(remoteInfo.author, localAuthor, - masterKey, timestamp, alice, remoteInfo.properties); + Contact contact = addContact(p, remoteInfo.author, localAuthor, + masterKey, timestamp, alice, verified, remoteInfo.properties); // Contact exchange succeeded LOG.info("Contact exchange succeeded"); @@ -207,18 +224,34 @@ class ContactExchangeManagerImpl implements ContactExchangeManager { return new ContactInfo(author, properties, signature, timestamp); } - private Contact addContact(Author remoteAuthor, LocalAuthor localAuthor, - SecretKey masterKey, long timestamp, boolean alice, + private Contact addContact(@Nullable PendingContactId pendingContactId, + Author remoteAuthor, LocalAuthor localAuthor, SecretKey masterKey, + long timestamp, boolean alice, boolean verified, Map remoteProperties) - throws DbException { - return db.transactionWithResult(false, txn -> { - ContactId contactId = contactManager.addContact(txn, remoteAuthor, - localAuthor.getId(), masterKey, timestamp, alice, - true, true); + throws DbException, FormatException { + Transaction txn = db.startTransaction(false); + try { + ContactId contactId; + if (pendingContactId == null) { + contactId = contactManager.addContact(txn, remoteAuthor, + localAuthor.getId(), masterKey, timestamp, alice, + verified, true); + } else { + contactId = contactManager.addContact(txn, pendingContactId, + remoteAuthor, localAuthor.getId(), masterKey, + timestamp, alice, verified, true); + } transportPropertyManager.addRemoteProperties(txn, contactId, remoteProperties); - return contactManager.getContact(txn, contactId); - }); + Contact contact = contactManager.getContact(txn, contactId); + db.commitTransaction(txn); + return contact; + } catch (GeneralSecurityException e) { + // Pending contact's public key is invalid + throw new FormatException(); + } finally { + db.endTransaction(txn); + } } private static class ContactInfo { diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/RecordTypes.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeRecordTypes.java similarity index 51% rename from bramble-api/src/main/java/org/briarproject/bramble/api/contact/RecordTypes.java rename to bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeRecordTypes.java index bd24dcf67..750cd7e53 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/RecordTypes.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeRecordTypes.java @@ -1,9 +1,9 @@ -package org.briarproject.bramble.api.contact; +package org.briarproject.bramble.contact; /** * Record types for the contact exchange protocol. */ -public interface RecordTypes { +interface ContactExchangeRecordTypes { byte CONTACT_INFO = 0; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeViewModel.java index a7ada2cbe..7a274bdc9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeViewModel.java @@ -58,8 +58,8 @@ class ContactExchangeViewModel extends AndroidViewModel { SecretKey masterKey, boolean alice) { ioExecutor.execute(() -> { try { - Contact contact = contactExchangeManager.exchangeContacts(t, - conn, masterKey, alice); + Contact contact = contactExchangeManager.exchangeContacts(conn, + masterKey, alice, true); // Reuse the connection as a transport connection connectionManager.manageOutgoingConnection(contact.getId(), t, conn);