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

View File

@@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
}
@Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorID,
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, remoteAuthorID, localAuthorId);
return db.containsContact(txn, remoteAuthorId, localAuthorId);
}
@Override
public boolean contactExists(AuthorId remoteAuthorID,
public boolean contactExists(AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
boolean exists = false;
Transaction txn = db.startTransaction(true);
try {
exists = contactExists(txn, remoteAuthorID, localAuthorId);
exists = contactExists(txn, remoteAuthorId, localAuthorId);
txn.setComplete();
} finally {
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.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
@@ -28,22 +29,23 @@ public class ContactModule {
@Provides
@Singleton
ContactManager getContactManager(LifecycleManager lifecycleManager,
IdentityManager identityManager,
ContactManager getContactManager(IdentityManager identityManager,
ContactManagerImpl contactManager) {
identityManager.registerRemoveIdentityHook(contactManager);
return contactManager;
}
@Provides
ContactExchangeTask provideContactExchangeTask(
ContactExchangeTask provideContactExchangeTask(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory,
return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory,
bdfWriterFactory, clock, connectionManager, contactManager,
crypto, streamReaderFactory, streamWriterFactory);
transportPropertyManager, crypto, streamReaderFactory,
streamWriterFactory);
}
}