mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Added an initiator flag (bit 31) to the IV.
The flag is used to distinguish between the initiator and responder directions of a stream-mode connection, allowing them to use the same connection number without risking IV reuse. The flag is also raised for batch-mode connections, which only have one direction.
This commit is contained in:
@@ -20,8 +20,6 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
class ConnectionDecrypterImpl extends FilterInputStream
|
||||
implements ConnectionDecrypter {
|
||||
|
||||
private final int transportId;
|
||||
private final long connection;
|
||||
private final Cipher frameCipher;
|
||||
private final SecretKey frameKey;
|
||||
private final byte[] buf, iv;
|
||||
@@ -30,15 +28,13 @@ implements ConnectionDecrypter {
|
||||
private long frame = 0L;
|
||||
private boolean betweenFrames = true;
|
||||
|
||||
ConnectionDecrypterImpl(InputStream in, int transportId, long connection,
|
||||
Cipher frameCipher, SecretKey frameKey) {
|
||||
ConnectionDecrypterImpl(InputStream in, boolean initiator, int transportId,
|
||||
long connection, Cipher frameCipher, SecretKey frameKey) {
|
||||
super(in);
|
||||
this.transportId = transportId;
|
||||
this.connection = connection;
|
||||
this.frameCipher = frameCipher;
|
||||
this.frameKey = frameKey;
|
||||
buf = new byte[IV_LENGTH];
|
||||
iv = new byte[IV_LENGTH];
|
||||
iv = IvEncoder.encodeIv(initiator, transportId, connection);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
@@ -132,8 +128,7 @@ implements ConnectionDecrypter {
|
||||
private void initialiseCipher() {
|
||||
assert betweenFrames;
|
||||
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||
IvEncoder.encodeIv(iv, transportId, connection, frame);
|
||||
// Use the plaintext IV to initialise the frame cipher
|
||||
IvEncoder.updateIv(iv, frame);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
try {
|
||||
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
|
||||
@@ -145,4 +140,4 @@ implements ConnectionDecrypter {
|
||||
frame++;
|
||||
betweenFrames = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,25 +18,21 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
class ConnectionEncrypterImpl extends FilterOutputStream
|
||||
implements ConnectionEncrypter {
|
||||
|
||||
private final int transportId;
|
||||
private final long connection;
|
||||
private final Cipher ivCipher, frameCipher;
|
||||
private final SecretKey frameKey;
|
||||
private final byte[] iv;
|
||||
|
||||
private long frame = 0L;
|
||||
private boolean started = false, betweenFrames = false;
|
||||
private boolean ivWritten = false, betweenFrames = false;
|
||||
|
||||
ConnectionEncrypterImpl(OutputStream out, int transportId,
|
||||
long connection, Cipher ivCipher, Cipher frameCipher,
|
||||
SecretKey ivKey, SecretKey frameKey) {
|
||||
ConnectionEncrypterImpl(OutputStream out, boolean initiator,
|
||||
int transportId, long connection, Cipher ivCipher,
|
||||
Cipher frameCipher, SecretKey ivKey, SecretKey frameKey) {
|
||||
super(out);
|
||||
this.transportId = transportId;
|
||||
this.connection = connection;
|
||||
this.ivCipher = ivCipher;
|
||||
this.frameCipher = frameCipher;
|
||||
this.frameKey = frameKey;
|
||||
iv = new byte[IV_LENGTH];
|
||||
iv = IvEncoder.encodeIv(initiator, transportId, connection);
|
||||
try {
|
||||
ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
|
||||
} catch(InvalidKeyException badKey) {
|
||||
@@ -51,7 +47,7 @@ implements ConnectionEncrypter {
|
||||
}
|
||||
|
||||
public void writeMac(byte[] mac) throws IOException {
|
||||
if(!started || betweenFrames) throw new IllegalStateException();
|
||||
if(!ivWritten || betweenFrames) throw new IllegalStateException();
|
||||
try {
|
||||
out.write(frameCipher.doFinal(mac));
|
||||
} catch(BadPaddingException badCipher) {
|
||||
@@ -64,7 +60,7 @@ implements ConnectionEncrypter {
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if(!started) writeIv();
|
||||
if(!ivWritten) writeIv();
|
||||
if(betweenFrames) initialiseCipher();
|
||||
byte[] ciphertext = frameCipher.update(new byte[] {(byte) b});
|
||||
if(ciphertext != null) out.write(ciphertext);
|
||||
@@ -77,16 +73,15 @@ implements ConnectionEncrypter {
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if(!started) writeIv();
|
||||
if(!ivWritten) writeIv();
|
||||
if(betweenFrames) initialiseCipher();
|
||||
byte[] ciphertext = frameCipher.update(b, off, len);
|
||||
if(ciphertext != null) out.write(ciphertext);
|
||||
}
|
||||
|
||||
private void writeIv() throws IOException {
|
||||
assert !started;
|
||||
assert !ivWritten;
|
||||
assert !betweenFrames;
|
||||
IvEncoder.encodeIv(iv, transportId, connection, 0L);
|
||||
try {
|
||||
out.write(ivCipher.doFinal(iv));
|
||||
} catch(BadPaddingException badCipher) {
|
||||
@@ -94,15 +89,15 @@ implements ConnectionEncrypter {
|
||||
} catch(IllegalBlockSizeException badCipher) {
|
||||
throw new RuntimeException(badCipher);
|
||||
}
|
||||
started = true;
|
||||
ivWritten = true;
|
||||
betweenFrames = true;
|
||||
}
|
||||
|
||||
private void initialiseCipher() {
|
||||
assert started;
|
||||
assert ivWritten;
|
||||
assert betweenFrames;
|
||||
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||
IvEncoder.encodeIv(iv, transportId, connection, frame);
|
||||
IvEncoder.updateIv(iv, frame);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
try {
|
||||
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||
|
||||
@@ -23,7 +23,8 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
||||
}
|
||||
|
||||
public ConnectionReader createConnectionReader(InputStream in,
|
||||
int transportId, long connection, byte[] secret) {
|
||||
boolean initiator, int transportId, long connection,
|
||||
byte[] secret) {
|
||||
SecretKey macKey = crypto.deriveIncomingMacKey(secret);
|
||||
SecretKey frameKey = crypto.deriveIncomingFrameKey(secret);
|
||||
Cipher frameCipher = crypto.getFrameCipher();
|
||||
@@ -34,7 +35,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
|
||||
transportId, connection, frameCipher, frameKey);
|
||||
initiator, transportId, connection, frameCipher, frameKey);
|
||||
return new ConnectionReaderImpl(decrypter, mac);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ DatabaseListener {
|
||||
}
|
||||
|
||||
private synchronized byte[] encryptIv(ContactId c, long connection) {
|
||||
byte[] iv = IvEncoder.encodeIv(transportId, connection);
|
||||
byte[] iv = IvEncoder.encodeIv(true, transportId, connection);
|
||||
Cipher cipher = contactToCipher.get(c);
|
||||
assert cipher != null;
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,8 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
||||
}
|
||||
|
||||
public ConnectionWriter createConnectionWriter(OutputStream out,
|
||||
int transportId, long connection, byte[] secret) {
|
||||
boolean initiator, int transportId, long connection,
|
||||
byte[] secret) {
|
||||
SecretKey macKey = crypto.deriveOutgoingMacKey(secret);
|
||||
SecretKey ivKey = crypto.deriveOutgoingIvKey(secret);
|
||||
SecretKey frameKey = crypto.deriveOutgoingFrameKey(secret);
|
||||
@@ -36,8 +37,8 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
||||
throw new IllegalArgumentException(badKey);
|
||||
}
|
||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||
transportId, connection, ivCipher, frameCipher, ivKey,
|
||||
frameKey);
|
||||
initiator, transportId, connection, ivCipher, frameCipher,
|
||||
ivKey, frameKey);
|
||||
return new ConnectionWriterImpl(encrypter, mac);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import net.sf.briar.util.ByteUtils;
|
||||
|
||||
class IvEncoder {
|
||||
|
||||
static byte[] encodeIv(int transportId, long connection) {
|
||||
static byte[] encodeIv(boolean initiator, int transportId,
|
||||
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(transportId, iv, 4);
|
||||
// Encode the connection number as an unsigned 32-bit integer
|
||||
@@ -14,20 +17,9 @@ class IvEncoder {
|
||||
return iv;
|
||||
}
|
||||
|
||||
static void encodeIv(byte[] iv, int transportId, long connection,
|
||||
long frame) {
|
||||
static void updateIv(byte[] iv, long frame) {
|
||||
if(iv.length != IV_LENGTH) throw new IllegalArgumentException();
|
||||
// The first 16 bits of the IV must be zero (reserved)
|
||||
iv[0] = 0;
|
||||
iv[1] = 0;
|
||||
// Encode the transport identifier as an unsigned 16-bit integer
|
||||
ByteUtils.writeUint16(transportId, iv, 4);
|
||||
// Encode the connection number as an unsigned 32-bit integer
|
||||
ByteUtils.writeUint32(connection, iv, 6);
|
||||
// Encode the frame number as an unsigned 32-bit integer
|
||||
ByteUtils.writeUint32(frame, iv, 10);
|
||||
// The last 16 bits of the IV must be zero (block number)
|
||||
iv[14] = 0;
|
||||
iv[15] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user