Add handshake key pairs to DB, remove inactive contacts.

This commit is contained in:
akwizgran
2019-04-18 13:15:25 +01:00
parent dcebd5a81c
commit 0587fdc54c
44 changed files with 407 additions and 532 deletions

View File

@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
@@ -72,7 +71,7 @@ class ContactManagerImpl implements ContactManager {
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
ContactId c = db.addContact(txn, remote, local, verified);
keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
@@ -81,8 +80,8 @@ class ContactManagerImpl implements ContactManager {
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
boolean verified) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
@@ -165,12 +164,8 @@ class ContactManagerImpl implements ContactManager {
}
@Override
public Collection<Contact> getActiveContacts() throws DbException {
Collection<Contact> contacts =
db.transactionWithResult(true, db::getContacts);
List<Contact> active = new ArrayList<>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c);
return active;
public Collection<Contact> getContacts() throws DbException {
return db.transactionWithResult(true, db::getContacts);
}
@Override
@@ -178,12 +173,6 @@ class ContactManagerImpl implements ContactManager {
db.transaction(false, txn -> removeContact(txn, c));
}
@Override
public void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException {
db.setContactActive(txn, c, active);
}
@Override
public void setContactAlias(Transaction txn, ContactId c,
@Nullable String alias) throws DbException {

View File

@@ -90,8 +90,8 @@ interface Database<T> {
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
*/
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified,
boolean active) throws DbException;
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified)
throws DbException;
/**
* Stores a group.
@@ -672,12 +672,6 @@ interface Database<T> {
*/
void setContactVerified(T txn, ContactId c) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(T txn, ContactId c, boolean active)
throws DbException;
/**
* Sets an alias name for a contact.
*/

View File

@@ -6,7 +6,6 @@ import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.CommitAction;
@@ -234,7 +233,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean verified, boolean active)
AuthorId local, boolean verified)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
@@ -244,9 +243,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new ContactExistsException();
if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException();
ContactId c = db.addContact(txn, remote, local, verified, active);
transaction.attach(new ContactAddedEvent(c, active));
if (active) transaction.attach(new ContactStatusChangedEvent(c, true));
ContactId c = db.addContact(txn, remote, local, verified);
transaction.attach(new ContactAddedEvent(c));
return c;
}
@@ -969,17 +967,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactVerifiedEvent(c));
}
@Override
public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setContactActive(txn, c, active);
transaction.attach(new ContactStatusChangedEvent(c, active));
}
@Override
public void setContactAlias(Transaction transaction, ContactId c,
@Nullable String alias) throws DbException {

View File

@@ -44,7 +44,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -64,6 +63,7 @@ import javax.annotation.Nullable;
import static java.sql.Types.BINARY;
import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR;
import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
@@ -113,6 +113,8 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " name _STRING NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " privateKey _BINARY NOT NULL,"
+ " handshakePublicKey _BINARY," // Null if not generated
+ " handshakePrivateKey _BINARY," // Null if not generated
+ " created BIGINT NOT NULL,"
+ " PRIMARY KEY (authorId))";
@@ -122,11 +124,11 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " authorId _HASH NOT NULL,"
+ " formatVersion INT NOT NULL,"
+ " name _STRING NOT NULL,"
+ " alias _STRING," // Null if no alias exists
+ " alias _STRING," // Null if no alias has been set
+ " publicKey _BINARY NOT NULL,"
+ " handshakePublicKey _BINARY," // Null if key is unknown
+ " localAuthorId _HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL,"
+ " active BOOLEAN NOT NULL,"
+ " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (localAuthorId)"
+ " REFERENCES localAuthors (authorId)"
@@ -479,11 +481,12 @@ abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
List<Migration<Connection>> getMigrations() {
return Arrays.asList(
return asList(
new Migration38_39(),
new Migration39_40(),
new Migration40_41(dbTypes),
new Migration41_42(dbTypes)
new Migration41_42(dbTypes),
new Migration42_43(dbTypes)
);
}
@@ -660,16 +663,15 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override
public ContactId addContact(Connection txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException {
boolean verified) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Create a contact row
String sql = "INSERT INTO contacts"
+ " (authorId, formatVersion, name, publicKey,"
+ " localAuthorId,"
+ " verified, active)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
+ " localAuthorId, verified)"
+ " VALUES (?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getId().getBytes());
ps.setInt(2, remote.getFormatVersion());
@@ -677,7 +679,6 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(4, remote.getPublicKey());
ps.setBytes(5, local.getBytes());
ps.setBoolean(6, verified);
ps.setBoolean(7, active);
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -878,16 +879,20 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO localAuthors"
+ " (authorId, formatVersion, name, publicKey,"
+ " privateKey, created)"
+ " VALUES (?, ?, ?, ?, ?, ?)";
+ " (authorId, formatVersion, name, publicKey, privateKey,"
+ " handshakePublicKey, handshakePrivateKey, created)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getId().getBytes());
ps.setInt(2, a.getFormatVersion());
ps.setString(3, a.getName());
ps.setBytes(4, a.getPublicKey());
ps.setBytes(5, a.getPrivateKey());
ps.setLong(6, a.getTimeCreated());
if (a.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
else ps.setBytes(6, a.getHandshakePublicKey());
if (a.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
else ps.setBytes(7, a.getHandshakePrivateKey());
ps.setLong(8, a.getTimeCreated());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -1427,7 +1432,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
String sql = "SELECT authorId, formatVersion, name, alias,"
+ " publicKey, localAuthorId, verified, active"
+ " publicKey, handshakePublicKey, localAuthorId, verified"
+ " FROM contacts"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
@@ -1439,15 +1444,15 @@ abstract class JdbcDatabase implements Database<Connection> {
String name = rs.getString(3);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
rs.close();
ps.close();
Author author =
new Author(authorId, formatVersion, name, publicKey);
return new Contact(c, author, localAuthorId, alias, verified,
active);
return new Contact(c, author, localAuthorId, alias,
handshakePublicKey, verified);
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
@@ -1456,13 +1461,13 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public Collection<Contact> getContacts(Connection txn)
throws DbException {
public Collection<Contact> getContacts(Connection txn) throws DbException {
Statement s = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " alias, publicKey, localAuthorId, verified, active"
+ " alias, publicKey, handshakePublicKey, localAuthorId,"
+ " verified"
+ " FROM contacts";
s = txn.createStatement();
rs = s.executeQuery(sql);
@@ -1474,13 +1479,13 @@ abstract class JdbcDatabase implements Database<Connection> {
String name = rs.getString(4);
String alias = rs.getString(5);
byte[] publicKey = rs.getBytes(6);
byte[] handshakePublicKey = rs.getBytes(7);
AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
boolean verified = rs.getBoolean(9);
Author author =
new Author(authorId, formatVersion, name, publicKey);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
boolean active = rs.getBoolean(9);
contacts.add(new Contact(contactId, author, localAuthorId,
alias, verified, active));
alias, handshakePublicKey, verified));
}
rs.close();
s.close();
@@ -1522,7 +1527,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
String sql = "SELECT contactId, formatVersion, name, alias,"
+ " publicKey, localAuthorId, verified, active"
+ " publicKey, handshakePublicKey, localAuthorId, verified"
+ " FROM contacts"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
@@ -1530,18 +1535,18 @@ abstract class JdbcDatabase implements Database<Connection> {
rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<>();
while (rs.next()) {
ContactId c = new ContactId(rs.getInt(1));
ContactId contactId = new ContactId(rs.getInt(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
Author author =
new Author(remote, formatVersion, name, publicKey);
contacts.add(new Contact(c, author, localAuthorId, alias,
verified, active));
contacts.add(new Contact(contactId, author, localAuthorId,
alias, handshakePublicKey, verified));
}
rs.close();
ps.close();
@@ -1661,8 +1666,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT formatVersion, name, publicKey,"
+ " privateKey, created"
String sql = "SELECT formatVersion, name, publicKey, privateKey,"
+ " handshakePublicKey, handshakePrivateKey, created"
+ " FROM localAuthors"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
@@ -1673,9 +1678,12 @@ abstract class JdbcDatabase implements Database<Connection> {
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
byte[] handshakePublicKey = rs.getBytes(5);
byte[] handshakePrivateKey = rs.getBytes(6);
long created = rs.getLong(5);
LocalAuthor localAuthor = new LocalAuthor(a, formatVersion, name,
publicKey, privateKey, created);
publicKey, privateKey, handshakePublicKey,
handshakePrivateKey, created);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
@@ -3119,24 +3127,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void setContactActive(Connection txn, ContactId c, boolean active)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET active = ? WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBoolean(1, active);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void setContactAlias(Connection txn, ContactId c,
@Nullable String alias) throws DbException {

View File

@@ -0,0 +1,52 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration42_43 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration42_43.class.getName());
private final DatabaseTypes dbTypes;
Migration42_43(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 42;
}
@Override
public int getEndVersion() {
return 43;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePrivateKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE contacts"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE contacts"
+ " DROP COLUMN active"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
@@ -80,12 +80,10 @@ class Poller implements EventListener {
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) {
// Connect to the newly activated contact
connectToContact(c.getContactId());
}
if (e instanceof ContactAddedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e;
// Connect to the newly activated contact
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased

View File

@@ -199,8 +199,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private TransportProperties getRemoteProperties(Transaction txn, Contact c,
TransportId t) throws DbException {
// Don't return properties for inactive contacts
if (!c.isActive()) return new TransportProperties();
Group g = getContactGroup(c);
try {
// Find the latest remote update

View File

@@ -1,9 +1,7 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -46,7 +44,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final Executor dbExecutor;
private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory;
private final Map<ContactId, Boolean> activeContacts;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
private final AtomicBoolean used = new AtomicBoolean(false);
@@ -58,8 +55,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory;
// Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<>();
managers = new ConcurrentHashMap<>();
}
@@ -73,8 +68,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
transports.put(f.getId(), f.getMaxLatency());
try {
db.transaction(false, txn -> {
for (Contact c : db.getContacts(txn))
if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : transports.entrySet())
db.addTransport(txn, e.getKey(), e.getValue());
for (Entry<TransportId, Integer> e : transports.entrySet()) {
@@ -130,8 +123,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override
public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException {
// Don't allow outgoing streams to inactive contacts
if (!activeContacts.containsKey(c)) return null;
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
@@ -157,15 +148,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
public void eventOccurred(Event e) {
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());
}
}
private void removeContact(ContactId c) {
activeContacts.remove(c);
dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values()) m.removeContact(c);
});