mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-23 08:09:54 +01:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user