mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
Merge branch 'contact-exchange-refactoring' into 'master'
Contact exchange refactoring See merge request briar/briar!1106
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
interface ContactExchangeConstants {
|
||||
|
||||
/**
|
||||
* The current version of the contact exchange protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's header key from the master key.
|
||||
*/
|
||||
String ALICE_KEY_LABEL =
|
||||
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's header key from the master key.
|
||||
*/
|
||||
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's key binding nonce from the master key.
|
||||
*/
|
||||
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's key binding nonce from the master key.
|
||||
*/
|
||||
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
|
||||
|
||||
/**
|
||||
* Label for signing key binding nonces.
|
||||
*/
|
||||
String SIGNING_LABEL = "org.briarproject.briar.contact/EXCHANGE";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
interface ContactExchangeCrypto {
|
||||
|
||||
/**
|
||||
* Derives the header key for a contact exchange stream from the master key.
|
||||
*
|
||||
* @param alice Whether the header key is for the stream sent by Alice
|
||||
*/
|
||||
SecretKey deriveHeaderKey(SecretKey masterKey, boolean alice);
|
||||
|
||||
/**
|
||||
* Creates and returns a signature that proves ownership of a pseudonym.
|
||||
*
|
||||
* @param privateKey The pseudonym's signature private key
|
||||
* @param alice Whether the pseudonym belongs to Alice
|
||||
*/
|
||||
byte[] sign(PrivateKey privateKey, SecretKey masterKey, boolean alice);
|
||||
|
||||
/**
|
||||
* Verifies a signature that proves ownership of a pseudonym.
|
||||
*
|
||||
* @param publicKey The pseudonym's signature public key
|
||||
* @param alice Whether the pseudonym belongs to Alice
|
||||
* @return True if the signature is valid
|
||||
*/
|
||||
boolean verify(PublicKey publicKey, SecretKey masterKey, boolean alice,
|
||||
byte[] signature);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.ALICE_KEY_LABEL;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.ALICE_NONCE_LABEL;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.BOB_KEY_LABEL;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.BOB_NONCE_LABEL;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.SIGNING_LABEL;
|
||||
|
||||
@NotNullByDefault
|
||||
class ContactExchangeCryptoImpl implements ContactExchangeCrypto {
|
||||
|
||||
private static final byte[] PROTOCOL_VERSION_BYTES =
|
||||
new byte[] {PROTOCOL_VERSION};
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
ContactExchangeCryptoImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveHeaderKey(SecretKey masterKey, boolean alice) {
|
||||
String label = alice ? ALICE_KEY_LABEL : BOB_KEY_LABEL;
|
||||
return crypto.deriveKey(label, masterKey, PROTOCOL_VERSION_BYTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(PrivateKey privateKey, SecretKey masterKey,
|
||||
boolean alice) {
|
||||
byte[] nonce = deriveNonce(masterKey, alice);
|
||||
try {
|
||||
return crypto.sign(SIGNING_LABEL, nonce, privateKey);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(PublicKey publicKey,
|
||||
SecretKey masterKey, boolean alice, byte[] signature) {
|
||||
byte[] nonce = deriveNonce(masterKey, alice);
|
||||
try {
|
||||
return crypto.verifySignature(signature, SIGNING_LABEL, nonce,
|
||||
publicKey);
|
||||
} catch (GeneralSecurityException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] deriveNonce(SecretKey masterKey, boolean alice) {
|
||||
String label = alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL;
|
||||
return crypto.mac(label, masterKey, PROTOCOL_VERSION_BYTES);
|
||||
}
|
||||
}
|
||||
@@ -3,24 +3,21 @@ package org.briarproject.bramble.contact;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
||||
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.event.ContactExchangeFailedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactExchangeSucceededEvent;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
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.ContactExistsException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
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.plugin.ConnectionManager;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
@@ -39,28 +36,24 @@ 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.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
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.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
class ContactExchangeManagerImpl implements ContactExchangeManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ContactExchangeTaskImpl.class.getName());
|
||||
|
||||
private static final String SIGNING_LABEL_EXCHANGE =
|
||||
"org.briarproject.briar.contact/EXCHANGE";
|
||||
getLogger(ContactExchangeManagerImpl.class.getName());
|
||||
|
||||
// Accept records with current protocol version, known record type
|
||||
private static final Predicate<Record> ACCEPT = r ->
|
||||
@@ -80,190 +73,108 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
private final ClientHelper clientHelper;
|
||||
private final RecordReaderFactory recordReaderFactory;
|
||||
private final RecordWriterFactory recordWriterFactory;
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final ConnectionManager connectionManager;
|
||||
private final ContactManager contactManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final TransportPropertyManager transportPropertyManager;
|
||||
private final CryptoComponent crypto;
|
||||
private final ContactExchangeCrypto contactExchangeCrypto;
|
||||
private final StreamReaderFactory streamReaderFactory;
|
||||
private final StreamWriterFactory streamWriterFactory;
|
||||
|
||||
private volatile LocalAuthor localAuthor;
|
||||
private volatile DuplexTransportConnection conn;
|
||||
private volatile TransportId transportId;
|
||||
private volatile SecretKey masterKey;
|
||||
private volatile boolean alice;
|
||||
|
||||
@Inject
|
||||
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
ContactExchangeManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
RecordReaderFactory recordReaderFactory,
|
||||
RecordWriterFactory recordWriterFactory, EventBus eventBus,
|
||||
Clock clock, ConnectionManager connectionManager,
|
||||
ContactManager contactManager,
|
||||
RecordWriterFactory recordWriterFactory, Clock clock,
|
||||
ContactManager contactManager, IdentityManager identityManager,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
|
||||
ContactExchangeCrypto contactExchangeCrypto,
|
||||
StreamReaderFactory streamReaderFactory,
|
||||
StreamWriterFactory streamWriterFactory) {
|
||||
this.db = db;
|
||||
this.clientHelper = clientHelper;
|
||||
this.recordReaderFactory = recordReaderFactory;
|
||||
this.recordWriterFactory = recordWriterFactory;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
this.connectionManager = connectionManager;
|
||||
this.contactManager = contactManager;
|
||||
this.identityManager = identityManager;
|
||||
this.transportPropertyManager = transportPropertyManager;
|
||||
this.crypto = crypto;
|
||||
this.contactExchangeCrypto = contactExchangeCrypto;
|
||||
this.streamReaderFactory = streamReaderFactory;
|
||||
this.streamWriterFactory = streamWriterFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
|
||||
DuplexTransportConnection conn, TransportId transportId,
|
||||
boolean alice) {
|
||||
this.localAuthor = localAuthor;
|
||||
this.conn = conn;
|
||||
this.transportId = transportId;
|
||||
this.masterKey = masterKey;
|
||||
this.alice = alice;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
public Contact exchangeContacts(TransportId t,
|
||||
DuplexTransportConnection conn, SecretKey masterKey, boolean alice)
|
||||
throws IOException, DbException {
|
||||
// Get the transport connection's input and output streams
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
try {
|
||||
in = conn.getReader().getInputStream();
|
||||
out = conn.getWriter().getOutputStream();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
tryToClose(conn);
|
||||
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||
return;
|
||||
}
|
||||
InputStream in = conn.getReader().getInputStream();
|
||||
OutputStream out = conn.getWriter().getOutputStream();
|
||||
|
||||
// Get the local transport properties
|
||||
Map<TransportId, TransportProperties> localProperties;
|
||||
try {
|
||||
localProperties = transportPropertyManager.getLocalProperties();
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||
tryToClose(conn);
|
||||
return;
|
||||
}
|
||||
// Get the local author and transport properties
|
||||
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
||||
Map<TransportId, TransportProperties> localProperties =
|
||||
transportPropertyManager.getLocalProperties();
|
||||
|
||||
// Derive the header keys for the transport streams
|
||||
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, masterKey,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterKey,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey localHeaderKey =
|
||||
contactExchangeCrypto.deriveHeaderKey(masterKey, alice);
|
||||
SecretKey remoteHeaderKey =
|
||||
contactExchangeCrypto.deriveHeaderKey(masterKey, !alice);
|
||||
|
||||
// Create the readers
|
||||
InputStream streamReader =
|
||||
streamReaderFactory.createContactExchangeStreamReader(in,
|
||||
alice ? bobHeaderKey : aliceHeaderKey);
|
||||
InputStream streamReader = streamReaderFactory
|
||||
.createContactExchangeStreamReader(in, remoteHeaderKey);
|
||||
RecordReader recordReader =
|
||||
recordReaderFactory.createRecordReader(streamReader);
|
||||
|
||||
// Create the writers
|
||||
StreamWriter streamWriter =
|
||||
streamWriterFactory.createContactExchangeStreamWriter(out,
|
||||
alice ? aliceHeaderKey : bobHeaderKey);
|
||||
RecordWriter recordWriter =
|
||||
recordWriterFactory
|
||||
.createRecordWriter(streamWriter.getOutputStream());
|
||||
StreamWriter streamWriter = streamWriterFactory
|
||||
.createContactExchangeStreamWriter(out, localHeaderKey);
|
||||
RecordWriter recordWriter = recordWriterFactory
|
||||
.createRecordWriter(streamWriter.getOutputStream());
|
||||
|
||||
// Derive the nonces to be signed
|
||||
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterKey,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterKey,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] localNonce = alice ? aliceNonce : bobNonce;
|
||||
byte[] remoteNonce = alice ? bobNonce : aliceNonce;
|
||||
|
||||
// Sign the nonce
|
||||
byte[] localSignature = sign(localAuthor, localNonce);
|
||||
// Create our signature
|
||||
byte[] localSignature = contactExchangeCrypto
|
||||
.sign(localAuthor.getPrivateKey(), masterKey, alice);
|
||||
|
||||
// Exchange contact info
|
||||
long localTimestamp = clock.currentTimeMillis();
|
||||
ContactInfo remoteInfo;
|
||||
try {
|
||||
if (alice) {
|
||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
||||
localSignature, localTimestamp);
|
||||
recordWriter.flush();
|
||||
remoteInfo = receiveContactInfo(recordReader);
|
||||
} else {
|
||||
remoteInfo = receiveContactInfo(recordReader);
|
||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
||||
localSignature, localTimestamp);
|
||||
recordWriter.flush();
|
||||
}
|
||||
// Send EOF on the outgoing stream
|
||||
streamWriter.sendEndOfStream();
|
||||
// Skip any remaining records from the incoming stream
|
||||
recordReader.readRecord(r -> false, IGNORE);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||
tryToClose(conn);
|
||||
return;
|
||||
if (alice) {
|
||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
||||
localSignature, localTimestamp);
|
||||
remoteInfo = receiveContactInfo(recordReader);
|
||||
} else {
|
||||
remoteInfo = receiveContactInfo(recordReader);
|
||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
||||
localSignature, localTimestamp);
|
||||
}
|
||||
|
||||
// Send EOF on the outgoing stream
|
||||
streamWriter.sendEndOfStream();
|
||||
|
||||
// Skip any remaining records from the incoming stream
|
||||
recordReader.readRecord(r -> false, IGNORE);
|
||||
|
||||
// Verify the contact's signature
|
||||
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
|
||||
PublicKey remotePublicKey = remoteInfo.author.getPublicKey();
|
||||
if (!contactExchangeCrypto.verify(remotePublicKey,
|
||||
masterKey, !alice, remoteInfo.signature)) {
|
||||
LOG.warning("Invalid signature");
|
||||
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||
tryToClose(conn);
|
||||
return;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
// The agreed timestamp is the minimum of the peers' timestamps
|
||||
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
||||
|
||||
try {
|
||||
// Add the contact
|
||||
ContactId contactId = addContact(remoteInfo.author, timestamp,
|
||||
remoteInfo.properties);
|
||||
// Reuse the connection as a transport connection
|
||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||
conn);
|
||||
// Pseudonym exchange succeeded
|
||||
LOG.info("Pseudonym exchange succeeded");
|
||||
eventBus.broadcast(
|
||||
new ContactExchangeSucceededEvent(remoteInfo.author));
|
||||
} catch (ContactExistsException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
tryToClose(conn);
|
||||
eventBus.broadcast(
|
||||
new ContactExchangeFailedEvent(remoteInfo.author));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
tryToClose(conn);
|
||||
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||
}
|
||||
}
|
||||
// Add the contact
|
||||
Contact contact = addContact(remoteInfo.author, localAuthor,
|
||||
masterKey, timestamp, alice, remoteInfo.properties);
|
||||
|
||||
private byte[] sign(LocalAuthor author, byte[] nonce) {
|
||||
try {
|
||||
return crypto.sign(SIGNING_LABEL_EXCHANGE, nonce,
|
||||
author.getPrivateKey());
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verify(Author author, byte[] nonce, byte[] signature) {
|
||||
try {
|
||||
return crypto.verifySignature(signature, SIGNING_LABEL_EXCHANGE,
|
||||
nonce, author.getPublicKey());
|
||||
} catch (GeneralSecurityException e) {
|
||||
return false;
|
||||
}
|
||||
// Contact exchange succeeded
|
||||
LOG.info("Contact exchange succeeded");
|
||||
return contact;
|
||||
}
|
||||
|
||||
private void sendContactInfo(RecordWriter recordWriter, Author author,
|
||||
@@ -274,6 +185,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
BdfList payload = BdfList.of(authorList, props, signature, timestamp);
|
||||
recordWriter.writeRecord(new Record(PROTOCOL_VERSION, CONTACT_INFO,
|
||||
clientHelper.toByteArray(payload)));
|
||||
recordWriter.flush();
|
||||
LOG.info("Sent contact info");
|
||||
}
|
||||
|
||||
@@ -295,7 +207,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
return new ContactInfo(author, properties, signature, timestamp);
|
||||
}
|
||||
|
||||
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||
private Contact addContact(Author remoteAuthor, LocalAuthor localAuthor,
|
||||
SecretKey masterKey, long timestamp, boolean alice,
|
||||
Map<TransportId, TransportProperties> remoteProperties)
|
||||
throws DbException {
|
||||
return db.transactionWithResult(false, txn -> {
|
||||
@@ -304,20 +217,10 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
true, true);
|
||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||
remoteProperties);
|
||||
return contactId;
|
||||
return contactManager.getContact(txn, contactId);
|
||||
});
|
||||
}
|
||||
|
||||
private void tryToClose(DuplexTransportConnection conn) {
|
||||
try {
|
||||
LOG.info("Closing connection");
|
||||
conn.getReader().dispose(true, true);
|
||||
conn.getWriter().dispose(true);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ContactInfo {
|
||||
|
||||
private final Author author;
|
||||
@@ -137,6 +137,11 @@ class ContactManagerImpl implements ContactManager {
|
||||
return db.transactionWithResult(true, txn -> db.getContact(txn, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contact getContact(Transaction txn, ContactId c) throws DbException {
|
||||
return db.getContact(txn, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
||||
throws DbException {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -19,14 +19,14 @@ public class ContactModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ContactManager getContactManager(ContactManagerImpl contactManager) {
|
||||
ContactManager provideContactManager(ContactManagerImpl contactManager) {
|
||||
return contactManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
ContactExchangeTask provideContactExchangeTask(
|
||||
ContactExchangeTaskImpl contactExchangeTask) {
|
||||
return contactExchangeTask;
|
||||
ContactExchangeManager provideContactExchangeManager(
|
||||
ContactExchangeManagerImpl contactExchangeManager) {
|
||||
return contactExchangeManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -34,4 +34,10 @@ public class ContactModule {
|
||||
PendingContactFactoryImpl pendingContactFactory) {
|
||||
return pendingContactFactory;
|
||||
}
|
||||
|
||||
@Provides
|
||||
ContactExchangeCrypto provideContactExchangeCrypto(
|
||||
ContactExchangeCryptoImpl contactExchangeCrypto) {
|
||||
return contactExchangeCrypto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,16 +234,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public ContactId addContact(Transaction transaction, Author remote,
|
||||
AuthorId local, boolean verified)
|
||||
throws DbException {
|
||||
AuthorId local, boolean verified) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsIdentity(txn, local))
|
||||
throw new NoSuchIdentityException();
|
||||
if (db.containsIdentity(txn, remote.getId()))
|
||||
throw new ContactExistsException();
|
||||
throw new ContactExistsException(local, remote);
|
||||
if (db.containsContact(txn, remote.getId(), local))
|
||||
throw new ContactExistsException();
|
||||
throw new ContactExistsException(local, remote);
|
||||
ContactId c = db.addContact(txn, remote, local, verified);
|
||||
transaction.attach(new ContactAddedEvent(c));
|
||||
return c;
|
||||
|
||||
@@ -1459,7 +1459,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
true));
|
||||
fail();
|
||||
} catch (ContactExistsException expected) {
|
||||
// Expected
|
||||
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
|
||||
assertEquals(author, expected.getRemoteAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1488,7 +1489,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
true));
|
||||
fail();
|
||||
} catch (ContactExistsException expected) {
|
||||
// Expected
|
||||
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
|
||||
assertEquals(author, expected.getRemoteAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user