|
|
|
|
@@ -3,10 +3,11 @@ 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.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.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;
|
|
|
|
|
@@ -17,7 +18,6 @@ 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;
|
|
|
|
|
@@ -36,7 +36,6 @@ 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;
|
|
|
|
|
|
|
|
|
|
@@ -45,12 +44,7 @@ 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.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;
|
|
|
|
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
|
|
|
|
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
|
|
|
|
|
|
|
|
|
@@ -80,39 +74,37 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
private final RecordReaderFactory recordReaderFactory;
|
|
|
|
|
private final RecordWriterFactory recordWriterFactory;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
ContactExchangeManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
|
|
|
|
RecordReaderFactory recordReaderFactory,
|
|
|
|
|
RecordWriterFactory recordWriterFactory,
|
|
|
|
|
Clock clock, ConnectionManager connectionManager,
|
|
|
|
|
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.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 Author exchangeContacts(TransportId t,
|
|
|
|
|
public Contact exchangeContacts(TransportId t,
|
|
|
|
|
DuplexTransportConnection conn, SecretKey masterKey, boolean alice)
|
|
|
|
|
throws IOException, DbException {
|
|
|
|
|
// Get the transport connection's input and output streams
|
|
|
|
|
@@ -125,36 +117,26 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
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();
|
|
|
|
|
@@ -168,13 +150,17 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
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");
|
|
|
|
|
throw new FormatException();
|
|
|
|
|
}
|
|
|
|
|
@@ -183,30 +169,12 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
|
|
|
|
|
|
|
|
|
// Add the contact
|
|
|
|
|
ContactId contactId = addContact(remoteInfo.author, localAuthor,
|
|
|
|
|
Contact contact = addContact(remoteInfo.author, localAuthor,
|
|
|
|
|
masterKey, timestamp, alice, remoteInfo.properties);
|
|
|
|
|
// Reuse the connection as a transport connection
|
|
|
|
|
connectionManager.manageOutgoingConnection(contactId, t, conn);
|
|
|
|
|
// Pseudonym exchange succeeded
|
|
|
|
|
LOG.info("Pseudonym exchange succeeded");
|
|
|
|
|
return remoteInfo.author;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] sign(LocalAuthor author, byte[] nonce) {
|
|
|
|
|
try {
|
|
|
|
|
return crypto.sign(SIGNING_LABEL, 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, 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,
|
|
|
|
|
@@ -239,7 +207,7 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
return new ContactInfo(author, properties, signature, timestamp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ContactId addContact(Author remoteAuthor, LocalAuthor localAuthor,
|
|
|
|
|
private Contact addContact(Author remoteAuthor, LocalAuthor localAuthor,
|
|
|
|
|
SecretKey masterKey, long timestamp, boolean alice,
|
|
|
|
|
Map<TransportId, TransportProperties> remoteProperties)
|
|
|
|
|
throws DbException {
|
|
|
|
|
@@ -249,7 +217,7 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
true, true);
|
|
|
|
|
transportPropertyManager.addRemoteProperties(txn, contactId,
|
|
|
|
|
remoteProperties);
|
|
|
|
|
return contactId;
|
|
|
|
|
return contactManager.getContact(txn, contactId);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|