Replaced encrypted IVs with pseudo-random tags.

This commit is contained in:
akwizgran
2011-12-02 12:57:39 +00:00
parent f3fdd85996
commit 14d5e6fe64
12 changed files with 104 additions and 174 deletions

View File

@@ -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);
} }

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);
} }
} }

View 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);
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
} }
} }

View File

@@ -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);