mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Add a method for binding transport keys to a contact.
This commit is contained in:
@@ -110,6 +110,12 @@ public interface DatabaseComponent {
|
|||||||
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
|
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
|
||||||
TransportKeys k) throws DbException;
|
TransportKeys k) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the given keys for the given transport to the given contact.
|
||||||
|
*/
|
||||||
|
void bindTransportKeys(Transaction txn, ContactId c, TransportId t,
|
||||||
|
KeySetId k) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact for the given
|
||||||
* local pseudonym.
|
* local pseudonym.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -31,9 +31,15 @@ public interface KeyManager {
|
|||||||
* Derives and stores a set of unbound transport keys for each transport
|
* Derives and stores a set of unbound transport keys for each transport
|
||||||
* and returns the key set IDs.
|
* and returns the key set IDs.
|
||||||
*/
|
*/
|
||||||
Collection<KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
|
Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
|
||||||
long timestamp, boolean alice) throws DbException;
|
long timestamp, boolean alice) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the given transport keys to the given contact.
|
||||||
|
*/
|
||||||
|
void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link StreamContext} for sending a stream to the given
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
* contact over the given transport, or null if an error occurs or the
|
* contact over the given transport, or null if an error occurs or the
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ interface Database<T> {
|
|||||||
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k)
|
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the given keys for the given transport to the given contact.
|
||||||
|
*/
|
||||||
|
void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact for the given
|
||||||
* local pseudonym.
|
* local pseudonym.
|
||||||
|
|||||||
@@ -245,6 +245,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.addTransportKeys(txn, c, k);
|
return db.addTransportKeys(txn, c, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindTransportKeys(Transaction transaction, ContactId c,
|
||||||
|
TransportId t, KeySetId k) throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsContact(txn, c))
|
||||||
|
throw new NoSuchContactException();
|
||||||
|
if (!db.containsTransport(txn, t))
|
||||||
|
throw new NoSuchTransportException();
|
||||||
|
db.bindTransportKeys(txn, c, t, k);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsContact(Transaction transaction, AuthorId remote,
|
public boolean containsContact(Transaction transaction, AuthorId remote,
|
||||||
AuthorId local) throws DbException {
|
AuthorId local) throws DbException {
|
||||||
|
|||||||
@@ -950,6 +950,33 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindTransportKeys(Connection txn, ContactId c, TransportId t,
|
||||||
|
KeySetId k) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
try {
|
||||||
|
String sql = "UPDATE outgoingKeys SET contactId = ?"
|
||||||
|
+ " WHERE keySetId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
ps.setInt(2, k.getInt());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if (affected < 0) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
sql = "UPDATE incomingKeys SET contactId = ?"
|
||||||
|
+ " WHERE keySetId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
ps.setInt(2, k.getInt());
|
||||||
|
affected = ps.executeUpdate();
|
||||||
|
if (affected < 0) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsContact(Connection txn, AuthorId remote,
|
public boolean containsContact(Connection txn, AuthorId remote,
|
||||||
AuthorId local) throws DbException {
|
AuthorId local) throws DbException {
|
||||||
@@ -2882,6 +2909,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Delete any existing outgoing keys - this will also remove any
|
// Delete any existing outgoing keys - this will also remove any
|
||||||
// incoming keys with the same key set ID
|
// incoming keys with the same key set ID
|
||||||
|
// TODO: Add an index to speed this up?
|
||||||
String sql = "DELETE FROM outgoingKeys WHERE keySetId = ?";
|
String sql = "DELETE FROM outgoingKeys WHERE keySetId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
for (KeySet ks : keys) {
|
for (KeySet ks : keys) {
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import org.briarproject.bramble.api.transport.KeyManager;
|
|||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -108,15 +106,32 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<KeySetId> addUnboundKeys(Transaction txn,
|
public Map<TransportId, KeySetId> addUnboundKeys(Transaction txn,
|
||||||
SecretKey master, long timestamp, boolean alice)
|
SecretKey master, long timestamp, boolean alice)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Collection<KeySetId> ids = new ArrayList<>();
|
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||||
for (TransportKeyManager m : managers.values())
|
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||||
ids.add(m.addUnboundKeys(txn, master, timestamp, alice));
|
TransportId t = e.getKey();
|
||||||
|
TransportKeyManager m = e.getValue();
|
||||||
|
ids.put(t, m.addUnboundKeys(txn, master, timestamp, alice));
|
||||||
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindKeys(Transaction txn, ContactId c,
|
||||||
|
Map<TransportId, KeySetId> keys) throws DbException {
|
||||||
|
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
|
||||||
|
TransportId t = e.getKey();
|
||||||
|
TransportKeyManager m = managers.get(t);
|
||||||
|
if (m == null) {
|
||||||
|
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||||
|
} else {
|
||||||
|
m.bindKeys(txn, c, e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamContext getStreamContext(ContactId c, TransportId t)
|
public StreamContext getStreamContext(ContactId c, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ interface TransportKeyManager {
|
|||||||
KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
|
KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
|
||||||
boolean alice) throws DbException;
|
boolean alice) throws DbException;
|
||||||
|
|
||||||
|
void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException;
|
||||||
|
|
||||||
void removeContact(ContactId c);
|
void removeContact(ContactId c);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -57,7 +56,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
private final ReentrantLock lock = new ReentrantLock();
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
// The following are locking: lock
|
// The following are locking: lock
|
||||||
private final Collection<MutableKeySet> keys = new ArrayList<>();
|
private final Map<KeySetId, MutableKeySet> keys = new HashMap<>();
|
||||||
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
||||||
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
|
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
|
||||||
|
|
||||||
@@ -123,7 +122,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
|
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
|
||||||
MutableTransportKeys m) {
|
MutableTransportKeys m) {
|
||||||
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
|
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
|
||||||
keys.add(ks);
|
keys.put(keySetId, ks);
|
||||||
if (contactId != null) {
|
if (contactId != null) {
|
||||||
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
||||||
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
||||||
@@ -204,22 +203,34 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindKeys(Transaction txn, ContactId c, KeySetId k)
|
||||||
|
throws DbException {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
MutableKeySet ks = keys.get(k);
|
||||||
|
if (ks == null) throw new IllegalArgumentException();
|
||||||
|
if (ks.getContactId() != null) throw new IllegalArgumentException();
|
||||||
|
MutableTransportKeys m = ks.getTransportKeys();
|
||||||
|
addKeys(k, c, m);
|
||||||
|
db.bindTransportKeys(txn, c, m.getTransportId(), k);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeContact(ContactId c) {
|
public void removeContact(ContactId c) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// Remove mutable state for the contact
|
// Remove mutable state for the contact
|
||||||
Iterator<Entry<Bytes, TagContext>> inContextsIter =
|
Iterator<TagContext> it = inContexts.values().iterator();
|
||||||
inContexts.entrySet().iterator();
|
while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove();
|
||||||
while (inContextsIter.hasNext()) {
|
|
||||||
ContactId c1 = inContextsIter.next().getValue().contactId;
|
|
||||||
if (c1.equals(c)) inContextsIter.remove();
|
|
||||||
}
|
|
||||||
outContexts.remove(c);
|
outContexts.remove(c);
|
||||||
Iterator<MutableKeySet> keysIter = keys.iterator();
|
Iterator<MutableKeySet> it1 = keys.values().iterator();
|
||||||
while (keysIter.hasNext()) {
|
while (it1.hasNext()) {
|
||||||
ContactId c1 = keysIter.next().getContactId();
|
ContactId c1 = it1.next().getContactId();
|
||||||
if (c1 != null && c1.equals(c)) keysIter.remove();
|
if (c1 != null && c1.equals(c)) it1.remove();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
@@ -300,7 +311,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
try {
|
try {
|
||||||
// Rotate the keys to the current rotation period
|
// Rotate the keys to the current rotation period
|
||||||
Collection<KeySet> snapshot = new ArrayList<>(keys.size());
|
Collection<KeySet> snapshot = new ArrayList<>(keys.size());
|
||||||
for (MutableKeySet ks : keys) {
|
for (MutableKeySet ks : keys.values()) {
|
||||||
snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(),
|
snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(),
|
||||||
ks.getTransportKeys().snapshot()));
|
ks.getTransportKeys().snapshot()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,11 +284,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the contact is in the DB (which it's not)
|
// Check whether the contact is in the DB (which it's not)
|
||||||
exactly(17).of(database).startTransaction();
|
exactly(18).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(17).of(database).containsContact(txn, contactId);
|
exactly(18).of(database).containsContact(txn, contactId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(17).of(database).abortTransaction(txn);
|
exactly(18).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
@@ -303,6 +303,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction = db.startTransaction(false);
|
||||||
|
try {
|
||||||
|
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchContactException expected) {
|
||||||
|
// Expected
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
transaction = db.startTransaction(false);
|
transaction = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.generateAck(transaction, contactId, 123);
|
db.generateAck(transaction, contactId, 123);
|
||||||
@@ -768,13 +778,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
// endTransaction()
|
// endTransaction()
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
// Check whether the transport is in the DB (which it's not)
|
// Check whether the transport is in the DB (which it's not)
|
||||||
exactly(4).of(database).startTransaction();
|
exactly(5).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
oneOf(database).containsContact(txn, contactId);
|
exactly(2).of(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
exactly(4).of(database).containsTransport(txn, transportId);
|
exactly(5).of(database).containsTransport(txn, transportId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(4).of(database).abortTransaction(txn);
|
exactly(5).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
@@ -789,6 +799,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction = db.startTransaction(false);
|
||||||
|
try {
|
||||||
|
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchTransportException expected) {
|
||||||
|
// Expected
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
transaction = db.startTransaction(false);
|
transaction = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.getTransportKeys(transaction, transportId);
|
db.getTransportKeys(transaction, transportId);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import java.util.Collection;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
@@ -122,7 +123,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(keySetId));
|
will(returnValue(keySetId));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(singletonList(keySetId),
|
assertEquals(singletonMap(transportId, keySetId),
|
||||||
keyManager.addUnboundKeys(txn, secretKey, timestamp, alice));
|
keyManager.addUnboundKeys(txn, secretKey, timestamp, alice));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -449,8 +449,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
oneOf(db).updateTransportKeys(txn1, asList(
|
oneOf(db).updateTransportKeys(txn1, asList(
|
||||||
new KeySet(keySetId, contactId, rotated),
|
new KeySet(keySetId1, null, rotated1),
|
||||||
new KeySet(keySetId1, null, rotated1)
|
new KeySet(keySetId, contactId, rotated)
|
||||||
));
|
));
|
||||||
// Schedule key rotation at the start of the next rotation period
|
// Schedule key rotation at the start of the next rotation period
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||||
|
|||||||
Reference in New Issue
Block a user