mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 06:39:54 +01:00
Replaced encrypted IVs with pseudo-random tags.
This commit is contained in:
@@ -27,13 +27,11 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private static final String DIGEST_ALGO = "SHA-256";
|
private static final String DIGEST_ALGO = "SHA-256";
|
||||||
private static final String KEY_PAIR_ALGO = "ECDSA";
|
private static final String KEY_PAIR_ALGO = "ECDSA";
|
||||||
private static final int KEY_PAIR_BITS = 256;
|
private static final int KEY_PAIR_BITS = 256;
|
||||||
private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
|
private static final String CIPHER_ALGO = "AES/CTR/NoPadding";
|
||||||
private static final String SECRET_KEY_ALGO = "AES";
|
private static final String SECRET_KEY_ALGO = "AES";
|
||||||
private static final int SECRET_KEY_BYTES = 32; // 256 bits
|
private static final int SECRET_KEY_BYTES = 32; // 256 bits
|
||||||
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
|
|
||||||
private static final String MAC_ALGO = "HMacSHA256";
|
private static final String MAC_ALGO = "HMacSHA256";
|
||||||
private static final String SIGNATURE_ALGO = "ECDSA";
|
private static final String SIGNATURE_ALGO = "ECDSA";
|
||||||
private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
|
|
||||||
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
|
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
|
||||||
|
|
||||||
// Labels for key derivation, null-terminated
|
// Labels for key derivation, null-terminated
|
||||||
@@ -104,7 +102,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
assert ivBytes[ivBytes.length - 1] == 0;
|
assert ivBytes[ivBytes.length - 1] == 0;
|
||||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(KEY_DERIVATION_ALGO, PROVIDER);
|
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, PROVIDER);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||||
byte[] output = cipher.doFinal(KEY_DERIVATION_INPUT);
|
byte[] output = cipher.doFinal(KEY_DERIVATION_INPUT);
|
||||||
assert output.length == SECRET_KEY_BYTES;
|
assert output.length == SECRET_KEY_BYTES;
|
||||||
@@ -137,7 +135,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
public Cipher getFrameCipher() {
|
public Cipher getFrameCipher() {
|
||||||
try {
|
try {
|
||||||
return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
|
return Cipher.getInstance(CIPHER_ALGO, PROVIDER);
|
||||||
} catch(GeneralSecurityException e) {
|
} catch(GeneralSecurityException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -178,7 +176,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
public Cipher getTagCipher() {
|
public Cipher getTagCipher() {
|
||||||
try {
|
try {
|
||||||
return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
|
return Cipher.getInstance(CIPHER_ALGO, PROVIDER);
|
||||||
} catch(GeneralSecurityException e) {
|
} catch(GeneralSecurityException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@@ -13,10 +12,11 @@ import java.security.InvalidKeyException;
|
|||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
|
|
||||||
class ConnectionDecrypterImpl extends FilterInputStream
|
class ConnectionDecrypterImpl extends FilterInputStream
|
||||||
implements ConnectionDecrypter {
|
implements ConnectionDecrypter {
|
||||||
|
|
||||||
@@ -28,14 +28,13 @@ implements ConnectionDecrypter {
|
|||||||
private long frame = 0L;
|
private long frame = 0L;
|
||||||
private boolean betweenFrames = true;
|
private boolean betweenFrames = true;
|
||||||
|
|
||||||
ConnectionDecrypterImpl(InputStream in, byte[] iv, Cipher frameCipher,
|
ConnectionDecrypterImpl(InputStream in, Cipher frameCipher,
|
||||||
ErasableKey frameKey) {
|
ErasableKey frameKey) {
|
||||||
super(in);
|
super(in);
|
||||||
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
|
|
||||||
this.iv = iv;
|
|
||||||
this.frameCipher = frameCipher;
|
this.frameCipher = frameCipher;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
buf = new byte[TAG_LENGTH];
|
iv = IvEncoder.encodeIv(0, frameCipher.getBlockSize());
|
||||||
|
buf = new byte[frameCipher.getBlockSize()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
|
|||||||
@@ -25,27 +25,17 @@ implements ConnectionEncrypter {
|
|||||||
private long capacity, frame = 0L;
|
private long capacity, frame = 0L;
|
||||||
private boolean tagWritten = false, betweenFrames = false;
|
private boolean tagWritten = false, betweenFrames = false;
|
||||||
|
|
||||||
ConnectionEncrypterImpl(OutputStream out, long capacity, byte[] iv,
|
ConnectionEncrypterImpl(OutputStream out, long capacity, Cipher tagCipher,
|
||||||
Cipher tagCipher, Cipher frameCipher, ErasableKey tagKey,
|
Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey) {
|
||||||
ErasableKey frameKey) {
|
|
||||||
super(out);
|
super(out);
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
this.iv = iv;
|
|
||||||
this.frameCipher = frameCipher;
|
this.frameCipher = frameCipher;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
|
iv = IvEncoder.encodeIv(0, frameCipher.getBlockSize());
|
||||||
// Encrypt the tag
|
// Encrypt the tag
|
||||||
try {
|
tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
|
||||||
tag = tagCipher.doFinal(iv);
|
|
||||||
} catch(BadPaddingException badCipher) {
|
|
||||||
throw new IllegalArgumentException(badCipher);
|
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
|
||||||
throw new IllegalArgumentException(badCipher);
|
|
||||||
} catch(InvalidKeyException badKey) {
|
|
||||||
throw new IllegalArgumentException(badKey);
|
|
||||||
}
|
|
||||||
if(tag.length != TAG_LENGTH) throw new IllegalArgumentException();
|
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
|
if(tag.length != TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
@@ -28,26 +25,12 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
|||||||
|
|
||||||
public ConnectionReader createConnectionReader(InputStream in,
|
public ConnectionReader createConnectionReader(InputStream in,
|
||||||
ConnectionContext ctx, byte[] tag) {
|
ConnectionContext ctx, byte[] tag) {
|
||||||
// Decrypt the tag
|
// Validate the tag
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
|
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
|
||||||
byte[] iv;
|
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey);
|
||||||
try {
|
|
||||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
|
||||||
iv = tagCipher.doFinal(tag);
|
|
||||||
} catch(BadPaddingException badCipher) {
|
|
||||||
throw new IllegalArgumentException(badCipher);
|
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
|
||||||
throw new IllegalArgumentException(badCipher);
|
|
||||||
} catch(InvalidKeyException badKey) {
|
|
||||||
throw new IllegalArgumentException(badKey);
|
|
||||||
}
|
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
// Validate the tag
|
if(!valid) throw new IllegalArgumentException();
|
||||||
int index = ctx.getTransportIndex().getInt();
|
|
||||||
long connection = ctx.getConnectionNumber();
|
|
||||||
if(!IvEncoder.validateIv(iv, index, connection))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return createConnectionReader(in, true, ctx);
|
return createConnectionReader(in, true, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +47,8 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
|||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||||
ByteUtils.erase(secret);
|
ByteUtils.erase(secret);
|
||||||
// Create the decrypter
|
// Create the decrypter
|
||||||
int index = ctx.getTransportIndex().getInt();
|
|
||||||
long connection = ctx.getConnectionNumber();
|
|
||||||
byte[] iv = IvEncoder.encodeIv(index, connection);
|
|
||||||
Cipher frameCipher = crypto.getFrameCipher();
|
Cipher frameCipher = crypto.getFrameCipher();
|
||||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
|
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
|
||||||
frameCipher, frameKey);
|
frameCipher, frameKey);
|
||||||
// Create the reader
|
// Create the reader
|
||||||
Mac mac = crypto.getMac();
|
Mac mac = crypto.getMac();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package net.sf.briar.transport;
|
|||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -15,9 +14,7 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.Bytes;
|
import net.sf.briar.api.Bytes;
|
||||||
import net.sf.briar.api.ContactId;
|
import net.sf.briar.api.ContactId;
|
||||||
@@ -103,22 +100,10 @@ DatabaseListener {
|
|||||||
|
|
||||||
// Locking: this
|
// Locking: this
|
||||||
private Bytes calculateTag(Context ctx, byte[] secret) {
|
private Bytes calculateTag(Context ctx, byte[] secret) {
|
||||||
byte[] iv = IvEncoder.encodeIv(ctx.transportIndex.getInt(),
|
|
||||||
ctx.connection);
|
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
||||||
try {
|
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
tagKey.erase();
|
||||||
byte[] tag = tagCipher.doFinal(iv);
|
return new Bytes(tag);
|
||||||
return new Bytes(tag);
|
|
||||||
} catch(BadPaddingException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
} catch(InvalidKeyException badKey) {
|
|
||||||
throw new RuntimeException(badKey);
|
|
||||||
} finally {
|
|
||||||
tagKey.erase();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptConnection(final TransportId t, final byte[] tag,
|
public void acceptConnection(final TransportId t, final byte[] tag,
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
@@ -36,23 +33,9 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
|||||||
// Decrypt the tag
|
// Decrypt the tag
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
|
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
|
||||||
byte[] iv;
|
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey);
|
||||||
try {
|
|
||||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
|
||||||
iv = tagCipher.doFinal(tag);
|
|
||||||
} catch(BadPaddingException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
} catch(InvalidKeyException badKey) {
|
|
||||||
throw new RuntimeException(badKey);
|
|
||||||
}
|
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
// Validate the tag
|
if(!valid) throw new IllegalArgumentException();
|
||||||
int index = ctx.getTransportIndex().getInt();
|
|
||||||
long connection = ctx.getConnectionNumber();
|
|
||||||
if(!IvEncoder.validateIv(iv, index, connection))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return createConnectionWriter(out, capacity, false, ctx);
|
return createConnectionWriter(out, capacity, false, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,13 +48,10 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
|||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||||
ByteUtils.erase(secret);
|
ByteUtils.erase(secret);
|
||||||
// Create the encrypter
|
// Create the encrypter
|
||||||
int index = ctx.getTransportIndex().getInt();
|
|
||||||
long connection = ctx.getConnectionNumber();
|
|
||||||
byte[] iv = IvEncoder.encodeIv(index, connection);
|
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
Cipher frameCipher = crypto.getFrameCipher();
|
Cipher frameCipher = crypto.getFrameCipher();
|
||||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||||
capacity, iv, tagCipher, frameCipher, tagKey, frameKey);
|
capacity, tagCipher, frameCipher, tagKey, frameKey);
|
||||||
// Create the writer
|
// Create the writer
|
||||||
Mac mac = crypto.getMac();
|
Mac mac = crypto.getMac();
|
||||||
return new ConnectionWriterImpl(encrypter, mac, macKey);
|
return new ConnectionWriterImpl(encrypter, mac, macKey);
|
||||||
|
|||||||
@@ -1,45 +1,19 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
class IvEncoder {
|
class IvEncoder {
|
||||||
|
|
||||||
static byte[] encodeIv(int index, long connection) {
|
static byte[] encodeIv(long frame, int blockSize) {
|
||||||
byte[] iv = new byte[TAG_LENGTH];
|
if(frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||||
// Encode the transport index as an unsigned 16-bit integer
|
throw new IllegalArgumentException();
|
||||||
ByteUtils.writeUint16(index, iv, 4);
|
byte[] iv = new byte[blockSize];
|
||||||
// Encode the connection number as an unsigned 32-bit integer
|
updateIv(iv, frame);
|
||||||
ByteUtils.writeUint32(connection, iv, 6);
|
|
||||||
return iv;
|
return iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateIv(byte[] iv, long frame) {
|
static void updateIv(byte[] iv, long frame) {
|
||||||
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
|
// Encode the frame number as a uint32, leaving 2 bytes for the counter
|
||||||
// Encode the frame number as an unsigned 32-bit integer
|
ByteUtils.writeUint32(frame, iv, iv.length - 6);
|
||||||
ByteUtils.writeUint32(frame, iv, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean validateIv(byte[] iv, int index, long connection) {
|
|
||||||
if(iv.length != TAG_LENGTH) return false;
|
|
||||||
// Check that the reserved bits are all zero
|
|
||||||
for(int i = 0; i < 3; i++) if(iv[i] != 0) return false;
|
|
||||||
for(int i = 10; i < iv.length; i++) if(iv[i] != 0) 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getTransportIndex(byte[] iv) {
|
|
||||||
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
|
|
||||||
return ByteUtils.readUint16(iv, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static long getConnectionNumber(byte[] iv) {
|
|
||||||
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
|
|
||||||
return ByteUtils.readUint32(iv, 6);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
components/net/sf/briar/transport/TagEncoder.java
Normal file
54
components/net/sf/briar/transport/TagEncoder.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
|
import net.sf.briar.api.transport.TransportConstants;
|
||||||
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
|
class TagEncoder {
|
||||||
|
|
||||||
|
static byte[] encodeTag(long frame, Cipher tagCipher, ErasableKey tagKey) {
|
||||||
|
if(frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
// The plaintext is blank
|
||||||
|
byte[] plaintext = new byte[TransportConstants.TAG_LENGTH];
|
||||||
|
// Encode the frame number as a uint32 at the end of the IV
|
||||||
|
byte[] iv = new byte[tagCipher.getBlockSize()];
|
||||||
|
if(iv.length != plaintext.length) throw new IllegalArgumentException();
|
||||||
|
ByteUtils.writeUint32(frame, iv, iv.length - 4);
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
|
try {
|
||||||
|
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey, ivSpec);
|
||||||
|
return tagCipher.doFinal(plaintext);
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
|
// Unsuitable cipher or key
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean validateTag(byte[] tag, long frame, Cipher tagCipher,
|
||||||
|
ErasableKey tagKey) {
|
||||||
|
if(tag.length != TransportConstants.TAG_LENGTH) return false;
|
||||||
|
// Encode the frame number as a uint32 at the end of the IV
|
||||||
|
byte[] iv = new byte[tagCipher.getBlockSize()];
|
||||||
|
if(iv.length != tag.length) throw new IllegalArgumentException();
|
||||||
|
ByteUtils.writeUint32(frame, iv, iv.length - 4);
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
|
try {
|
||||||
|
tagCipher.init(Cipher.DECRYPT_MODE, tagKey, ivSpec);
|
||||||
|
byte[] plaintext = tagCipher.doFinal(tag);
|
||||||
|
// The plaintext should be blank
|
||||||
|
for(int i = 0; i < plaintext.length; i++) {
|
||||||
|
if(plaintext[i] != 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
|
// Unsuitable cipher or key
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import net.sf.briar.TestUtils;
|
import net.sf.briar.TestUtils;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
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.crypto.CryptoModule;
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
|
||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
@@ -25,18 +23,14 @@ public class ConnectionDecrypterImplTest extends TestCase {
|
|||||||
|
|
||||||
private static final int MAC_LENGTH = 32;
|
private static final int MAC_LENGTH = 32;
|
||||||
|
|
||||||
private final Cipher tagCipher, frameCipher;
|
private final Cipher frameCipher;
|
||||||
private final ErasableKey tagKey, frameKey;
|
private final ErasableKey frameKey;
|
||||||
private final TransportIndex transportIndex = new TransportIndex(13);
|
|
||||||
private final long connection = 12345L;
|
|
||||||
|
|
||||||
public ConnectionDecrypterImplTest() {
|
public ConnectionDecrypterImplTest() {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||||
tagCipher = crypto.getTagCipher();
|
|
||||||
frameCipher = crypto.getFrameCipher();
|
frameCipher = crypto.getFrameCipher();
|
||||||
tagKey = crypto.generateTestKey();
|
|
||||||
frameKey = crypto.generateTestKey();
|
frameKey = crypto.generateTestKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,12 +45,8 @@ public class ConnectionDecrypterImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testDecryption(boolean initiator) throws Exception {
|
private void testDecryption(boolean initiator) throws Exception {
|
||||||
// Calculate the plaintext and ciphertext for the IV
|
|
||||||
byte[] iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
|
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
|
||||||
byte[] tag = tagCipher.doFinal(iv);
|
|
||||||
assertEquals(TAG_LENGTH, tag.length);
|
|
||||||
// Calculate the expected plaintext for the first frame
|
// Calculate the expected plaintext for the first frame
|
||||||
|
byte[] iv = new byte[frameCipher.getBlockSize()];
|
||||||
byte[] ciphertext = new byte[123];
|
byte[] ciphertext = new byte[123];
|
||||||
byte[] ciphertextMac = new byte[MAC_LENGTH];
|
byte[] ciphertextMac = new byte[MAC_LENGTH];
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
@@ -84,9 +74,8 @@ public class ConnectionDecrypterImplTest extends TestCase {
|
|||||||
out.write(ciphertextMac);
|
out.write(ciphertextMac);
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
// Use a ConnectionDecrypter to decrypt the ciphertext
|
// Use a ConnectionDecrypter to decrypt the ciphertext
|
||||||
ConnectionDecrypter d = new ConnectionDecrypterImpl(in,
|
ConnectionDecrypter d = new ConnectionDecrypterImpl(in, frameCipher,
|
||||||
IvEncoder.encodeIv(transportIndex.getInt(), connection),
|
frameKey);
|
||||||
frameCipher, frameKey);
|
|
||||||
// First frame
|
// First frame
|
||||||
byte[] decrypted = new byte[ciphertext.length];
|
byte[] decrypted = new byte[ciphertext.length];
|
||||||
TestUtils.readFully(d.getInputStream(), decrypted);
|
TestUtils.readFully(d.getInputStream(), decrypted);
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
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.crypto.CryptoModule;
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -25,8 +23,6 @@ public class ConnectionEncrypterImplTest extends TestCase {
|
|||||||
|
|
||||||
private final Cipher tagCipher, frameCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final ErasableKey tagKey, frameKey;
|
private final ErasableKey tagKey, frameKey;
|
||||||
private final TransportIndex transportIndex = new TransportIndex(13);
|
|
||||||
private final long connection = 12345L;
|
|
||||||
|
|
||||||
public ConnectionEncrypterImplTest() {
|
public ConnectionEncrypterImplTest() {
|
||||||
super();
|
super();
|
||||||
@@ -49,12 +45,10 @@ public class ConnectionEncrypterImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testEncryption(boolean initiator) throws Exception {
|
private void testEncryption(boolean initiator) throws Exception {
|
||||||
// Calculate the expected ciphertext for the IV
|
// Calculate the expected tag
|
||||||
byte[] iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
|
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
|
||||||
byte[] tag = tagCipher.doFinal(iv);
|
|
||||||
assertEquals(TAG_LENGTH, tag.length);
|
|
||||||
// Calculate the expected ciphertext for the first frame
|
// Calculate the expected ciphertext for the first frame
|
||||||
|
byte[] iv = new byte[frameCipher.getBlockSize()];
|
||||||
byte[] plaintext = new byte[123];
|
byte[] plaintext = new byte[123];
|
||||||
byte[] plaintextMac = new byte[MAC_LENGTH];
|
byte[] plaintextMac = new byte[MAC_LENGTH];
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
@@ -82,9 +76,8 @@ public class ConnectionEncrypterImplTest extends TestCase {
|
|||||||
byte[] expected = out.toByteArray();
|
byte[] expected = out.toByteArray();
|
||||||
// Use a ConnectionEncrypter to encrypt the plaintext
|
// Use a ConnectionEncrypter to encrypt the plaintext
|
||||||
out.reset();
|
out.reset();
|
||||||
iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
|
|
||||||
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
|
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
|
||||||
iv, tagCipher, frameCipher, tagKey, frameKey);
|
tagCipher, frameCipher, tagKey, frameKey);
|
||||||
e.getOutputStream().write(plaintext);
|
e.getOutputStream().write(plaintext);
|
||||||
e.writeMac(plaintextMac);
|
e.writeMac(plaintextMac);
|
||||||
e.getOutputStream().write(plaintext1);
|
e.getOutputStream().write(plaintext1);
|
||||||
|
|||||||
@@ -617,8 +617,6 @@ public class ConnectionRecogniserImplTest extends TestCase {
|
|||||||
// Calculate the expected tag for connection number 3
|
// Calculate the expected tag for connection number 3
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
return TagEncoder.encodeTag(0, tagCipher, tagKey);
|
||||||
byte[] iv = IvEncoder.encodeIv(remoteIndex.getInt(), 3);
|
|
||||||
return tagCipher.doFinal(iv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ import java.util.Random;
|
|||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
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.ConnectionReader;
|
import net.sf.briar.api.transport.ConnectionReader;
|
||||||
import net.sf.briar.api.transport.ConnectionWriter;
|
import net.sf.briar.api.transport.ConnectionWriter;
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
@@ -33,8 +32,6 @@ public class FrameReadWriteTest extends TestCase {
|
|||||||
private final byte[] outSecret;
|
private final byte[] outSecret;
|
||||||
private final ErasableKey tagKey, frameKey, macKey;
|
private final ErasableKey tagKey, frameKey, macKey;
|
||||||
private final Mac mac;
|
private final Mac mac;
|
||||||
private final TransportIndex transportIndex = new TransportIndex(13);
|
|
||||||
private final long connection = 12345L;
|
|
||||||
|
|
||||||
public FrameReadWriteTest() {
|
public FrameReadWriteTest() {
|
||||||
super();
|
super();
|
||||||
@@ -63,11 +60,8 @@ public class FrameReadWriteTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testWriteAndRead(boolean initiator) throws Exception {
|
private void testWriteAndRead(boolean initiator) throws Exception {
|
||||||
// Create and encrypt the IV
|
// Encode the tag
|
||||||
byte[] iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
|
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
|
||||||
byte[] tag = tagCipher.doFinal(iv);
|
|
||||||
assertEquals(TAG_LENGTH, tag.length);
|
|
||||||
// Generate two random frames
|
// Generate two random frames
|
||||||
byte[] frame = new byte[12345];
|
byte[] frame = new byte[12345];
|
||||||
random.nextBytes(frame);
|
random.nextBytes(frame);
|
||||||
@@ -80,7 +74,7 @@ public class FrameReadWriteTest extends TestCase {
|
|||||||
// Write the frames
|
// Write the frames
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||||
Long.MAX_VALUE, iv, tagCipher, frameCipher, tagCopy, frameCopy);
|
Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy);
|
||||||
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac,
|
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac,
|
||||||
macCopy);
|
macCopy);
|
||||||
OutputStream out1 = writer.getOutputStream();
|
OutputStream out1 = writer.getOutputStream();
|
||||||
@@ -88,18 +82,14 @@ public class FrameReadWriteTest extends TestCase {
|
|||||||
out1.flush();
|
out1.flush();
|
||||||
out1.write(frame1);
|
out1.write(frame1);
|
||||||
out1.flush();
|
out1.flush();
|
||||||
// Read the IV back
|
// Read the tag back
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
byte[] recoveredTag = new byte[TAG_LENGTH];
|
byte[] recoveredTag = new byte[TAG_LENGTH];
|
||||||
assertEquals(TAG_LENGTH, in.read(recoveredTag));
|
assertEquals(TAG_LENGTH, in.read(recoveredTag));
|
||||||
assertArrayEquals(tag, recoveredTag);
|
assertArrayEquals(tag, recoveredTag);
|
||||||
// Decrypt the IV
|
assertTrue(TagEncoder.validateTag(tag, 0, tagCipher, tagKey));
|
||||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
|
||||||
byte[] recoveredIv = tagCipher.doFinal(recoveredTag);
|
|
||||||
iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
|
|
||||||
assertArrayEquals(iv, recoveredIv);
|
|
||||||
// Read the frames back
|
// Read the frames back
|
||||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
|
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
|
||||||
frameCipher, frameKey);
|
frameCipher, frameKey);
|
||||||
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac,
|
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac,
|
||||||
macKey);
|
macKey);
|
||||||
|
|||||||
Reference in New Issue
Block a user