Forward secrecy.

Each connection's keys are derived from a secret that is erased after
deriving the keys and the secret for the next connection.
This commit is contained in:
akwizgran
2011-11-16 15:35:16 +00:00
parent d02a68edfc
commit f6ae4734ce
45 changed files with 506 additions and 430 deletions

View File

@@ -88,14 +88,14 @@ class CryptoComponentImpl implements CryptoComponent {
if(secret.length != SECRET_KEY_BYTES)
throw new IllegalArgumentException();
ErasableKey key = new ErasableKeyImpl(secret, SECRET_KEY_ALGO);
// The context must leave four bytes free for the length
if(context.length + 4 > SECRET_KEY_BYTES)
// The context must leave two bytes free for the length
if(context.length + 2 > SECRET_KEY_BYTES)
throw new IllegalArgumentException();
byte[] input = new byte[SECRET_KEY_BYTES];
// The initial bytes of the input are the context
System.arraycopy(context, 0, input, 0, context.length);
// The final bytes of the input are the length as a big-endian uint32
ByteUtils.writeUint32(context.length, input, input.length - 4);
// The input starts with the length of the context as a big-endian int16
ByteUtils.writeUint16(context.length, input, 0);
// The remaining bytes of the input are the context
System.arraycopy(context, 0, input, 2, context.length);
// Initialise the counter to zero
byte[] zero = new byte[KEY_DERIVATION_IV_BYTES];
IvParameterSpec iv = new IvParameterSpec(zero);
@@ -110,12 +110,15 @@ class CryptoComponentImpl implements CryptoComponent {
}
}
public byte[] deriveNextSecret(byte[] secret, long connection) {
public byte[] deriveNextSecret(byte[] secret, int index, long connection) {
if(index < 0 || index > ByteUtils.MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if(connection < 0 || connection > ByteUtils.MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
byte[] context = new byte[NEXT.length + 4];
byte[] context = new byte[NEXT.length + 6];
System.arraycopy(NEXT, 0, context, 0, NEXT.length);
ByteUtils.writeUint32(connection, context, NEXT.length);
ByteUtils.writeUint16(index, context, NEXT.length);
ByteUtils.writeUint32(connection, context, NEXT.length + 2);
return counterModeKdf(secret, context);
}

View File

@@ -3,6 +3,7 @@ package net.sf.briar.crypto;
import java.util.Arrays;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.util.ByteUtils;
class ErasableKeyImpl implements ErasableKey {
@@ -34,7 +35,7 @@ class ErasableKeyImpl implements ErasableKey {
public void erase() {
if(erased) throw new IllegalStateException();
for(int i = 0; i < key.length; i++) key[i] = 0;
ByteUtils.erase(key);
erased = true;
}

View File

@@ -84,10 +84,14 @@ interface Database<T> {
* Adds a new contact to the database with the given secrets and returns an
* ID for the contact.
* <p>
* Any secrets generated by the method are stored in the given collection
* and should be erased by the caller once the transaction has been
* committed or aborted.
* <p>
* Locking: contact write.
*/
ContactId addContact(T txn, byte[] incomingSecret, byte[] outgoingSecret)
throws DbException;
ContactId addContact(T txn, byte[] inSecret, byte[] outSecret,
Collection<byte[]> erase) throws DbException;
/**
* Returns false if the given message is already in the database. Otherwise
@@ -187,10 +191,14 @@ interface Database<T> {
* Returns an outgoing connection context for the given contact and
* transport.
* <p>
* Any secrets generated by the method are stored in the given collection
* and should be erased by the caller once the transaction has been
* committed or aborted.
* <p>
* Locking: contact read, window write.
*/
ConnectionContext getConnectionContext(T txn, ContactId c, TransportIndex i)
throws DbException;
ConnectionContext getConnectionContext(T txn, ContactId c, TransportIndex i,
Collection<byte[]> erase) throws DbException;
/**
* Returns the connection reordering window for the given contact and
@@ -373,14 +381,6 @@ interface Database<T> {
Collection<MessageId> getSendableMessages(T txn, ContactId c, int capacity)
throws DbException;
/**
* Returns the secret shared with the given contact.
* <p>
* Locking: contact read.
*/
byte[] getSharedSecret(T txn, ContactId c, boolean incoming)
throws DbException;
/**
* Returns true if the given message has been starred.
* <p>

View File

@@ -62,6 +62,7 @@ import net.sf.briar.api.protocol.writers.SubscriptionUpdateWriter;
import net.sf.briar.api.protocol.writers.TransportUpdateWriter;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionWindow;
import net.sf.briar.util.ByteUtils;
import com.google.inject.Inject;
@@ -136,23 +137,24 @@ DatabaseCleaner.Callback {
}
}
public ContactId addContact(byte[] incomingSecret, byte[] outgoingSecret)
public ContactId addContact(byte[] inSecret, byte[] outSecret)
throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact");
ContactId c;
Collection<byte[]> erase = new ArrayList<byte[]>();
contactLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
c = db.addContact(txn, incomingSecret, outgoingSecret);
c = db.addContact(txn, inSecret, outSecret, erase);
db.commitTransaction(txn);
if(LOG.isLoggable(Level.FINE)) LOG.fine("Added contact " + c);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
contactLock.writeLock().unlock();
// Erase the secrets after committing or aborting the transaction
for(byte[] b : erase) ByteUtils.erase(b);
}
// Call the listeners outside the lock
callListeners(new ContactAddedEvent(c));
@@ -703,6 +705,7 @@ DatabaseCleaner.Callback {
public ConnectionContext getConnectionContext(ContactId c, TransportIndex i)
throws DbException {
Collection<byte[]> erase = new ArrayList<byte[]>();
contactLock.readLock().lock();
try {
if(!containsContact(c)) throw new NoSuchContactException();
@@ -710,7 +713,8 @@ DatabaseCleaner.Callback {
try {
T txn = db.startTransaction();
try {
ConnectionContext ctx = db.getConnectionContext(txn, c, i);
ConnectionContext ctx =
db.getConnectionContext(txn, c, i, erase);
db.commitTransaction(txn);
return ctx;
} catch(DbException e) {
@@ -722,6 +726,8 @@ DatabaseCleaner.Callback {
}
} finally {
contactLock.readLock().unlock();
// Erase the secrets after committing or aborting the transaction
for(byte[] b : erase) ByteUtils.erase(b);
}
}
@@ -907,25 +913,6 @@ DatabaseCleaner.Callback {
}
}
public byte[] getSharedSecret(ContactId c, boolean incoming)
throws DbException {
contactLock.readLock().lock();
try {
if(!containsContact(c)) throw new NoSuchContactException();
T txn = db.startTransaction();
try {
byte[] secret = db.getSharedSecret(txn, c, incoming);
db.commitTransaction(txn);
return secret;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
contactLock.readLock().unlock();
}
}
public Collection<Group> getSubscriptions() throws DbException {
subscriptionLock.readLock().lock();
try {

View File

@@ -29,6 +29,11 @@ class H2Database extends JdbcDatabase {
private static final Logger LOG =
Logger.getLogger(H2Database.class.getName());
private static final String HASH_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
private static final String SECRET_TYPE = "BINARY(32)";
private final File home;
private final Password password;
private final String url;
@@ -42,7 +47,7 @@ class H2Database extends JdbcDatabase {
ConnectionWindowFactory connectionWindowFactory,
GroupFactory groupFactory) {
super(connectionContextFactory, connectionWindowFactory, groupFactory,
"BINARY(32)", "BINARY", "INT NOT NULL AUTO_INCREMENT");
HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE);
home = new File(dir, "db");
this.password = password;
url = "jdbc:h2:split:" + home.getPath()

View File

@@ -58,8 +58,6 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_CONTACTS =
"CREATE TABLE contacts"
+ " (contactId COUNTER,"
+ " incomingSecret BINARY NOT NULL,"
+ " outgoingSecret BINARY NOT NULL,"
+ " PRIMARY KEY (contactId))";
private static final String CREATE_MESSAGES =
@@ -221,7 +219,8 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE connections"
+ " (contactId INT NOT NULL,"
+ " index INT NOT NULL,"
+ " outgoing BIGINT NOT NULL,"
+ " connection BIGINT NOT NULL,"
+ " secret SECRET NOT NULL,"
+ " PRIMARY KEY (contactId, index),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)";
@@ -231,6 +230,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (contactId INT NOT NULL,"
+ " index INT NOT NULL,"
+ " unseen BIGINT NOT NULL,"
+ " secret SECRET NOT NULL,"
+ " PRIMARY KEY (contactId, index, unseen),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)";
@@ -271,7 +271,7 @@ abstract class JdbcDatabase implements Database<Connection> {
private final ConnectionWindowFactory connectionWindowFactory;
private final GroupFactory groupFactory;
// Different database libraries use different names for certain types
private final String hashType, binaryType, counterType;
private final String hashType, binaryType, counterType, secretType;
private final LinkedList<Connection> connections =
new LinkedList<Connection>(); // Locking: self
@@ -284,13 +284,14 @@ abstract class JdbcDatabase implements Database<Connection> {
JdbcDatabase(ConnectionContextFactory connectionContextFactory,
ConnectionWindowFactory connectionWindowFactory,
GroupFactory groupFactory, String hashType, String binaryType,
String counterType) {
String counterType, String secretType) {
this.connectionContextFactory = connectionContextFactory;
this.connectionWindowFactory = connectionWindowFactory;
this.groupFactory = groupFactory;
this.hashType = hashType;
this.binaryType = binaryType;
this.counterType = counterType;
this.secretType = secretType;
}
protected void open(boolean resume, File dir, String driverClass)
@@ -371,6 +372,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s = s.replaceAll("HASH", hashType);
s = s.replaceAll("BINARY", binaryType);
s = s.replaceAll("COUNTER", counterType);
s = s.replaceAll("SECRET", secretType);
return s;
}
@@ -515,17 +517,14 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public ContactId addContact(Connection txn, byte[] incomingSecret,
byte[] outgoingSecret) throws DbException {
public ContactId addContact(Connection txn, byte[] inSecret,
byte[] outSecret, Collection<byte[]> erase) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Create a new contact row
String sql = "INSERT INTO contacts (incomingSecret, outgoingSecret)"
+ " VALUES (?, ?)";
String sql = "INSERT INTO contacts DEFAULT VALUES";
ps = txn.prepareStatement(sql);
ps.setBytes(1, incomingSecret);
ps.setBytes(2, outgoingSecret);
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
@@ -558,13 +557,20 @@ abstract class JdbcDatabase implements Database<Connection> {
affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
// Initialise the connection numbers for all transports
sql = "INSERT INTO connections (contactId, index, outgoing)"
+ " VALUES (?, ?, ZERO())";
// Initialise the outgoing connection contexts for all transports
sql = "INSERT INTO connections"
+ " (contactId, index, connection, secret)"
+ " VALUES (?, ?, ZERO(), ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) {
ps.setInt(2, i);
ConnectionContext ctx =
connectionContextFactory.createNextConnectionContext(c,
new TransportIndex(i), 0L, outSecret);
byte[] secret = ctx.getSecret();
erase.add(secret);
ps.setBytes(3, secret);
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();
@@ -574,18 +580,23 @@ abstract class JdbcDatabase implements Database<Connection> {
if(batchAffected[i] != 1) throw new DbStateException();
}
ps.close();
// Initialise the connection windows for all transports
sql = "INSERT INTO connectionWindows (contactId, index, unseen)"
+ " VALUES (?, ?, ?)";
// Initialise the incoming connection windows for all transports
sql = "INSERT INTO connectionWindows"
+ " (contactId, index, unseen, secret)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
int batchSize = 0;
for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) {
ps.setInt(2, i);
ConnectionWindow w =
connectionWindowFactory.createConnectionWindow();
for(long l : w.getUnseen()) {
ps.setLong(3, l);
connectionWindowFactory.createConnectionWindow(
new TransportIndex(i), inSecret);
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
ps.setLong(3, e.getKey());
byte[] secret = e.getValue();
erase.add(secret);
ps.setBytes(4, secret);
ps.addBatch();
batchSize++;
}
@@ -945,31 +956,43 @@ abstract class JdbcDatabase implements Database<Connection> {
}
public ConnectionContext getConnectionContext(Connection txn, ContactId c,
TransportIndex i) throws DbException {
TransportIndex i, Collection<byte[]> erase) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "UPDATE connections SET outgoing = outgoing + 1"
+ " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, i.getInt());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
sql = "SELECT outgoing FROM connections"
// Retrieve the current context
String sql = "SELECT connection, secret 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()) throw new DbStateException();
long outgoing = rs.getLong(1);
long connection = rs.getLong(1);
byte[] secret = rs.getBytes(2);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return connectionContextFactory.createConnectionContext(c, i,
outgoing);
ConnectionContext ctx =
connectionContextFactory.createConnectionContext(c, i,
connection, secret);
// Calculate and store the next context
ConnectionContext next =
connectionContextFactory.createNextConnectionContext(c, i,
connection + 1, secret);
byte[] nextSecret = next.getSecret();
erase.add(nextSecret);
sql = "UPDATE connections"
+ " SET connection = connection + 1, secret = ?"
+ " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, nextSecret);
ps.setInt(2, c.getInt());
ps.setInt(3, i.getInt());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
return ctx;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -982,17 +1005,17 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT unseen FROM connectionWindows"
String sql = "SELECT unseen, secret FROM connectionWindows"
+ " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, i.getInt());
rs = ps.executeQuery();
Collection<Long> unseen = new ArrayList<Long>();
while(rs.next()) unseen.add(rs.getLong(1));
Map<Long, byte[]> unseen = new HashMap<Long, byte[]>();
while(rs.next()) unseen.put(rs.getLong(1), rs.getBytes(2));
rs.close();
ps.close();
return connectionWindowFactory.createConnectionWindow(unseen);
return connectionWindowFactory.createConnectionWindow(i, unseen);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -1652,29 +1675,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public byte[] getSharedSecret(Connection txn, ContactId c, boolean incoming)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String col = incoming ? "incomingSecret" : "outgoingSecret";
String sql = "SELECT " + col + " FROM contacts WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
byte[] secret = rs.getBytes(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return secret;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public boolean getStarred(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -2197,14 +2197,16 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.executeUpdate();
ps.close();
// Store the new connection window
sql = "INSERT INTO connectionWindows (contactId, index, unseen)"
+ " VALUES(?, ?, ?)";
sql = "INSERT INTO connectionWindows"
+ " (contactId, index, unseen, secret)"
+ " 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);
Map<Long, byte[]> unseen = w.getUnseen();
for(Entry<Long, byte[]> e : unseen.entrySet()) {
ps.setLong(3, e.getKey());
ps.setBytes(4, e.getValue());
ps.addBatch();
}
int[] affectedBatch = ps.executeBatch();

View File

@@ -292,7 +292,7 @@ class PluginManagerImpl implements PluginManager {
public void writerCreated(ContactId c, BatchTransportWriter w) {
assert index != null;
dispatcher.dispatchWriter(index, c, w);
dispatcher.dispatchWriter(c, index, w);
}
}
@@ -307,7 +307,7 @@ class PluginManagerImpl implements PluginManager {
public void outgoingConnectionCreated(ContactId c,
StreamTransportConnection s) {
assert index != null;
dispatcher.dispatchOutgoingConnection(index, c, s);
dispatcher.dispatchOutgoingConnection(c, index, s);
}
}
}

View File

@@ -1,14 +1,31 @@
package net.sf.briar.transport;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionContextFactory;
import com.google.inject.Inject;
class ConnectionContextFactoryImpl implements ConnectionContextFactory {
private final CryptoComponent crypto;
@Inject
ConnectionContextFactoryImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
public ConnectionContext createConnectionContext(ContactId c,
TransportIndex i, long connection) {
return new ConnectionContextImpl(c, i, connection);
TransportIndex i, long connection, byte[] secret) {
return new ConnectionContextImpl(c, i, connection, secret);
}
public ConnectionContext createNextConnectionContext(ContactId c,
TransportIndex i, long connection, byte[] previousSecret) {
byte[] secret = crypto.deriveNextSecret(previousSecret, i.getInt(),
connection);
return new ConnectionContextImpl(c, i, connection, secret);
}
}

View File

@@ -9,12 +9,14 @@ class ConnectionContextImpl implements ConnectionContext {
private final ContactId contactId;
private final TransportIndex transportIndex;
private final long connectionNumber;
private final byte[] secret;
ConnectionContextImpl(ContactId contactId, TransportIndex transportIndex,
long connectionNumber) {
long connectionNumber, byte[] secret) {
this.contactId = contactId;
this.transportIndex = transportIndex;
this.connectionNumber = connectionNumber;
this.secret = secret;
}
public ContactId getContactId() {
@@ -28,4 +30,8 @@ class ConnectionContextImpl implements ConnectionContext {
public long getConnectionNumber() {
return connectionNumber;
}
public byte[] getSecret() {
return secret;
}
}

View File

@@ -62,8 +62,7 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
r.dispose(false);
return;
}
batchConnFactory.createIncomingConnection(ctx.getTransportIndex(),
ctx.getContactId(), r, encryptedIv);
batchConnFactory.createIncomingConnection(ctx, r, encryptedIv);
}
private byte[] readIv(InputStream in) throws IOException {
@@ -77,9 +76,9 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
return b;
}
public void dispatchWriter(TransportIndex i, ContactId c,
public void dispatchWriter(ContactId c, TransportIndex i,
BatchTransportWriter w) {
batchConnFactory.createOutgoingConnection(i, c, w);
batchConnFactory.createOutgoingConnection(c, i, w);
}
public void dispatchIncomingConnection(TransportId t,
@@ -106,12 +105,11 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
s.dispose(false);
return;
}
streamConnFactory.createIncomingConnection(ctx.getTransportIndex(),
ctx.getContactId(), s, encryptedIv);
streamConnFactory.createIncomingConnection(ctx, s, encryptedIv);
}
public void dispatchOutgoingConnection(TransportIndex i, ContactId c,
public void dispatchOutgoingConnection(ContactId c, TransportIndex i,
StreamTransportConnection s) {
streamConnFactory.createOutgoingConnection(i, c, s);
streamConnFactory.createOutgoingConnection(c, i, s);
}
}

View File

@@ -7,12 +7,13 @@ import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.util.ByteUtils;
import com.google.inject.Inject;
@@ -26,10 +27,10 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
}
public ConnectionReader createConnectionReader(InputStream in,
TransportIndex i, byte[] encryptedIv, byte[] secret) {
ConnectionContext ctx, byte[] encryptedIv) {
// Decrypt the IV
Cipher ivCipher = crypto.getIvCipher();
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
ErasableKey ivKey = crypto.deriveIvKey(ctx.getSecret(), true);
byte[] iv;
try {
ivCipher.init(Cipher.DECRYPT_MODE, ivKey);
@@ -42,27 +43,25 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
throw new IllegalArgumentException(badKey);
}
// Validate the IV
if(!IvEncoder.validateIv(iv, true, i))
if(!IvEncoder.validateIv(iv, true, ctx))
throw new IllegalArgumentException();
// Copy the connection number
long connection = IvEncoder.getConnectionNumber(iv);
return createConnectionReader(in, true, i, connection, secret);
return createConnectionReader(in, true, ctx);
}
public ConnectionReader createConnectionReader(InputStream in,
TransportIndex i, long connection, byte[] secret) {
return createConnectionReader(in, false, i, connection, secret);
ConnectionContext ctx) {
return createConnectionReader(in, false, ctx);
}
private ConnectionReader createConnectionReader(InputStream in,
boolean initiator, TransportIndex i, long connection,
byte[] secret) {
boolean initiator, ConnectionContext ctx) {
// Derive the keys and erase the secret
byte[] secret = ctx.getSecret();
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
for(int j = 0; j < secret.length; j++) secret[j] = 0;
ByteUtils.erase(secret);
// Create the decrypter
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
byte[] iv = IvEncoder.encodeIv(initiator, ctx);
Cipher frameCipher = crypto.getFrameCipher();
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
frameCipher, frameKey);

View File

@@ -8,25 +8,26 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.Bytes;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.TransportAddedEvent;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
import net.sf.briar.api.db.event.TransportAddedEvent;
import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportIndex;
@@ -75,30 +76,29 @@ DatabaseListener {
}
private synchronized void calculateIvs(ContactId c) throws DbException {
byte[] secret = db.getSharedSecret(c, true);
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
for(int i = 0; i < secret.length; i++) secret[i] = 0;
for(TransportId t : localTransportIds) {
TransportIndex i = db.getRemoteIndex(c, t);
if(i != null) {
ConnectionWindow w = db.getConnectionWindow(c, i);
calculateIvs(c, i, ivKey, w);
calculateIvs(c, i, w);
}
}
}
private synchronized void calculateIvs(ContactId c, TransportIndex i,
ErasableKey ivKey, ConnectionWindow w)
throws DbException {
for(Long unseen : w.getUnseen()) {
ConnectionWindow w) throws DbException {
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
long unseen = e.getKey();
byte[] secret = e.getValue();
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
expected.put(iv, new ConnectionContextImpl(c, i, unseen));
expected.put(iv, new ConnectionContextImpl(c, i, unseen, secret));
}
}
private synchronized byte[] encryptIv(TransportIndex i, long connection,
ErasableKey ivKey) {
byte[] iv = IvEncoder.encodeIv(true, i, connection);
byte[] iv = IvEncoder.encodeIv(true, i.getInt(), connection);
try {
ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
return ivCipher.doFinal(iv);
@@ -133,10 +133,7 @@ DatabaseListener {
TransportIndex i1 = ctx1.getTransportIndex();
if(c1.equals(c) && i1.equals(i)) it.remove();
}
byte[] secret = db.getSharedSecret(c, true);
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
for(int j = 0; j < secret.length; j++) secret[j] = 0;
calculateIvs(c, i, ivKey, w);
calculateIvs(c, i, w);
} catch(NoSuchContactException e) {
// The contact was removed - clean up when we get the event
}
@@ -185,13 +182,10 @@ DatabaseListener {
private synchronized void calculateIvs(TransportId t) throws DbException {
for(ContactId c : db.getContacts()) {
try {
byte[] secret = db.getSharedSecret(c, true);
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
for(int i = 0; i < secret.length; i++) secret[i] = 0;
TransportIndex i = db.getRemoteIndex(c, t);
if(i != null) {
ConnectionWindow w = db.getConnectionWindow(c, i);
calculateIvs(c, i, ivKey, w);
calculateIvs(c, i, w);
}
} catch(NoSuchContactException e) {
// The contact was removed - clean up when we get the event

View File

@@ -1,17 +1,30 @@
package net.sf.briar.transport;
import java.util.Collection;
import java.util.Map;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.ConnectionWindow;
import net.sf.briar.api.transport.ConnectionWindowFactory;
import com.google.inject.Inject;
class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
public ConnectionWindow createConnectionWindow() {
return new ConnectionWindowImpl();
private final CryptoComponent crypto;
@Inject
ConnectionWindowFactoryImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
public ConnectionWindow createConnectionWindow(Collection<Long> unseen) {
return new ConnectionWindowImpl(unseen);
public ConnectionWindow createConnectionWindow(TransportIndex i,
byte[] secret) {
return new ConnectionWindowImpl(crypto, i, secret);
}
public ConnectionWindow createConnectionWindow(TransportIndex i,
Map<Long, byte[]> unseen) {
return new ConnectionWindowImpl(crypto, i, unseen);
}
}

View File

@@ -2,28 +2,38 @@ package net.sf.briar.transport;
import static net.sf.briar.api.protocol.ProtocolConstants.CONNECTION_WINDOW_SIZE;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import java.util.HashMap;
import java.util.Map;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.ConnectionWindow;
import net.sf.briar.util.ByteUtils;
class ConnectionWindowImpl implements ConnectionWindow {
private final Set<Long> unseen;
private final CryptoComponent crypto;
private final int index;
private final Map<Long, byte[]> unseen;
private long centre;
ConnectionWindowImpl() {
unseen = new TreeSet<Long>();
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) unseen.add(l);
ConnectionWindowImpl(CryptoComponent crypto, TransportIndex i,
byte[] secret) {
this.crypto = crypto;
index = i.getInt();
unseen = new HashMap<Long, byte[]>();
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) {
secret = crypto.deriveNextSecret(secret, index, l);
unseen.put(l, secret);
}
centre = 0;
}
ConnectionWindowImpl(Collection<Long> unseen) {
ConnectionWindowImpl(CryptoComponent crypto, TransportIndex i,
Map<Long, byte[]> unseen) {
long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
for(long l : unseen) {
for(long l : unseen.keySet()) {
if(l < 0 || l > ByteUtils.MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
if(l < min) min = l;
@@ -31,15 +41,17 @@ class ConnectionWindowImpl implements ConnectionWindow {
}
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();
if(!unseen.containsKey(l)) throw new IllegalArgumentException();
}
this.crypto = crypto;
index = i.getInt();
this.unseen = unseen;
}
public boolean isSeen(long connection) {
return !unseen.contains(connection);
return !unseen.containsKey(connection);
}
public void setSeen(long connection) {
@@ -47,14 +59,26 @@ class ConnectionWindowImpl implements ConnectionWindow {
long top = getTop(centre);
if(connection < bottom || connection > top)
throw new IllegalArgumentException();
if(!unseen.remove(connection)) throw new IllegalArgumentException();
if(!unseen.containsKey(connection))
throw new IllegalArgumentException();
if(connection >= centre) {
centre = connection + 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);
for(long l = bottom; l < newBottom; l++) {
byte[] expired = unseen.remove(l);
if(expired != null) ByteUtils.erase(expired);
}
byte[] topSecret = unseen.get(top);
assert topSecret != null;
for(long l = top + 1; l <= newTop; l++) {
topSecret = crypto.deriveNextSecret(topSecret, index, l);
unseen.put(l, topSecret);
}
}
byte[] seen = unseen.remove(connection);
assert seen != null;
ByteUtils.erase(seen);
}
// Returns the lowest value contained in a window with the given centre
@@ -68,7 +92,7 @@ class ConnectionWindowImpl implements ConnectionWindow {
centre + CONNECTION_WINDOW_SIZE / 2 - 1);
}
public Collection<Long> getUnseen() {
public Map<Long, byte[]> getUnseen() {
return unseen;
}
}

View File

@@ -7,12 +7,13 @@ import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.util.ByteUtils;
import com.google.inject.Inject;
@@ -26,17 +27,15 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
}
public ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, TransportIndex i, long connection, byte[] secret) {
return createConnectionWriter(out, capacity, true, i, connection,
secret);
long capacity, ConnectionContext ctx) {
return createConnectionWriter(out, capacity, true, ctx);
}
public ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, TransportIndex i, byte[] encryptedIv,
byte[] secret) {
long capacity, ConnectionContext ctx, byte[] encryptedIv) {
// Decrypt the IV
Cipher ivCipher = crypto.getIvCipher();
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
ErasableKey ivKey = crypto.deriveIvKey(ctx.getSecret(), true);
byte[] iv;
try {
ivCipher.init(Cipher.DECRYPT_MODE, ivKey);
@@ -49,26 +48,23 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
throw new RuntimeException(badKey);
}
// Validate the IV
if(!IvEncoder.validateIv(iv, true, i))
if(!IvEncoder.validateIv(iv, true, ctx))
throw new IllegalArgumentException();
// Copy the connection number
long connection = IvEncoder.getConnectionNumber(iv);
return createConnectionWriter(out, capacity, false, i, connection,
secret);
return createConnectionWriter(out, capacity, false, ctx);
}
private ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, boolean initiator, TransportIndex i, long connection,
byte[] secret) {
long capacity, boolean initiator, ConnectionContext ctx) {
// Derive the keys and erase the secret
byte[] secret = ctx.getSecret();
ErasableKey ivKey = crypto.deriveIvKey(secret, initiator);
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
for(int j = 0; j < secret.length; j++) secret[j] = 0;
ByteUtils.erase(secret);
// Create the encrypter
Cipher ivCipher = crypto.getIvCipher();
Cipher frameCipher = crypto.getFrameCipher();
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
byte[] iv = IvEncoder.encodeIv(initiator, ctx);
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
capacity, iv, ivCipher, frameCipher, ivKey, frameKey);
// Create the writer

View File

@@ -1,18 +1,22 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.util.ByteUtils;
class IvEncoder {
static byte[] encodeIv(boolean initiator, TransportIndex i,
long connection) {
static byte[] encodeIv(boolean initiator, ConnectionContext ctx) {
return encodeIv(initiator, ctx.getTransportIndex().getInt(),
ctx.getConnectionNumber());
}
static byte[] encodeIv(boolean initiator, int index, long connection) {
byte[] iv = new byte[IV_LENGTH];
// Bit 31 is the initiator flag
if(initiator) iv[3] = 1;
// Encode the transport identifier as an unsigned 16-bit integer
ByteUtils.writeUint16(i.getInt(), iv, 4);
// Encode the transport index as an unsigned 16-bit integer
ByteUtils.writeUint16(index, iv, 4);
// Encode the connection number as an unsigned 32-bit integer
ByteUtils.writeUint32(connection, iv, 6);
return iv;
@@ -24,7 +28,14 @@ class IvEncoder {
ByteUtils.writeUint32(frame, iv, 10);
}
static boolean validateIv(byte[] iv, boolean initiator, TransportIndex i) {
static boolean validateIv(byte[] iv, boolean initiator,
ConnectionContext ctx) {
return validateIv(iv, initiator, ctx.getTransportIndex().getInt(),
ctx.getConnectionNumber());
}
static boolean validateIv(byte[] iv, boolean initiator, int index,
long connection) {
if(iv.length != IV_LENGTH) return false;
// Check that the reserved bits are all zero
for(int j = 0; j < 2; j++) if(iv[j] != 0) return false;
@@ -32,8 +43,10 @@ class IvEncoder {
for(int j = 10; j < iv.length; j++) if(iv[j] != 0) return false;
// Check that the initiator flag matches
if(initiator != getInitiatorFlag(iv)) return false;
// Check that the transport ID matches
if(i.getInt() != getTransportId(iv)) return false;
// Check that the transport index matches
if(index != getTransportIndex(iv)) return false;
// Check that the connection number matches
if(connection != getConnectionNumber(iv)) return false;
// The IV is valid
return true;
}
@@ -43,7 +56,7 @@ class IvEncoder {
return (iv[3] & 1) == 1;
}
static int getTransportId(byte[] iv) {
static int getTransportIndex(byte[] iv) {
if(iv.length != IV_LENGTH) throw new IllegalArgumentException();
return ByteUtils.readUint16(iv, 4);
}

View File

@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.BatchConnectionFactory;
import net.sf.briar.api.transport.BatchTransportReader;
import net.sf.briar.api.transport.BatchTransportWriter;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -33,11 +34,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory {
this.protoWriterFactory = protoWriterFactory;
}
public void createIncomingConnection(TransportIndex i, ContactId c,
public void createIncomingConnection(ConnectionContext ctx,
BatchTransportReader r, byte[] encryptedIv) {
final IncomingBatchConnection conn = new IncomingBatchConnection(
connReaderFactory, db, protoReaderFactory, i, c, r,
encryptedIv);
connReaderFactory, db, protoReaderFactory, ctx, r, encryptedIv);
Runnable read = new Runnable() {
public void run() {
conn.read();
@@ -46,10 +46,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory {
new Thread(read).start();
}
public void createOutgoingConnection(TransportIndex i, ContactId c,
public void createOutgoingConnection(ContactId c, TransportIndex i,
BatchTransportWriter w) {
final OutgoingBatchConnection conn = new OutgoingBatchConnection(
connWriterFactory, db, protoWriterFactory, i, c, w);
connWriterFactory, db, protoWriterFactory, c, i, w);
Runnable write = new Runnable() {
public void run() {
conn.write();

View File

@@ -13,9 +13,9 @@ import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.transport.BatchTransportReader;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -27,46 +27,43 @@ class IncomingBatchConnection {
private final ConnectionReaderFactory connFactory;
private final DatabaseComponent db;
private final ProtocolReaderFactory protoFactory;
private final TransportIndex transportIndex;
private final ContactId contactId;
private final ConnectionContext ctx;
private final BatchTransportReader reader;
private final byte[] encryptedIv;
IncomingBatchConnection(ConnectionReaderFactory connFactory,
DatabaseComponent db, ProtocolReaderFactory protoFactory,
TransportIndex transportIndex, ContactId contactId,
BatchTransportReader reader, byte[] encryptedIv) {
ConnectionContext ctx, BatchTransportReader reader,
byte[] encryptedIv) {
this.connFactory = connFactory;
this.db = db;
this.protoFactory = protoFactory;
this.transportIndex = transportIndex;
this.contactId = contactId;
this.ctx = ctx;
this.reader = reader;
this.encryptedIv = encryptedIv;
}
void read() {
try {
byte[] secret = db.getSharedSecret(contactId, true);
ConnectionReader conn = connFactory.createConnectionReader(
reader.getInputStream(), transportIndex, encryptedIv,
secret);
reader.getInputStream(), ctx, encryptedIv);
ProtocolReader proto = protoFactory.createProtocolReader(
conn.getInputStream());
ContactId c = ctx.getContactId();
// Read packets until EOF
while(!proto.eof()) {
if(proto.hasAck()) {
Ack a = proto.readAck();
db.receiveAck(contactId, a);
db.receiveAck(c, a);
} else if(proto.hasBatch()) {
Batch b = proto.readBatch();
db.receiveBatch(contactId, b);
db.receiveBatch(c, b);
} else if(proto.hasSubscriptionUpdate()) {
SubscriptionUpdate s = proto.readSubscriptionUpdate();
db.receiveSubscriptionUpdate(contactId, s);
db.receiveSubscriptionUpdate(c, s);
} else if(proto.hasTransportUpdate()) {
TransportUpdate t = proto.readTransportUpdate();
db.receiveTransportUpdate(contactId, t);
db.receiveTransportUpdate(c, t);
} else {
throw new FormatException();
}

View File

@@ -29,30 +29,28 @@ class OutgoingBatchConnection {
private final ConnectionWriterFactory connFactory;
private final DatabaseComponent db;
private final ProtocolWriterFactory protoFactory;
private final TransportIndex transportIndex;
private final ContactId contactId;
private final TransportIndex transportIndex;
private final BatchTransportWriter writer;
OutgoingBatchConnection(ConnectionWriterFactory connFactory,
DatabaseComponent db, ProtocolWriterFactory protoFactory,
TransportIndex transportIndex, ContactId contactId,
ContactId contactId, TransportIndex transportIndex,
BatchTransportWriter writer) {
this.connFactory = connFactory;
this.db = db;
this.protoFactory = protoFactory;
this.transportIndex = transportIndex;
this.contactId = contactId;
this.transportIndex = transportIndex;
this.writer = writer;
}
void write() {
try {
byte[] secret = db.getSharedSecret(contactId, false);
ConnectionContext ctx =
db.getConnectionContext(contactId, transportIndex);
ConnectionContext ctx = db.getConnectionContext(contactId,
transportIndex);
ConnectionWriter conn = connFactory.createConnectionWriter(
writer.getOutputStream(), writer.getCapacity(),
transportIndex, ctx.getConnectionNumber(), secret);
writer.getOutputStream(), writer.getCapacity(), ctx);
OutputStream out = conn.getOutputStream();
// There should be enough space for a packet
long capacity = conn.getRemainingCapacity();

View File

@@ -2,12 +2,11 @@ package net.sf.briar.transport.stream;
import java.io.IOException;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.api.transport.ConnectionWriter;
@@ -16,35 +15,32 @@ import net.sf.briar.api.transport.StreamTransportConnection;
public class IncomingStreamConnection extends StreamConnection {
private final ConnectionContext ctx;
private final byte[] encryptedIv;
IncomingStreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory,
TransportIndex transportIndex, ContactId contactId,
StreamTransportConnection connection,
ConnectionContext ctx, StreamTransportConnection connection,
byte[] encryptedIv) {
super(connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, transportIndex, contactId, connection);
protoWriterFactory, ctx.getContactId(), connection);
this.ctx = ctx;
this.encryptedIv = encryptedIv;
}
@Override
protected ConnectionReader createConnectionReader() throws DbException,
IOException {
byte[] secret = db.getSharedSecret(contactId, true);
return connReaderFactory.createConnectionReader(
connection.getInputStream(), transportIndex, encryptedIv,
secret);
connection.getInputStream(), ctx, encryptedIv);
}
@Override
protected ConnectionWriter createConnectionWriter() throws DbException,
IOException {
byte[] secret = db.getSharedSecret(contactId, false);
return connWriterFactory.createConnectionWriter(
connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
encryptedIv, secret);
connection.getOutputStream(), Long.MAX_VALUE, ctx, encryptedIv);
}
}

View File

@@ -17,43 +17,40 @@ import net.sf.briar.api.transport.StreamTransportConnection;
public class OutgoingStreamConnection extends StreamConnection {
private final TransportIndex transportIndex;
private ConnectionContext ctx = null; // Locking: this
OutgoingStreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory,
TransportIndex transportIndex, ContactId contactId,
ProtocolWriterFactory protoWriterFactory, ContactId contactId,
TransportIndex transportIndex,
StreamTransportConnection connection) {
super(connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, transportIndex, contactId, connection);
protoWriterFactory, contactId, connection);
this.transportIndex = transportIndex;
}
@Override
protected ConnectionReader createConnectionReader() throws DbException,
IOException {
synchronized(this) {
if(ctx == null) {
if(ctx == null)
ctx = db.getConnectionContext(contactId, transportIndex);
}
}
byte[] secret = db.getSharedSecret(contactId, true);
return connReaderFactory.createConnectionReader(
connection.getInputStream(), transportIndex,
ctx.getConnectionNumber(), secret);
connection.getInputStream(), ctx);
}
@Override
protected ConnectionWriter createConnectionWriter() throws DbException,
IOException {
synchronized(this) {
if(ctx == null) {
if(ctx == null)
ctx = db.getConnectionContext(contactId, transportIndex);
}
}
byte[] secret = db.getSharedSecret(contactId, false);
return connWriterFactory.createConnectionWriter(
connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
ctx.getConnectionNumber(), secret);
connection.getOutputStream(), Long.MAX_VALUE, ctx);
}
}

View File

@@ -29,7 +29,6 @@ import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter;
@@ -56,7 +55,6 @@ abstract class StreamConnection implements DatabaseListener {
protected final DatabaseComponent db;
protected final ProtocolReaderFactory protoReaderFactory;
protected final ProtocolWriterFactory protoWriterFactory;
protected final TransportIndex transportIndex;
protected final ContactId contactId;
protected final StreamTransportConnection connection;
@@ -69,15 +67,13 @@ abstract class StreamConnection implements DatabaseListener {
StreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory,
TransportIndex transportIndex, ContactId contactId,
ProtocolWriterFactory protoWriterFactory, ContactId contactId,
StreamTransportConnection connection) {
this.connReaderFactory = connReaderFactory;
this.connWriterFactory = connWriterFactory;
this.db = db;
this.protoReaderFactory = protoReaderFactory;
this.protoWriterFactory = protoWriterFactory;
this.transportIndex = transportIndex;
this.contactId = contactId;
this.connection = connection;
}

View File

@@ -5,6 +5,7 @@ import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.api.transport.StreamConnectionFactory;
@@ -32,11 +33,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory {
this.protoWriterFactory = protoWriterFactory;
}
public void createIncomingConnection(TransportIndex i, ContactId c,
public void createIncomingConnection(ConnectionContext ctx,
StreamTransportConnection s, byte[] encryptedIv) {
final StreamConnection conn = new IncomingStreamConnection(
connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, i, c, s, encryptedIv);
protoWriterFactory, ctx, s, encryptedIv);
Runnable write = new Runnable() {
public void run() {
conn.write();
@@ -51,11 +52,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory {
new Thread(read).start();
}
public void createOutgoingConnection(TransportIndex i, ContactId c,
public void createOutgoingConnection(ContactId c, TransportIndex i,
StreamTransportConnection s) {
final StreamConnection conn = new OutgoingStreamConnection(
connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, i, c, s);
protoWriterFactory, c, i, s);
Runnable write = new Runnable() {
public void run() {
conn.write();