mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Store each connection window slot as a database row.
This is less memory-efficient but necessary for the coming forward secrecy changes.
This commit is contained in:
@@ -18,6 +18,5 @@ public class CryptoModule extends AbstractModule {
|
||||
// FIXME: Use a real key
|
||||
bind(SecretKey.class).annotatedWith(SecretStorageKey.class).toInstance(
|
||||
new SecretKeySpec(new byte[32], "AES"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import net.sf.briar.api.transport.ConnectionWindow;
|
||||
* obtained by calling startTransaction(). Every transaction must be
|
||||
* terminated by calling either abortTransaction() or commitTransaction(),
|
||||
* even if an exception is thrown.
|
||||
*
|
||||
* <p>
|
||||
* Locking is provided by the DatabaseComponent implementation. To prevent
|
||||
* deadlock, locks must be acquired in the following order:
|
||||
* <ul>
|
||||
|
||||
@@ -1597,7 +1597,7 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given message (and all associated state) from the database.
|
||||
* Removes the given message (and all associated state) from the database.
|
||||
* <p>
|
||||
* Locking: contact read, message write, messageStatus write.
|
||||
*/
|
||||
|
||||
@@ -214,14 +214,21 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_CONNECTIONS =
|
||||
"CREATE TABLE connections"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " index INT NOT NULL,"
|
||||
+ " outgoing BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, index),"
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_CONNECTION_WINDOWS =
|
||||
"CREATE TABLE connectionWindows"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " index INT NOT NULL,"
|
||||
+ " centre BIGINT NOT NULL,"
|
||||
+ " bitmap INT NOT NULL,"
|
||||
+ " outgoing BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, index),"
|
||||
+ " unseen BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, index, unseen),"
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
@@ -342,6 +349,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONNECTIONS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
|
||||
@@ -895,19 +903,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT outgoing FROM connectionWindows"
|
||||
String sql = "SELECT outgoing FROM connections"
|
||||
+ " WHERE contactId = ? AND index = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
rs = ps.executeQuery();
|
||||
if(rs.next()) {
|
||||
// A connection window row exists - update it
|
||||
// A connection row exists - update it
|
||||
long outgoing = rs.getLong(1);
|
||||
if(rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
sql = "UPDATE connectionWindows SET outgoing = ?"
|
||||
sql = "UPDATE connections SET outgoing = ?"
|
||||
+ " WHERE contactId = ? AND index = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, outgoing + 1);
|
||||
@@ -918,12 +926,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.close();
|
||||
return outgoing;
|
||||
} else {
|
||||
// No connection window row exists - create one
|
||||
// No connection row exists - create one
|
||||
rs.close();
|
||||
ps.close();
|
||||
sql = "INSERT INTO connectionWindows"
|
||||
+ " (contactId, index, centre, bitmap, outgoing)"
|
||||
+ " VALUES(?, ?, ZERO(), ZERO(), ZERO())";
|
||||
sql = "INSERT INTO connections (contactId, index, outgoing)"
|
||||
+ " VALUES(?, ?, ZERO())";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
@@ -944,23 +951,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT centre, bitmap FROM connectionWindows"
|
||||
String sql = "SELECT unseen FROM connectionWindows"
|
||||
+ " WHERE contactId = ? AND index = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
rs = ps.executeQuery();
|
||||
long centre = 0L;
|
||||
int bitmap = 0;
|
||||
if(rs.next()) {
|
||||
centre = rs.getLong(1);
|
||||
bitmap = rs.getInt(2);
|
||||
if(rs.next()) throw new DbStateException();
|
||||
}
|
||||
Collection<Long> unseen = new ArrayList<Long>();
|
||||
while(rs.next()) unseen.add(rs.getLong(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return connectionWindowFactory.createConnectionWindow(centre,
|
||||
bitmap);
|
||||
if(unseen.isEmpty())
|
||||
return connectionWindowFactory.createConnectionWindow();
|
||||
else return connectionWindowFactory.createConnectionWindow(unseen);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
@@ -2154,46 +2157,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
public void setConnectionWindow(Connection txn, ContactId c,
|
||||
TransportIndex i, ConnectionWindow w) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT NULL FROM connectionWindows"
|
||||
// Delete any existing connection window
|
||||
String sql = "DELETE FROM connectionWindows"
|
||||
+ " WHERE contactId = ? AND index = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
if(rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
if(found) {
|
||||
// A connection window row exists - update it
|
||||
sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?"
|
||||
+ " WHERE contactId = ? AND index = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, w.getCentre());
|
||||
ps.setInt(2, w.getBitmap());
|
||||
ps.setInt(3, c.getInt());
|
||||
ps.setInt(4, i.getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} else {
|
||||
// No connection window row exists - create one
|
||||
sql = "INSERT INTO connectionWindows"
|
||||
+ " (contactId, index, centre, bitmap, outgoing)"
|
||||
+ " VALUES(?, ?, ?, ?, ZERO())";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
ps.setLong(3, w.getCentre());
|
||||
ps.setInt(4, w.getBitmap());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Store the new connection window
|
||||
sql = "INSERT INTO connectionWindows (contactId, index, unseen)"
|
||||
+ " VALUES(?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, i.getInt());
|
||||
Collection<Long> unseen = w.getUnseen();
|
||||
for(long l : unseen) {
|
||||
ps.setLong(3, l);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] affectedBatch = ps.executeBatch();
|
||||
if(affectedBatch.length != unseen.size())
|
||||
throw new DbStateException();
|
||||
for(int j = 0; j < affectedBatch.length; j++) {
|
||||
if(affectedBatch[j] != 1) throw new DbStateException();
|
||||
}
|
||||
ps.close();
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class ReaderImpl implements Reader {
|
||||
objectReaders.length);
|
||||
objectReaders = newObjectReaders;
|
||||
}
|
||||
objectReaders[id] = o;
|
||||
objectReaders[id] = o;
|
||||
}
|
||||
|
||||
public void removeObjectReader(int id) {
|
||||
|
||||
@@ -88,7 +88,7 @@ DatabaseListener {
|
||||
private synchronized void calculateIvs(ContactId c, TransportId t,
|
||||
TransportIndex i, SecretKey ivKey, ConnectionWindow w)
|
||||
throws DbException {
|
||||
for(Long unseen : w.getUnseenConnectionNumbers()) {
|
||||
for(Long unseen : w.getUnseen()) {
|
||||
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
|
||||
expected.put(iv, new ConnectionContextImpl(c, t, i, unseen));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||
|
||||
class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
|
||||
|
||||
public ConnectionWindow createConnectionWindow(long centre, int bitmap) {
|
||||
return new ConnectionWindowImpl(centre, bitmap);
|
||||
public ConnectionWindow createConnectionWindow() {
|
||||
return new ConnectionWindowImpl();
|
||||
}
|
||||
|
||||
public ConnectionWindow createConnectionWindow(Collection<Long> unseen) {
|
||||
return new ConnectionWindowImpl(unseen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,74 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.CONNECTION_WINDOW_SIZE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
class ConnectionWindowImpl implements ConnectionWindow {
|
||||
|
||||
private final Set<Long> unseen;
|
||||
|
||||
private long centre;
|
||||
private int bitmap;
|
||||
|
||||
ConnectionWindowImpl(long centre, int bitmap) {
|
||||
this.centre = centre;
|
||||
this.bitmap = bitmap;
|
||||
ConnectionWindowImpl() {
|
||||
unseen = new TreeSet<Long>();
|
||||
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) unseen.add(l);
|
||||
centre = 0;
|
||||
}
|
||||
|
||||
public long getCentre() {
|
||||
return centre;
|
||||
}
|
||||
|
||||
public int getBitmap() {
|
||||
return bitmap;
|
||||
ConnectionWindowImpl(Collection<Long> unseen) {
|
||||
long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
|
||||
for(long l : unseen) {
|
||||
if(l < 0 || l > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(l < min) min = l;
|
||||
if(l > max) max = l;
|
||||
}
|
||||
if(max - min > CONNECTION_WINDOW_SIZE)
|
||||
throw new IllegalArgumentException();
|
||||
this.unseen = new TreeSet<Long>(unseen);
|
||||
centre = max - CONNECTION_WINDOW_SIZE / 2 + 1;
|
||||
for(long l = centre; l <= max; l++) {
|
||||
if(!this.unseen.contains(l)) throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSeen(long connection) {
|
||||
int offset = getOffset(connection);
|
||||
int mask = 0x80000000 >>> offset;
|
||||
return (bitmap & mask) != 0;
|
||||
}
|
||||
|
||||
private int getOffset(long connection) {
|
||||
if(connection < 0L) throw new IllegalArgumentException();
|
||||
if(connection > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
int offset = (int) (connection - centre) + 16;
|
||||
if(offset < 0 || offset > 31) throw new IllegalArgumentException();
|
||||
return offset;
|
||||
return !unseen.contains(connection);
|
||||
}
|
||||
|
||||
public void setSeen(long connection) {
|
||||
int offset = getOffset(connection);
|
||||
int mask = 0x80000000 >>> offset;
|
||||
if((bitmap & mask) != 0) throw new IllegalArgumentException();
|
||||
bitmap |= mask;
|
||||
// If the new connection number is above the centre, slide the window
|
||||
long bottom = getBottom(centre);
|
||||
long top = getTop(centre);
|
||||
if(connection < bottom || connection > top)
|
||||
throw new IllegalArgumentException();
|
||||
if(!unseen.remove(connection)) throw new IllegalArgumentException();
|
||||
if(connection >= centre) {
|
||||
centre = connection + 1;
|
||||
bitmap <<= offset - 16 + 1;
|
||||
long newBottom = getBottom(centre);
|
||||
long newTop = getTop(centre);
|
||||
for(long l = bottom; l < newBottom; l++) unseen.remove(l);
|
||||
for(long l = top + 1; l <= newTop; l++) unseen.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Long> getUnseenConnectionNumbers() {
|
||||
Collection<Long> unseen = new ArrayList<Long>();
|
||||
for(int i = 0; i < 32; i++) {
|
||||
int mask = 0x80000000 >>> i;
|
||||
if((bitmap & mask) == 0) {
|
||||
long c = centre - 16 + i;
|
||||
if(c >= 0L && c <= MAX_32_BIT_UNSIGNED) unseen.add(c);
|
||||
}
|
||||
}
|
||||
// The centre of the window should be an unseen value unless the
|
||||
// maximum possible value has been seen
|
||||
assert unseen.contains(centre) || centre == MAX_32_BIT_UNSIGNED + 1;
|
||||
// Returns the lowest value contained in a window with the given centre
|
||||
private long getBottom(long centre) {
|
||||
return Math.max(0, centre - CONNECTION_WINDOW_SIZE / 2);
|
||||
}
|
||||
|
||||
// Returns the highest value contained in a window with the given centre
|
||||
private long getTop(long centre) {
|
||||
return Math.min(ByteUtils.MAX_32_BIT_UNSIGNED,
|
||||
centre + CONNECTION_WINDOW_SIZE / 2 - 1);
|
||||
}
|
||||
|
||||
public Collection<Long> getUnseen() {
|
||||
return unseen;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user