mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +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:
@@ -48,4 +48,7 @@ public interface ProtocolConstants {
|
|||||||
|
|
||||||
/** The length of a message's random salt in bytes. */
|
/** The length of a message's random salt in bytes. */
|
||||||
static final int SALT_LENGTH = 8;
|
static final int SALT_LENGTH = 8;
|
||||||
|
|
||||||
|
/** The size of the connection reordering window. */
|
||||||
|
static final int CONNECTION_WINDOW_SIZE = 32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ import java.util.Collection;
|
|||||||
|
|
||||||
public interface ConnectionWindow {
|
public interface ConnectionWindow {
|
||||||
|
|
||||||
long getCentre();
|
|
||||||
|
|
||||||
int getBitmap();
|
|
||||||
|
|
||||||
boolean isSeen(long connection);
|
boolean isSeen(long connection);
|
||||||
|
|
||||||
void setSeen(long connection);
|
void setSeen(long connection);
|
||||||
|
|
||||||
Collection<Long> getUnseenConnectionNumbers();
|
Collection<Long> getUnseen();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package net.sf.briar.api.transport;
|
package net.sf.briar.api.transport;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
public interface ConnectionWindowFactory {
|
public interface ConnectionWindowFactory {
|
||||||
|
|
||||||
ConnectionWindow createConnectionWindow(long centre, int bitmap);
|
ConnectionWindow createConnectionWindow();
|
||||||
|
|
||||||
|
ConnectionWindow createConnectionWindow(Collection<Long> unseen);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import net.sf.briar.api.protocol.TransportIndex;
|
|||||||
|
|
||||||
public interface StreamConnectionFactory {
|
public interface StreamConnectionFactory {
|
||||||
|
|
||||||
void createIncomingConnection(TransportIndex i, ContactId c,
|
void createIncomingConnection(TransportIndex i, ContactId c,
|
||||||
StreamTransportConnection s, byte[] encryptedIv);
|
StreamTransportConnection s, byte[] encryptedIv);
|
||||||
|
|
||||||
void createOutgoingConnection(TransportIndex i, ContactId c,
|
void createOutgoingConnection(TransportIndex i, ContactId c,
|
||||||
|
|||||||
@@ -18,6 +18,5 @@ public class CryptoModule extends AbstractModule {
|
|||||||
// FIXME: Use a real key
|
// FIXME: Use a real key
|
||||||
bind(SecretKey.class).annotatedWith(SecretStorageKey.class).toInstance(
|
bind(SecretKey.class).annotatedWith(SecretStorageKey.class).toInstance(
|
||||||
new SecretKeySpec(new byte[32], "AES"));
|
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
|
* obtained by calling startTransaction(). Every transaction must be
|
||||||
* terminated by calling either abortTransaction() or commitTransaction(),
|
* terminated by calling either abortTransaction() or commitTransaction(),
|
||||||
* even if an exception is thrown.
|
* even if an exception is thrown.
|
||||||
*
|
* <p>
|
||||||
* Locking is provided by the DatabaseComponent implementation. To prevent
|
* Locking is provided by the DatabaseComponent implementation. To prevent
|
||||||
* deadlock, locks must be acquired in the following order:
|
* deadlock, locks must be acquired in the following order:
|
||||||
* <ul>
|
* <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>
|
* <p>
|
||||||
* Locking: contact read, message write, messageStatus write.
|
* Locking: contact read, message write, messageStatus write.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -214,14 +214,21 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " 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 =
|
private static final String CREATE_CONNECTION_WINDOWS =
|
||||||
"CREATE TABLE connectionWindows"
|
"CREATE TABLE connectionWindows"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " index INT NOT NULL,"
|
+ " index INT NOT NULL,"
|
||||||
+ " centre BIGINT NOT NULL,"
|
+ " unseen BIGINT NOT NULL,"
|
||||||
+ " bitmap INT NOT NULL,"
|
+ " PRIMARY KEY (contactId, index, unseen),"
|
||||||
+ " outgoing BIGINT NOT NULL,"
|
|
||||||
+ " PRIMARY KEY (contactId, index),"
|
|
||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
@@ -342,6 +349,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS));
|
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS));
|
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS));
|
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS));
|
||||||
|
s.executeUpdate(insertTypeNames(CREATE_CONNECTIONS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
|
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
|
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
|
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
|
||||||
@@ -895,19 +903,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT outgoing FROM connectionWindows"
|
String sql = "SELECT outgoing FROM connections"
|
||||||
+ " WHERE contactId = ? AND index = ?";
|
+ " WHERE contactId = ? AND index = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, i.getInt());
|
ps.setInt(2, i.getInt());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
if(rs.next()) {
|
if(rs.next()) {
|
||||||
// A connection window row exists - update it
|
// A connection row exists - update it
|
||||||
long outgoing = rs.getLong(1);
|
long outgoing = rs.getLong(1);
|
||||||
if(rs.next()) throw new DbStateException();
|
if(rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
sql = "UPDATE connectionWindows SET outgoing = ?"
|
sql = "UPDATE connections SET outgoing = ?"
|
||||||
+ " WHERE contactId = ? AND index = ?";
|
+ " WHERE contactId = ? AND index = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setLong(1, outgoing + 1);
|
ps.setLong(1, outgoing + 1);
|
||||||
@@ -918,12 +926,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return outgoing;
|
return outgoing;
|
||||||
} else {
|
} else {
|
||||||
// No connection window row exists - create one
|
// No connection row exists - create one
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
sql = "INSERT INTO connectionWindows"
|
sql = "INSERT INTO connections (contactId, index, outgoing)"
|
||||||
+ " (contactId, index, centre, bitmap, outgoing)"
|
+ " VALUES(?, ?, ZERO())";
|
||||||
+ " VALUES(?, ?, ZERO(), ZERO(), ZERO())";
|
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, i.getInt());
|
ps.setInt(2, i.getInt());
|
||||||
@@ -944,23 +951,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT centre, bitmap FROM connectionWindows"
|
String sql = "SELECT unseen FROM connectionWindows"
|
||||||
+ " WHERE contactId = ? AND index = ?";
|
+ " WHERE contactId = ? AND index = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, i.getInt());
|
ps.setInt(2, i.getInt());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
long centre = 0L;
|
Collection<Long> unseen = new ArrayList<Long>();
|
||||||
int bitmap = 0;
|
while(rs.next()) unseen.add(rs.getLong(1));
|
||||||
if(rs.next()) {
|
|
||||||
centre = rs.getLong(1);
|
|
||||||
bitmap = rs.getInt(2);
|
|
||||||
if(rs.next()) throw new DbStateException();
|
|
||||||
}
|
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return connectionWindowFactory.createConnectionWindow(centre,
|
if(unseen.isEmpty())
|
||||||
bitmap);
|
return connectionWindowFactory.createConnectionWindow();
|
||||||
|
else return connectionWindowFactory.createConnectionWindow(unseen);
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs);
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
@@ -2154,46 +2157,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
public void setConnectionWindow(Connection txn, ContactId c,
|
public void setConnectionWindow(Connection txn, ContactId c,
|
||||||
TransportIndex i, ConnectionWindow w) throws DbException {
|
TransportIndex i, ConnectionWindow w) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT NULL FROM connectionWindows"
|
// Delete any existing connection window
|
||||||
|
String sql = "DELETE FROM connectionWindows"
|
||||||
+ " WHERE contactId = ? AND index = ?";
|
+ " WHERE contactId = ? AND index = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, i.getInt());
|
ps.setInt(2, i.getInt());
|
||||||
rs = ps.executeQuery();
|
ps.executeUpdate();
|
||||||
boolean found = rs.next();
|
|
||||||
if(rs.next()) throw new DbStateException();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
ps.close();
|
||||||
if(found) {
|
// Store the new connection window
|
||||||
// A connection window row exists - update it
|
sql = "INSERT INTO connectionWindows (contactId, index, unseen)"
|
||||||
sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?"
|
+ " VALUES(?, ?, ?)";
|
||||||
+ " WHERE contactId = ? AND index = ?";
|
ps = txn.prepareStatement(sql);
|
||||||
ps = txn.prepareStatement(sql);
|
ps.setInt(1, c.getInt());
|
||||||
ps.setLong(1, w.getCentre());
|
ps.setInt(2, i.getInt());
|
||||||
ps.setInt(2, w.getBitmap());
|
Collection<Long> unseen = w.getUnseen();
|
||||||
ps.setInt(3, c.getInt());
|
for(long l : unseen) {
|
||||||
ps.setInt(4, i.getInt());
|
ps.setLong(3, l);
|
||||||
int affected = ps.executeUpdate();
|
ps.addBatch();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
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) {
|
} catch(SQLException e) {
|
||||||
tryToClose(rs);
|
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class ReaderImpl implements Reader {
|
|||||||
objectReaders.length);
|
objectReaders.length);
|
||||||
objectReaders = newObjectReaders;
|
objectReaders = newObjectReaders;
|
||||||
}
|
}
|
||||||
objectReaders[id] = o;
|
objectReaders[id] = o;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeObjectReader(int id) {
|
public void removeObjectReader(int id) {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ DatabaseListener {
|
|||||||
private synchronized void calculateIvs(ContactId c, TransportId t,
|
private synchronized void calculateIvs(ContactId c, TransportId t,
|
||||||
TransportIndex i, SecretKey ivKey, ConnectionWindow w)
|
TransportIndex i, SecretKey ivKey, ConnectionWindow w)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
for(Long unseen : w.getUnseenConnectionNumbers()) {
|
for(Long unseen : w.getUnseen()) {
|
||||||
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
|
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
|
||||||
expected.put(iv, new ConnectionContextImpl(c, t, i, unseen));
|
expected.put(iv, new ConnectionContextImpl(c, t, i, unseen));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import net.sf.briar.api.transport.ConnectionWindow;
|
import net.sf.briar.api.transport.ConnectionWindow;
|
||||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||||
|
|
||||||
class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
|
class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
|
||||||
|
|
||||||
public ConnectionWindow createConnectionWindow(long centre, int bitmap) {
|
public ConnectionWindow createConnectionWindow() {
|
||||||
return new ConnectionWindowImpl(centre, bitmap);
|
return new ConnectionWindowImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionWindow createConnectionWindow(Collection<Long> unseen) {
|
||||||
|
return new ConnectionWindowImpl(unseen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,74 @@
|
|||||||
package net.sf.briar.transport;
|
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.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import net.sf.briar.api.transport.ConnectionWindow;
|
import net.sf.briar.api.transport.ConnectionWindow;
|
||||||
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
class ConnectionWindowImpl implements ConnectionWindow {
|
class ConnectionWindowImpl implements ConnectionWindow {
|
||||||
|
|
||||||
|
private final Set<Long> unseen;
|
||||||
|
|
||||||
private long centre;
|
private long centre;
|
||||||
private int bitmap;
|
|
||||||
|
|
||||||
ConnectionWindowImpl(long centre, int bitmap) {
|
ConnectionWindowImpl() {
|
||||||
this.centre = centre;
|
unseen = new TreeSet<Long>();
|
||||||
this.bitmap = bitmap;
|
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) unseen.add(l);
|
||||||
|
centre = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCentre() {
|
ConnectionWindowImpl(Collection<Long> unseen) {
|
||||||
return centre;
|
long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
|
||||||
}
|
for(long l : unseen) {
|
||||||
|
if(l < 0 || l > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||||
public int getBitmap() {
|
throw new IllegalArgumentException();
|
||||||
return bitmap;
|
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) {
|
public boolean isSeen(long connection) {
|
||||||
int offset = getOffset(connection);
|
return !unseen.contains(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSeen(long connection) {
|
public void setSeen(long connection) {
|
||||||
int offset = getOffset(connection);
|
long bottom = getBottom(centre);
|
||||||
int mask = 0x80000000 >>> offset;
|
long top = getTop(centre);
|
||||||
if((bitmap & mask) != 0) throw new IllegalArgumentException();
|
if(connection < bottom || connection > top)
|
||||||
bitmap |= mask;
|
throw new IllegalArgumentException();
|
||||||
// If the new connection number is above the centre, slide the window
|
if(!unseen.remove(connection)) throw new IllegalArgumentException();
|
||||||
if(connection >= centre) {
|
if(connection >= centre) {
|
||||||
centre = connection + 1;
|
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() {
|
// Returns the lowest value contained in a window with the given centre
|
||||||
Collection<Long> unseen = new ArrayList<Long>();
|
private long getBottom(long centre) {
|
||||||
for(int i = 0; i < 32; i++) {
|
return Math.max(0, centre - CONNECTION_WINDOW_SIZE / 2);
|
||||||
int mask = 0x80000000 >>> i;
|
}
|
||||||
if((bitmap & mask) == 0) {
|
|
||||||
long c = centre - 16 + i;
|
// Returns the highest value contained in a window with the given centre
|
||||||
if(c >= 0L && c <= MAX_32_BIT_UNSIGNED) unseen.add(c);
|
private long getTop(long centre) {
|
||||||
}
|
return Math.min(ByteUtils.MAX_32_BIT_UNSIGNED,
|
||||||
}
|
centre + CONNECTION_WINDOW_SIZE / 2 - 1);
|
||||||
// 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;
|
public Collection<Long> getUnseen() {
|
||||||
return unseen;
|
return unseen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class BasicH2Test extends TestCase {
|
|||||||
// Check that the name can be retrieved using the unique ID
|
// Check that the name can be retrieved using the unique ID
|
||||||
assertEquals("foo", getName(uniqueId));
|
assertEquals("foo", getName(uniqueId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRow(byte[] uniqueId, String name) throws SQLException {
|
private void addRow(byte[] uniqueId, String name) throws SQLException {
|
||||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import net.sf.briar.api.protocol.GroupFactory;
|
|||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolConstants;
|
||||||
import net.sf.briar.api.protocol.Transport;
|
import net.sf.briar.api.protocol.Transport;
|
||||||
import net.sf.briar.api.protocol.TransportId;
|
import net.sf.briar.api.protocol.TransportId;
|
||||||
import net.sf.briar.api.protocol.TransportIndex;
|
import net.sf.briar.api.protocol.TransportIndex;
|
||||||
@@ -1446,8 +1447,8 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
remoteIndex);
|
remoteIndex);
|
||||||
// The connection window should exist and be in the initial state
|
// The connection window should exist and be in the initial state
|
||||||
assertNotNull(w);
|
assertNotNull(w);
|
||||||
assertEquals(0L, w.getCentre());
|
long top = ProtocolConstants.CONNECTION_WINDOW_SIZE / 2 - 1;
|
||||||
assertEquals(0, w.getBitmap());
|
for(long l = 0; l <= top; l++) assertFalse(w.isSeen(l));
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
@@ -1465,17 +1466,21 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
remoteIndex);
|
remoteIndex);
|
||||||
// The connection window should exist and be in the initial state
|
// The connection window should exist and be in the initial state
|
||||||
assertNotNull(w);
|
assertNotNull(w);
|
||||||
assertEquals(0L, w.getCentre());
|
Collection<Long> unseen = w.getUnseen();
|
||||||
assertEquals(0, w.getBitmap());
|
long top = ProtocolConstants.CONNECTION_WINDOW_SIZE / 2 - 1;
|
||||||
|
assertEquals(top + 1, unseen.size());
|
||||||
|
for(long l = 0; l <= top; l++) {
|
||||||
|
assertFalse(w.isSeen(l));
|
||||||
|
assertTrue(unseen.contains(l));
|
||||||
|
}
|
||||||
// Update the connection window and store it
|
// Update the connection window and store it
|
||||||
w.setSeen(5L);
|
w.setSeen(5);
|
||||||
db.setConnectionWindow(txn, contactId, remoteIndex, w);
|
db.setConnectionWindow(txn, contactId, remoteIndex, w);
|
||||||
// Check that the connection window was stored
|
// Check that the connection window was stored
|
||||||
w = db.getConnectionWindow(txn, contactId, remoteIndex);
|
w = db.getConnectionWindow(txn, contactId, remoteIndex);
|
||||||
assertNotNull(w);
|
assertNotNull(w);
|
||||||
assertEquals(6L, w.getCentre());
|
top += 5;
|
||||||
assertTrue(w.isSeen(5L));
|
for(long l = 0; l <= top; l++) assertEquals(l == 5, w.isSeen(l));
|
||||||
assertEquals(0x00010000, w.getBitmap());
|
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class ConsumersTest extends TestCase {
|
|||||||
Injector i = Guice.createInjector(new CryptoModule());
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
crypto = i.getInstance(CryptoComponent.class);
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDigestingConsumer() throws Exception {
|
public void testDigestingConsumer() throws Exception {
|
||||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class WriterImplTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWriteFloat32() throws IOException {
|
public void testWriteFloat32() throws IOException {
|
||||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||||
// 1 bit for sign, 8 for exponent, 23 for significand
|
// 1 bit for sign, 8 for exponent, 23 for significand
|
||||||
w.writeFloat32(0F); // 0 0 0 -> 0x00000000
|
w.writeFloat32(0F); // 0 0 0 -> 0x00000000
|
||||||
w.writeFloat32(1F); // 0 127 1 -> 0x3F800000
|
w.writeFloat32(1F); // 0 127 1 -> 0x3F800000
|
||||||
w.writeFloat32(2F); // 0 128 1 -> 0x40000000
|
w.writeFloat32(2F); // 0 128 1 -> 0x40000000
|
||||||
@@ -118,7 +118,7 @@ public class WriterImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteFloat64() throws IOException {
|
public void testWriteFloat64() throws IOException {
|
||||||
// 1 bit for sign, 11 for exponent, 52 for significand
|
// 1 bit for sign, 11 for exponent, 52 for significand
|
||||||
w.writeFloat64(0.0); // 0 0 0 -> 0x0000000000000000
|
w.writeFloat64(0.0); // 0 0 0 -> 0x0000000000000000
|
||||||
w.writeFloat64(1.0); // 0 1023 1 -> 0x3FF0000000000000
|
w.writeFloat64(1.0); // 0 1023 1 -> 0x3FF0000000000000
|
||||||
w.writeFloat64(2.0); // 0 1024 1 -> 0x4000000000000000
|
w.writeFloat64(2.0); // 0 1024 1 -> 0x4000000000000000
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class ConnectionDecrypterImplTest extends TestCase {
|
|||||||
out.write(ciphertextMac);
|
out.write(ciphertextMac);
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
// Use a ConnectionDecrypter to decrypt the ciphertext
|
// Use a ConnectionDecrypter to decrypt the ciphertext
|
||||||
ConnectionDecrypter d = new ConnectionDecrypterImpl(in,
|
ConnectionDecrypter d = new ConnectionDecrypterImpl(in,
|
||||||
IvEncoder.encodeIv(initiator, transportIndex, connection),
|
IvEncoder.encodeIv(initiator, transportIndex, connection),
|
||||||
frameCipher, frameKey);
|
frameCipher, frameKey);
|
||||||
// First frame
|
// First frame
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
|
|||||||
Transport transport = new Transport(transportId, localIndex,
|
Transport transport = new Transport(transportId, localIndex,
|
||||||
Collections.singletonMap("foo", "bar"));
|
Collections.singletonMap("foo", "bar"));
|
||||||
transports = Collections.singletonList(transport);
|
transports = Collections.singletonList(transport);
|
||||||
connectionWindow = new ConnectionWindowImpl(0L, 0);
|
connectionWindow = new ConnectionWindowImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -120,8 +120,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
|
|||||||
// Second time - the IV should no longer be expected
|
// Second time - the IV should no longer be expected
|
||||||
assertNull(c.acceptConnection(encryptedIv));
|
assertNull(c.acceptConnection(encryptedIv));
|
||||||
// The window should have advanced
|
// The window should have advanced
|
||||||
assertEquals(4L, connectionWindow.getCentre());
|
Collection<Long> unseen = connectionWindow.getUnseen();
|
||||||
Collection<Long> unseen = connectionWindow.getUnseenConnectionNumbers();
|
|
||||||
assertEquals(19, unseen.size());
|
assertEquals(19, unseen.size());
|
||||||
for(int i = 0; i < 19; i++) {
|
for(int i = 0; i < 19; i++) {
|
||||||
if(i == 3) continue;
|
if(i == 3) continue;
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class ConnectionWindowImplTest extends TestCase {
|
public class ConnectionWindowImplTest extends TestCase {
|
||||||
|
|
||||||
private static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWindowSliding() {
|
public void testWindowSliding() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
for(int i = 0; i < 100; i++) {
|
for(int i = 0; i < 100; i++) {
|
||||||
assertFalse(w.isSeen(i));
|
assertFalse(w.isSeen(i));
|
||||||
w.setSeen(i);
|
w.setSeen(i);
|
||||||
@@ -22,7 +22,7 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWindowJumping() {
|
public void testWindowJumping() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
for(int i = 0; i < 100; i += 13) {
|
for(int i = 0; i < 100; i += 13) {
|
||||||
assertFalse(w.isSeen(i));
|
assertFalse(w.isSeen(i));
|
||||||
w.setSeen(i);
|
w.setSeen(i);
|
||||||
@@ -32,7 +32,7 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWindowUpperLimit() {
|
public void testWindowUpperLimit() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
// Centre is 0, highest value in window is 15
|
// Centre is 0, highest value in window is 15
|
||||||
w.setSeen(15);
|
w.setSeen(15);
|
||||||
// Centre is 16, highest value in window is 31
|
// Centre is 16, highest value in window is 31
|
||||||
@@ -42,18 +42,22 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
w.setSeen(48);
|
w.setSeen(48);
|
||||||
fail();
|
fail();
|
||||||
} catch(IllegalArgumentException expected) {}
|
} catch(IllegalArgumentException expected) {}
|
||||||
w = new ConnectionWindowImpl(MAX_32_BIT_UNSIGNED - 1, 0);
|
// Values greater than 2^32 - 1 should never be allowed
|
||||||
// Values greater than 2^31 - 1 should never be allowed
|
Collection<Long> unseen = new ArrayList<Long>();
|
||||||
w.setSeen(MAX_32_BIT_UNSIGNED);
|
for(int i = 0; i < 32; i++) {
|
||||||
|
unseen.add(ByteUtils.MAX_32_BIT_UNSIGNED - i);
|
||||||
|
}
|
||||||
|
w = new ConnectionWindowImpl(unseen);
|
||||||
|
w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED);
|
||||||
try {
|
try {
|
||||||
w.setSeen(MAX_32_BIT_UNSIGNED + 1);
|
w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED + 1);
|
||||||
fail();
|
fail();
|
||||||
} catch(IllegalArgumentException expected) {}
|
} catch(IllegalArgumentException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWindowLowerLimit() {
|
public void testWindowLowerLimit() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
// Centre is 0, negative values should never be allowed
|
// Centre is 0, negative values should never be allowed
|
||||||
try {
|
try {
|
||||||
w.setSeen(-1);
|
w.setSeen(-1);
|
||||||
@@ -83,7 +87,7 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCannotSetSeenTwice() {
|
public void testCannotSetSeenTwice() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
w.setSeen(15);
|
w.setSeen(15);
|
||||||
try {
|
try {
|
||||||
w.setSeen(15);
|
w.setSeen(15);
|
||||||
@@ -93,9 +97,9 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetUnseenConnectionNumbers() {
|
public void testGetUnseenConnectionNumbers() {
|
||||||
ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
|
ConnectionWindowImpl w = new ConnectionWindowImpl();
|
||||||
// Centre is 0; window should cover 0 to 15, inclusive, with none seen
|
// Centre is 0; window should cover 0 to 15, inclusive, with none seen
|
||||||
Collection<Long> unseen = w.getUnseenConnectionNumbers();
|
Collection<Long> unseen = w.getUnseen();
|
||||||
assertEquals(16, unseen.size());
|
assertEquals(16, unseen.size());
|
||||||
for(int i = 0; i < 16; i++) {
|
for(int i = 0; i < 16; i++) {
|
||||||
assertTrue(unseen.contains(Long.valueOf(i)));
|
assertTrue(unseen.contains(Long.valueOf(i)));
|
||||||
@@ -104,7 +108,7 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
w.setSeen(3);
|
w.setSeen(3);
|
||||||
w.setSeen(4);
|
w.setSeen(4);
|
||||||
// Centre is 5; window should cover 0 to 20, inclusive, with two seen
|
// Centre is 5; window should cover 0 to 20, inclusive, with two seen
|
||||||
unseen = w.getUnseenConnectionNumbers();
|
unseen = w.getUnseen();
|
||||||
assertEquals(19, unseen.size());
|
assertEquals(19, unseen.size());
|
||||||
for(int i = 0; i < 21; i++) {
|
for(int i = 0; i < 21; i++) {
|
||||||
if(i == 3 || i == 4) {
|
if(i == 3 || i == 4) {
|
||||||
@@ -117,7 +121,7 @@ public class ConnectionWindowImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
w.setSeen(19);
|
w.setSeen(19);
|
||||||
// Centre is 20; window should cover 4 to 35, inclusive, with two seen
|
// Centre is 20; window should cover 4 to 35, inclusive, with two seen
|
||||||
unseen = w.getUnseenConnectionNumbers();
|
unseen = w.getUnseen();
|
||||||
assertEquals(30, unseen.size());
|
assertEquals(30, unseen.size());
|
||||||
for(int i = 4; i < 36; i++) {
|
for(int i = 4; i < 36; i++) {
|
||||||
if(i == 4 || i == 19) {
|
if(i == 4 || i == 19) {
|
||||||
|
|||||||
Reference in New Issue
Block a user