Exchange transport properties when adding contact.

This commit is contained in:
akwizgran
2016-04-06 13:10:24 +01:00
parent 7de83b5624
commit d38aa4fd9b
3 changed files with 127 additions and 42 deletions

View File

@@ -10,17 +10,22 @@ import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser; import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature; import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfReader;
import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriter; import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.ContactExistsException; import org.briarproject.api.db.ContactExistsException;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
@@ -29,13 +34,19 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
public class ContactExchangeTaskImpl extends Thread public class ContactExchangeTaskImpl extends Thread
implements ContactExchangeTask { implements ContactExchangeTask {
@@ -43,35 +54,40 @@ public class ContactExchangeTaskImpl extends Thread
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ContactExchangeTaskImpl.class.getName()); Logger.getLogger(ContactExchangeTaskImpl.class.getName());
private final DatabaseComponent db;
private final AuthorFactory authorFactory; private final AuthorFactory authorFactory;
private final BdfReaderFactory bdfReaderFactory; private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory; private final BdfWriterFactory bdfWriterFactory;
private final Clock clock; private final Clock clock;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ContactManager contactManager; private final ContactManager contactManager;
private final TransportPropertyManager transportPropertyManager;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final StreamReaderFactory streamReaderFactory; private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory; private final StreamWriterFactory streamWriterFactory;
private ContactExchangeListener listener; private volatile ContactExchangeListener listener;
private LocalAuthor localAuthor; private volatile LocalAuthor localAuthor;
private DuplexTransportConnection conn; private volatile DuplexTransportConnection conn;
private TransportId transportId; private volatile TransportId transportId;
private SecretKey masterSecret; private volatile SecretKey masterSecret;
private boolean alice; private volatile boolean alice;
public ContactExchangeTaskImpl(AuthorFactory authorFactory, public ContactExchangeTaskImpl(DatabaseComponent db,
BdfReaderFactory bdfReaderFactory, AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock, BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager, ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
this.db = db;
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.bdfReaderFactory = bdfReaderFactory; this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory; this.bdfWriterFactory = bdfWriterFactory;
this.clock = clock; this.clock = clock;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.contactManager = contactManager; this.contactManager = contactManager;
this.transportPropertyManager = transportPropertyManager;
this.crypto = crypto; this.crypto = crypto;
this.streamReaderFactory = streamReaderFactory; this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory; this.streamWriterFactory = streamWriterFactory;
@@ -93,24 +109,12 @@ public class ContactExchangeTaskImpl extends Thread
@Override @Override
public void run() { public void run() {
// Derive the header keys for the transport streams // Get the transport connection's input and output streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true); InputStream in;
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false); OutputStream out;
BdfReader r;
BdfWriter w;
try { try {
// Create the readers in = conn.getReader().getInputStream();
InputStream streamReader = out = conn.getWriter().getOutputStream();
streamReaderFactory.createInvitationStreamReader(
conn.getReader().getInputStream(),
alice ? bobHeaderKey : aliceHeaderKey);
r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createInvitationStreamWriter(
conn.getWriter().getOutputStream(),
alice ? aliceHeaderKey : bobHeaderKey);
w = bdfWriterFactory.createWriter(streamWriter);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed(); listener.contactExchangeFailed();
@@ -118,6 +122,32 @@ public class ContactExchangeTaskImpl extends Thread
return; return;
} }
// Get the local transport properties
Map<TransportId, TransportProperties> localProperties, remoteProperties;
try {
localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
return;
}
// Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
BdfReader r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createInvitationStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true); byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false); byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
@@ -130,13 +160,19 @@ public class ContactExchangeTaskImpl extends Thread
if (alice) { if (alice) {
sendPseudonym(w, aliceNonce); sendPseudonym(w, aliceNonce);
sendTimestamp(w, localTimestamp); sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteAuthor = receivePseudonym(r, bobNonce); remoteAuthor = receivePseudonym(r, bobNonce);
remoteTimestamp = receiveTimestamp(r); remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
} else { } else {
remoteAuthor = receivePseudonym(r, aliceNonce); remoteAuthor = receivePseudonym(r, aliceNonce);
remoteTimestamp = receiveTimestamp(r); remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendPseudonym(w, bobNonce); sendPseudonym(w, bobNonce);
sendTimestamp(w, localTimestamp); sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
} }
// Close the outgoing stream and expect EOF on the incoming stream // Close the outgoing stream and expect EOF on the incoming stream
w.close(); w.close();
@@ -159,7 +195,7 @@ public class ContactExchangeTaskImpl extends Thread
try { try {
// Add the contact // Add the contact
ContactId contactId = addContact(remoteAuthor, masterSecret, ContactId contactId = addContact(remoteAuthor, masterSecret,
timestamp, alice); timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection // Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId, connectionManager.manageOutgoingConnection(contactId, transportId,
conn); conn);
@@ -187,19 +223,22 @@ public class ContactExchangeTaskImpl extends Thread
signature.update(nonce); signature.update(nonce);
byte[] sig = signature.sign(); byte[] sig = signature.sign();
// Write the name, public key and signature // Write the name, public key and signature
w.writeListStart();
w.writeString(localAuthor.getName()); w.writeString(localAuthor.getName());
w.writeRaw(localAuthor.getPublicKey()); w.writeRaw(localAuthor.getPublicKey());
w.writeRaw(sig); w.writeRaw(sig);
w.flush(); w.writeListEnd();
LOG.info("Sent pseudonym"); LOG.info("Sent pseudonym");
} }
private Author receivePseudonym(BdfReader r, byte[] nonce) private Author receivePseudonym(BdfReader r, byte[] nonce)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException {
// Read the name, public key and signature // Read the name, public key and signature
r.readListStart();
String name = r.readString(MAX_AUTHOR_NAME_LENGTH); String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH); byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
r.readListEnd();
LOG.info("Received pseudonym"); LOG.info("Received pseudonym");
// Verify the signature // Verify the signature
Signature signature = crypto.getSignature(); Signature signature = crypto.getSignature();
@@ -217,7 +256,6 @@ public class ContactExchangeTaskImpl extends Thread
private void sendTimestamp(BdfWriter w, long timestamp) private void sendTimestamp(BdfWriter w, long timestamp)
throws IOException { throws IOException {
w.writeLong(timestamp); w.writeLong(timestamp);
w.flush();
LOG.info("Sent timestamp"); LOG.info("Sent timestamp");
} }
@@ -228,11 +266,56 @@ public class ContactExchangeTaskImpl extends Thread
return timestamp; return timestamp;
} }
private void sendTransportProperties(BdfWriter w,
Map<TransportId, TransportProperties> local) throws IOException {
w.writeListStart();
for (Entry<TransportId, TransportProperties> e : local.entrySet())
w.writeList(BdfList.of(e.getKey().getString(), e.getValue()));
w.writeListEnd();
}
private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote =
new HashMap<TransportId, TransportProperties>();
r.readListStart();
while (!r.hasListEnd()) {
r.readListStart();
String id = r.readString(MAX_TRANSPORT_ID_LENGTH);
if (id.isEmpty()) throw new FormatException();
TransportProperties p = new TransportProperties();
r.readDictionaryStart();
while (!r.hasDictionaryEnd()) {
if (p.size() == MAX_PROPERTIES_PER_TRANSPORT)
throw new FormatException();
String key = r.readString(MAX_PROPERTY_LENGTH);
String value = r.readString(MAX_PROPERTY_LENGTH);
p.put(key, value);
}
r.readDictionaryEnd();
r.readListEnd();
remote.put(new TransportId(id), p);
}
r.readListEnd();
return remote;
}
private ContactId addContact(Author remoteAuthor, SecretKey master, private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice) throws DbException { long timestamp, boolean alice,
// Add the contact to the database Map<TransportId, TransportProperties> remoteProperties)
return contactManager.addContact(remoteAuthor, localAuthor.getId(), throws DbException {
master, timestamp, alice, true); ContactId contactId;
Transaction txn = db.startTransaction(false);
try {
contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), master, timestamp, alice, true);
transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return contactId;
} }
private void tryToClose(DuplexTransportConnection conn, private void tryToClose(DuplexTransportConnection conn,

View File

@@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} }
@Override @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorID, public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, remoteAuthorID, localAuthorId); return db.containsContact(txn, remoteAuthorId, localAuthorId);
} }
@Override @Override
public boolean contactExists(AuthorId remoteAuthorID, public boolean contactExists(AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {
boolean exists = false; boolean exists = false;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
exists = contactExists(txn, remoteAuthorID, localAuthorId); exists = contactExists(txn, remoteAuthorId, localAuthorId);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);

View File

@@ -5,10 +5,11 @@ import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
@@ -28,22 +29,23 @@ public class ContactModule {
@Provides @Provides
@Singleton @Singleton
ContactManager getContactManager(LifecycleManager lifecycleManager, ContactManager getContactManager(IdentityManager identityManager,
IdentityManager identityManager,
ContactManagerImpl contactManager) { ContactManagerImpl contactManager) {
identityManager.registerRemoveIdentityHook(contactManager); identityManager.registerRemoveIdentityHook(contactManager);
return contactManager; return contactManager;
} }
@Provides @Provides
ContactExchangeTask provideContactExchangeTask( ContactExchangeTask provideContactExchangeTask(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory, AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock, BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager, ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory, return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory,
bdfWriterFactory, clock, connectionManager, contactManager, bdfWriterFactory, clock, connectionManager, contactManager,
crypto, streamReaderFactory, streamWriterFactory); transportPropertyManager, crypto, streamReaderFactory,
streamWriterFactory);
} }
} }