Merge branch 'embargo-inactive-contacts' into 'master'

Don't communicate with inactive contacts

When we're introduced to a new contact, we need to store the transport keys and start key rotation before activating the contact. During this period we shouldn't use the transport keys for outgoing streams until either the introduction protocol is complete or we receive an incoming stream from the contact, indicating that they've also started key rotation.

See merge request !106
This commit is contained in:
akwizgran
2016-02-18 15:58:05 +00:00
3 changed files with 70 additions and 2 deletions

View File

@@ -1,6 +1,7 @@
package org.briarproject.transport;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
@@ -9,6 +10,7 @@ import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportAddedEvent;
@@ -19,6 +21,7 @@ import org.briarproject.api.system.Timer;
import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamContext;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -39,6 +42,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final ExecutorService dbExecutor;
private final Timer timer;
private final Clock clock;
private final Map<ContactId, Boolean> activeContacts;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
@Inject
@@ -50,20 +54,26 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.dbExecutor = dbExecutor;
this.timer = timer;
this.clock = clock;
// Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<ContactId, Boolean>();
managers = new ConcurrentHashMap<TransportId, TransportKeyManager>();
}
@Override
public boolean start() {
try {
Collection<Contact> contacts;
Map<TransportId, Integer> latencies;
Transaction txn = db.startTransaction();
try {
contacts = db.getContacts(txn);
latencies = db.getTransportLatencies(txn);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
for (Contact c : contacts)
if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : latencies.entrySet())
addTransport(e.getKey(), e.getValue());
} catch (DbException e) {
@@ -85,13 +95,33 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
}
public StreamContext getStreamContext(ContactId c, TransportId t) {
// Don't allow outgoing streams to inactive contacts
if (!activeContacts.containsKey(c)) return null;
TransportKeyManager m = managers.get(t);
return m == null ? null : m.getStreamContext(c);
}
public StreamContext getStreamContext(TransportId t, byte[] tag) {
TransportKeyManager m = managers.get(t);
return m == null ? null : m.recogniseTag(tag);
if (m == null) return null;
StreamContext ctx = m.getStreamContext(tag);
if (ctx == null) return null;
// Activate the contact if not already active
if (!activeContacts.containsKey(ctx.getContactId())) {
try {
Transaction txn = db.startTransaction();
try {
db.setContactActive(txn, ctx.getContactId(), true);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
return ctx;
}
public void eventOccurred(Event e) {
@@ -102,6 +132,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
removeTransport(((TransportRemovedEvent) e).getTransportId());
} else if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) activeContacts.put(c.getContactId(), true);
else activeContacts.remove(c.getContactId());
}
}
@@ -121,6 +155,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
}
private void removeContact(final ContactId c) {
activeContacts.remove(c);
dbExecutor.execute(new Runnable() {
public void run() {
for (TransportKeyManager m : managers.values())

View File

@@ -207,7 +207,7 @@ class TransportKeyManager extends TimerTask {
}
}
StreamContext recogniseTag(byte[] tag) {
StreamContext getStreamContext(byte[] tag) {
lock.lock();
try {
// Look up the incoming keys for the tag

View File

@@ -4,6 +4,7 @@ import org.briarproject.BriarTestCase;
import org.briarproject.TestDatabaseConfig;
import org.briarproject.TestUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
@@ -1108,6 +1109,38 @@ public class H2DatabaseTest extends BriarTestCase {
db.close();
}
@Test
public void testSetContactActive() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
// The contact should be active
Contact contact = db.getContact(txn, contactId);
assertTrue(contact.isActive());
// Set the contact inactive
db.setContactActive(txn, contactId, false);
// The contact should be inactive
contact = db.getContact(txn, contactId);
assertFalse(contact.isActive());
// Set the contact active
db.setContactActive(txn, contactId, true);
// The contact should be active
contact = db.getContact(txn, contactId);
assertTrue(contact.isActive());
db.commitTransaction(txn);
db.close();
}
@Test
public void testExceptionHandling() throws Exception {
Database<Connection> db = open(false);