The word "tag" was overloaded, so from now on use "tag" for the

predefined tags in the protocol and serial components, and "IV" for
the encrypted IVs used to identify connections in the transport
component.
This commit is contained in:
akwizgran
2011-08-19 11:15:35 +02:00
parent 2411e2008b
commit 9dea4d0299
19 changed files with 239 additions and 250 deletions

View File

@@ -10,31 +10,31 @@ import javax.crypto.SecretKey;
public interface CryptoComponent { public interface CryptoComponent {
SecretKey deriveIncomingMacKey(byte[] secret);
SecretKey deriveIncomingFrameKey(byte[] secret); SecretKey deriveIncomingFrameKey(byte[] secret);
SecretKey deriveIncomingTagKey(byte[] secret); SecretKey deriveIncomingIvKey(byte[] secret);
SecretKey deriveOutgoingMacKey(byte[] secret); SecretKey deriveIncomingMacKey(byte[] secret);
SecretKey deriveOutgoingFrameKey(byte[] secret); SecretKey deriveOutgoingFrameKey(byte[] secret);
SecretKey deriveOutgoingTagKey(byte[] secret); SecretKey deriveOutgoingIvKey(byte[] secret);
SecretKey deriveOutgoingMacKey(byte[] secret);
KeyPair generateKeyPair(); KeyPair generateKeyPair();
SecretKey generateSecretKey(); SecretKey generateSecretKey();
Cipher getFrameCipher();
Cipher getIvCipher();
KeyParser getKeyParser(); KeyParser getKeyParser();
Mac getMac(); Mac getMac();
MessageDigest getMessageDigest(); MessageDigest getMessageDigest();
Cipher getFrameCipher();
Signature getSignature(); Signature getSignature();
Cipher getTagCipher();
} }

View File

@@ -10,8 +10,9 @@ import net.sf.briar.api.db.DbException;
public interface ConnectionRecogniser { public interface ConnectionRecogniser {
/** /**
* Returns the ID of the contact who created the tag if the connection * Returns the ID of the contact who created the encrypted IV if the
* should be accepted, or null if the connection should be rejected. * connection should be accepted, or null if the connection should be
* rejected.
*/ */
ContactId acceptConnection(byte[] tag) throws DbException; ContactId acceptConnection(byte[] encryptedIv) throws DbException;
} }

View File

@@ -7,6 +7,9 @@ public interface TransportConstants {
*/ */
static final int MAX_FRAME_LENGTH = 65536; // 2^16 static final int MAX_FRAME_LENGTH = 65536; // 2^16
/** The length in bytes of the tag that uniquely identifies a connection. */ /**
static final int TAG_LENGTH = 16; * The length in bytes of the encrypted IV that uniquely identifies a
* connection.
*/
static final int IV_LENGTH = 16;
} }

View File

@@ -37,12 +37,12 @@ class CryptoComponentImpl implements CryptoComponent {
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 SECRET_STORAGE_ALGO = "AES/CTR/NoPadding"; private static final String SECRET_STORAGE_ALGO = "AES/CTR/NoPadding";
private static final String MAC_ALGO = "HMacSHA256"; private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
private static final String PACKET_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_BITS = 256; private static final int SECRET_KEY_BITS = 256;
private static final String IV_CIPHER_ALGO = "AES/ECB/NoPadding";
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 TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
private final SecretKey secretStorageKey; private final SecretKey secretStorageKey;
private final KeyParser keyParser; private final KeyParser keyParser;
@@ -68,14 +68,14 @@ class CryptoComponentImpl implements CryptoComponent {
} }
} }
public SecretKey deriveIncomingMacKey(byte[] secret) { public SecretKey deriveIncomingFrameKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret); SharedSecret s = new SharedSecret(secret);
return deriveMacKey(s, !s.getAlice()); return deriveFrameKey(s, !s.getAlice());
} }
private SecretKey deriveMacKey(SharedSecret s, boolean alice) { private SecretKey deriveFrameKey(SharedSecret s, boolean alice) {
if(alice) return deriveKey("MACA", s.getIv(), s.getCiphertext()); if(alice) return deriveKey("F_A", s.getIv(), s.getCiphertext());
else return deriveKey("MACB", s.getIv(), s.getCiphertext()); else return deriveKey("F_B", s.getIv(), s.getCiphertext());
} }
private SecretKey deriveKey(String name, IvParameterSpec iv, private SecretKey deriveKey(String name, IvParameterSpec iv,
@@ -114,29 +114,24 @@ class CryptoComponentImpl implements CryptoComponent {
} }
} }
public SecretKey deriveIncomingFrameKey(byte[] secret) { public SecretKey deriveIncomingIvKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret); SharedSecret s = new SharedSecret(secret);
return deriveFrameKey(s, !s.getAlice()); return deriveIvKey(s, !s.getAlice());
} }
private SecretKey deriveFrameKey(SharedSecret s, boolean alice) { private SecretKey deriveIvKey(SharedSecret s, boolean alice) {
if(alice) return deriveKey("PKTA", s.getIv(), s.getCiphertext()); if(alice) return deriveKey("I_A", s.getIv(), s.getCiphertext());
else return deriveKey("PKTB", s.getIv(), s.getCiphertext()); else return deriveKey("I_B", s.getIv(), s.getCiphertext());
} }
public SecretKey deriveIncomingTagKey(byte[] secret) { public SecretKey deriveIncomingMacKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret); SharedSecret s = new SharedSecret(secret);
return deriveTagKey(s, !s.getAlice()); return deriveMacKey(s, !s.getAlice());
} }
private SecretKey deriveTagKey(SharedSecret s, boolean alice) { private SecretKey deriveMacKey(SharedSecret s, boolean alice) {
if(alice) return deriveKey("TAGA", s.getIv(), s.getCiphertext()); if(alice) return deriveKey("M_A", s.getIv(), s.getCiphertext());
else return deriveKey("TAGB", s.getIv(), s.getCiphertext()); else return deriveKey("M_B", s.getIv(), s.getCiphertext());
}
public SecretKey deriveOutgoingMacKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret);
return deriveMacKey(s, s.getAlice());
} }
public SecretKey deriveOutgoingFrameKey(byte[] secret) { public SecretKey deriveOutgoingFrameKey(byte[] secret) {
@@ -144,9 +139,14 @@ class CryptoComponentImpl implements CryptoComponent {
return deriveFrameKey(s, s.getAlice()); return deriveFrameKey(s, s.getAlice());
} }
public SecretKey deriveOutgoingTagKey(byte[] secret) { public SecretKey deriveOutgoingIvKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret); SharedSecret s = new SharedSecret(secret);
return deriveTagKey(s, s.getAlice()); return deriveIvKey(s, s.getAlice());
}
public SecretKey deriveOutgoingMacKey(byte[] secret) {
SharedSecret s = new SharedSecret(secret);
return deriveMacKey(s, s.getAlice());
} }
public KeyPair generateKeyPair() { public KeyPair generateKeyPair() {
@@ -157,6 +157,30 @@ class CryptoComponentImpl implements CryptoComponent {
return keyGenerator.generateKey(); return keyGenerator.generateKey();
} }
public Cipher getFrameCipher() {
try {
return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch(NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public Cipher getIvCipher() {
try {
return Cipher.getInstance(IV_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch(NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public KeyParser getKeyParser() { public KeyParser getKeyParser() {
return keyParser; return keyParser;
} }
@@ -181,18 +205,6 @@ class CryptoComponentImpl implements CryptoComponent {
} }
} }
public Cipher getFrameCipher() {
try {
return Cipher.getInstance(PACKET_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch(NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public Signature getSignature() { public Signature getSignature() {
try { try {
return Signature.getInstance(SIGNATURE_ALGO, PROVIDER); return Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
@@ -202,16 +214,4 @@ class CryptoComponentImpl implements CryptoComponent {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public Cipher getTagCipher() {
try {
return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch(NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
} }

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_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;
@@ -24,7 +24,7 @@ implements ConnectionDecrypter {
private final long connection; private final long connection;
private final Cipher frameCipher; private final Cipher frameCipher;
private final SecretKey frameKey; private final SecretKey frameKey;
private final byte[] buf, tag; private final byte[] buf, iv;
private int bufOff = 0, bufLen = 0; private int bufOff = 0, bufLen = 0;
private long frame = 0L; private long frame = 0L;
@@ -37,8 +37,8 @@ implements ConnectionDecrypter {
this.connection = connection; this.connection = connection;
this.frameCipher = frameCipher; this.frameCipher = frameCipher;
this.frameKey = frameKey; this.frameKey = frameKey;
buf = new byte[TAG_LENGTH]; buf = new byte[IV_LENGTH];
tag = new byte[TAG_LENGTH]; iv = new byte[IV_LENGTH];
} }
public InputStream getInputStream() { public InputStream getInputStream() {
@@ -132,11 +132,11 @@ implements ConnectionDecrypter {
private void initialiseCipher() { private void initialiseCipher() {
assert betweenFrames; assert betweenFrames;
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
TagEncoder.encodeTag(tag, transportId, connection, frame); IvEncoder.encodeIv(iv, transportId, connection, frame);
// Use the plaintext tag to initialise the packet cipher // Use the plaintext IV to initialise the frame cipher
IvParameterSpec iv = new IvParameterSpec(tag); IvParameterSpec ivSpec = new IvParameterSpec(iv);
try { try {
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
} catch(InvalidAlgorithmParameterException badIv) { } catch(InvalidAlgorithmParameterException badIv) {
throw new RuntimeException(badIv); throw new RuntimeException(badIv);
} catch(InvalidKeyException badKey) { } catch(InvalidKeyException badKey) {

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_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.FilterOutputStream; import java.io.FilterOutputStream;
@@ -20,29 +20,29 @@ implements ConnectionEncrypter {
private final int transportId; private final int transportId;
private final long connection; private final long connection;
private final Cipher tagCipher, frameCipher; private final Cipher ivCipher, frameCipher;
private final SecretKey frameKey; private final SecretKey frameKey;
private final byte[] tag; private final byte[] iv;
private long frame = 0L; private long frame = 0L;
private boolean started = false, betweenFrames = false; private boolean started = false, betweenFrames = false;
ConnectionEncrypterImpl(OutputStream out, int transportId, ConnectionEncrypterImpl(OutputStream out, int transportId,
long connection, Cipher tagCipher, Cipher frameCipher, long connection, Cipher ivCipher, Cipher frameCipher,
SecretKey tagKey, SecretKey frameKey) { SecretKey ivKey, SecretKey frameKey) {
super(out); super(out);
this.transportId = transportId; this.transportId = transportId;
this.connection = connection; this.connection = connection;
this.tagCipher = tagCipher; this.ivCipher = ivCipher;
this.frameCipher = frameCipher; this.frameCipher = frameCipher;
this.frameKey = frameKey; this.frameKey = frameKey;
tag = new byte[TAG_LENGTH]; iv = new byte[IV_LENGTH];
try { try {
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
} catch(InvalidKeyException badKey) { } catch(InvalidKeyException badKey) {
throw new IllegalArgumentException(badKey); throw new IllegalArgumentException(badKey);
} }
if(tagCipher.getOutputSize(TAG_LENGTH) != TAG_LENGTH) if(ivCipher.getOutputSize(IV_LENGTH) != IV_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
@@ -64,7 +64,7 @@ implements ConnectionEncrypter {
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
if(!started) writeTag(); if(!started) writeIv();
if(betweenFrames) initialiseCipher(); if(betweenFrames) initialiseCipher();
byte[] ciphertext = frameCipher.update(new byte[] {(byte) b}); byte[] ciphertext = frameCipher.update(new byte[] {(byte) b});
if(ciphertext != null) out.write(ciphertext); if(ciphertext != null) out.write(ciphertext);
@@ -77,18 +77,18 @@ implements ConnectionEncrypter {
@Override @Override
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
if(!started) writeTag(); if(!started) writeIv();
if(betweenFrames) initialiseCipher(); if(betweenFrames) initialiseCipher();
byte[] ciphertext = frameCipher.update(b, off, len); byte[] ciphertext = frameCipher.update(b, off, len);
if(ciphertext != null) out.write(ciphertext); if(ciphertext != null) out.write(ciphertext);
} }
private void writeTag() throws IOException { private void writeIv() throws IOException {
assert !started; assert !started;
assert !betweenFrames; assert !betweenFrames;
TagEncoder.encodeTag(tag, transportId, connection, 0L); IvEncoder.encodeIv(iv, transportId, connection, 0L);
try { try {
out.write(tagCipher.doFinal(tag)); out.write(ivCipher.doFinal(iv));
} catch(BadPaddingException badCipher) { } catch(BadPaddingException badCipher) {
throw new IOException(badCipher); throw new IOException(badCipher);
} catch(IllegalBlockSizeException badCipher) { } catch(IllegalBlockSizeException badCipher) {
@@ -102,10 +102,10 @@ implements ConnectionEncrypter {
assert started; assert started;
assert betweenFrames; assert betweenFrames;
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
TagEncoder.encodeTag(tag, transportId, connection, frame); IvEncoder.encodeIv(iv, transportId, connection, frame);
IvParameterSpec iv = new IvParameterSpec(tag); IvParameterSpec ivSpec = new IvParameterSpec(iv);
try { try {
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
} catch(InvalidAlgorithmParameterException badIv) { } catch(InvalidAlgorithmParameterException badIv) {
throw new RuntimeException(badIv); throw new RuntimeException(badIv);
} catch(InvalidKeyException badKey) { } catch(InvalidKeyException badKey) {

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_LENGTH;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.util.HashMap; import java.util.HashMap;
@@ -27,9 +27,9 @@ DatabaseListener {
private final int transportId; private final int transportId;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final DatabaseComponent db; private final DatabaseComponent db;
private final Map<Bytes, ContactId> tagToContact; private final Map<Bytes, ContactId> ivToContact;
private final Map<Bytes, Long> tagToConnectionNumber; private final Map<Bytes, Long> ivToConnectionNumber;
private final Map<ContactId, Map<Long, Bytes>> contactToTags; private final Map<ContactId, Map<Long, Bytes>> contactToIvs;
private final Map<ContactId, Cipher> contactToCipher; private final Map<ContactId, Cipher> contactToCipher;
private final Map<ContactId, ConnectionWindow> contactToWindow; private final Map<ContactId, ConnectionWindow> contactToWindow;
private boolean initialised = false; private boolean initialised = false;
@@ -40,9 +40,9 @@ DatabaseListener {
this.crypto = crypto; this.crypto = crypto;
this.db = db; this.db = db;
// FIXME: There's probably a tidier way of maintaining all this state // FIXME: There's probably a tidier way of maintaining all this state
tagToContact = new HashMap<Bytes, ContactId>(); ivToContact = new HashMap<Bytes, ContactId>();
tagToConnectionNumber = new HashMap<Bytes, Long>(); ivToConnectionNumber = new HashMap<Bytes, Long>();
contactToTags = new HashMap<ContactId, Map<Long, Bytes>>(); contactToIvs = new HashMap<ContactId, Map<Long, Bytes>>();
contactToCipher = new HashMap<ContactId, Cipher>(); contactToCipher = new HashMap<ContactId, Cipher>();
contactToWindow = new HashMap<ContactId, ConnectionWindow>(); contactToWindow = new HashMap<ContactId, ConnectionWindow>();
db.addListener(this); db.addListener(this);
@@ -51,26 +51,26 @@ DatabaseListener {
private synchronized void initialise() throws DbException { private synchronized void initialise() throws DbException {
for(ContactId c : db.getContacts()) { for(ContactId c : db.getContacts()) {
try { try {
// Initialise and store the contact's tag cipher // Initialise and store the contact's IV cipher
byte[] secret = db.getSharedSecret(c); byte[] secret = db.getSharedSecret(c);
SecretKey tagKey = crypto.deriveIncomingTagKey(secret); SecretKey ivKey = crypto.deriveIncomingIvKey(secret);
Cipher cipher = crypto.getTagCipher(); Cipher cipher = crypto.getIvCipher();
try { try {
cipher.init(Cipher.ENCRYPT_MODE, tagKey); cipher.init(Cipher.ENCRYPT_MODE, ivKey);
} catch(InvalidKeyException badKey) { } catch(InvalidKeyException badKey) {
throw new RuntimeException(badKey); throw new RuntimeException(badKey);
} }
contactToCipher.put(c, cipher); contactToCipher.put(c, cipher);
// Calculate the tags for the contact's connection window // Calculate the IVs for the contact's connection window
ConnectionWindow w = db.getConnectionWindow(c, transportId); ConnectionWindow w = db.getConnectionWindow(c, transportId);
Map<Long, Bytes> tags = new HashMap<Long, Bytes>(); Map<Long, Bytes> ivs = new HashMap<Long, Bytes>();
for(Long unseen : w.getUnseenConnectionNumbers()) { for(Long unseen : w.getUnseenConnectionNumbers()) {
Bytes expectedTag = new Bytes(calculateTag(c, unseen)); Bytes expectedIv = new Bytes(encryptIv(c, unseen));
tagToContact.put(expectedTag, c); ivToContact.put(expectedIv, c);
tagToConnectionNumber.put(expectedTag, unseen); ivToConnectionNumber.put(expectedIv, unseen);
tags.put(unseen, expectedTag); ivs.put(unseen, expectedIv);
} }
contactToTags.put(c, tags); contactToIvs.put(c, ivs);
contactToWindow.put(c, w); contactToWindow.put(c, w);
} catch(NoSuchContactException e) { } catch(NoSuchContactException e) {
// The contact was removed after the call to getContacts() // The contact was removed after the call to getContacts()
@@ -80,12 +80,12 @@ DatabaseListener {
initialised = true; initialised = true;
} }
private synchronized byte[] calculateTag(ContactId c, long connection) { private synchronized byte[] encryptIv(ContactId c, long connection) {
byte[] tag = TagEncoder.encodeTag(transportId, connection); byte[] iv = IvEncoder.encodeIv(transportId, connection);
Cipher cipher = contactToCipher.get(c); Cipher cipher = contactToCipher.get(c);
assert cipher != null; assert cipher != null;
try { try {
return cipher.doFinal(tag); return cipher.doFinal(iv);
} catch(BadPaddingException badCipher) { } catch(BadPaddingException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} catch(IllegalBlockSizeException badCipher) { } catch(IllegalBlockSizeException badCipher) {
@@ -93,36 +93,36 @@ DatabaseListener {
} }
} }
public synchronized ContactId acceptConnection(byte[] tag) public synchronized ContactId acceptConnection(byte[] encryptedIv)
throws DbException { throws DbException {
if(tag.length != TAG_LENGTH) if(encryptedIv.length != IV_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if(!initialised) initialise(); if(!initialised) initialise();
Bytes b = new Bytes(tag); Bytes b = new Bytes(encryptedIv);
ContactId contactId = tagToContact.remove(b); ContactId contactId = ivToContact.remove(b);
Long connection = tagToConnectionNumber.remove(b); Long connection = ivToConnectionNumber.remove(b);
assert (contactId == null) == (connection == null); assert (contactId == null) == (connection == null);
if(contactId == null) return null; if(contactId == null) return null;
// The tag was expected - update and save the connection window // The IV was expected - update and save the connection window
ConnectionWindow w = contactToWindow.get(contactId); ConnectionWindow w = contactToWindow.get(contactId);
assert w != null; assert w != null;
w.setSeen(connection); w.setSeen(connection);
db.setConnectionWindow(contactId, transportId, w); db.setConnectionWindow(contactId, transportId, w);
// Update the set of expected tags // Update the set of expected IVs
Map<Long, Bytes> oldTags = contactToTags.remove(contactId); Map<Long, Bytes> oldIvs = contactToIvs.remove(contactId);
assert oldTags != null; assert oldIvs != null;
assert oldTags.containsKey(connection); assert oldIvs.containsKey(connection);
Map<Long, Bytes> newTags = new HashMap<Long, Bytes>(); Map<Long, Bytes> newIvs = new HashMap<Long, Bytes>();
for(Long unseen : w.getUnseenConnectionNumbers()) { for(Long unseen : w.getUnseenConnectionNumbers()) {
Bytes expectedTag = oldTags.get(unseen); Bytes expectedIv = oldIvs.get(unseen);
if(expectedTag == null) { if(expectedIv == null) {
expectedTag = new Bytes(calculateTag(contactId, unseen)); expectedIv = new Bytes(encryptIv(contactId, unseen));
tagToContact.put(expectedTag, contactId); ivToContact.put(expectedIv, contactId);
tagToConnectionNumber.put(expectedTag, connection); ivToConnectionNumber.put(expectedIv, connection);
} }
newTags.put(unseen, expectedTag); newIvs.put(unseen, expectedIv);
} }
contactToTags.put(contactId, newTags); contactToIvs.put(contactId, newIvs);
return contactId; return contactId;
} }

View File

@@ -25,9 +25,9 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
public ConnectionWriter createConnectionWriter(OutputStream out, public ConnectionWriter createConnectionWriter(OutputStream out,
int transportId, long connection, byte[] secret) { int transportId, long connection, byte[] secret) {
SecretKey macKey = crypto.deriveOutgoingMacKey(secret); SecretKey macKey = crypto.deriveOutgoingMacKey(secret);
SecretKey tagKey = crypto.deriveOutgoingTagKey(secret); SecretKey ivKey = crypto.deriveOutgoingIvKey(secret);
SecretKey frameKey = crypto.deriveOutgoingFrameKey(secret); SecretKey frameKey = crypto.deriveOutgoingFrameKey(secret);
Cipher tagCipher = crypto.getTagCipher(); Cipher ivCipher = crypto.getIvCipher();
Cipher frameCipher = crypto.getFrameCipher(); Cipher frameCipher = crypto.getFrameCipher();
Mac mac = crypto.getMac(); Mac mac = crypto.getMac();
try { try {
@@ -36,7 +36,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
throw new IllegalArgumentException(badKey); throw new IllegalArgumentException(badKey);
} }
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out, ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
transportId, connection, tagCipher, frameCipher, tagKey, transportId, connection, ivCipher, frameCipher, ivKey,
frameKey); frameKey);
return new ConnectionWriterImpl(encrypter, mac); return new ConnectionWriterImpl(encrypter, mac);
} }

View File

@@ -0,0 +1,33 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
import net.sf.briar.util.ByteUtils;
class IvEncoder {
static byte[] encodeIv(int transportId, long connection) {
byte[] iv = new byte[IV_LENGTH];
// 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);
return iv;
}
static void encodeIv(byte[] iv, int transportId, long connection,
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;
}
}

View File

@@ -1,20 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import net.sf.briar.util.ByteUtils;
class TagDecoder {
static boolean decodeTag(byte[] tag, int transportId, long connection) {
if(tag.length != TAG_LENGTH) return false;
// First 32 bits must be zero (reserved)
for(int i = 0; i < 4; i++) if(tag[i] != 0) return false;
// Transport identifier is encoded as an unsigned 16-bit integer
if(ByteUtils.readUint16(tag, 4) != transportId) return false;
// Connection number is encoded as an unsigned 32-bit integer
if(ByteUtils.readUint32(tag, 6) != connection) return false;
// Last 48 bits must be zero (frame number and block number)
for(int i = 10; i < 16; i++) if(tag[i] != 0) return false;
return true;
}
}

View File

@@ -1,31 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import net.sf.briar.util.ByteUtils;
class TagEncoder {
static byte[] encodeTag(int transportId, long connection) {
byte[] tag = new byte[TAG_LENGTH];
// Encode the transport identifier as an unsigned 16-bit integer
ByteUtils.writeUint16(transportId, tag, 4);
// Encode the connection number as an unsigned 32-bit integer
ByteUtils.writeUint32(connection, tag, 6);
return tag;
}
static void encodeTag(byte[] tag, int transportId, long connection,
long frame) {
if(tag.length != TAG_LENGTH) throw new IllegalArgumentException();
// The first 16 bits of the tag must be zero (reserved)
ByteUtils.writeUint16(0, tag, 0);
// Encode the transport identifier as an unsigned 16-bit integer
ByteUtils.writeUint16(transportId, tag, 4);
// Encode the connection number as an unsigned 32-bit integer
ByteUtils.writeUint32(connection, tag, 6);
// Encode the frame number as an unsigned 32-bit integer
ByteUtils.writeUint32(frame, tag, 10);
// The last 16 bits of the tag must be zero (block number)
ByteUtils.writeUint16(0, tag, 14);
}
}

View File

@@ -185,10 +185,10 @@ public class FileReadWriteTest extends TestCase {
testWriteFile(); testWriteFile();
InputStream in = new FileInputStream(file); InputStream in = new FileInputStream(file);
byte[] firstTag = new byte[16]; byte[] iv = new byte[16];
int offset = 0; int offset = 0;
while(offset < 16) { while(offset < 16) {
int read = in.read(firstTag, offset, firstTag.length - offset); int read = in.read(iv, offset, iv.length - offset);
if(read == -1) break; if(read == -1) break;
offset += read; offset += read;
} }

View File

@@ -29,21 +29,21 @@ public class CryptoComponentTest extends TestCase {
crypto.deriveOutgoingMacKey(bobSecret)); crypto.deriveOutgoingMacKey(bobSecret));
assertEquals(crypto.deriveIncomingFrameKey(aliceSecret), assertEquals(crypto.deriveIncomingFrameKey(aliceSecret),
crypto.deriveOutgoingFrameKey(bobSecret)); crypto.deriveOutgoingFrameKey(bobSecret));
assertEquals(crypto.deriveIncomingTagKey(aliceSecret), assertEquals(crypto.deriveIncomingIvKey(aliceSecret),
crypto.deriveOutgoingTagKey(bobSecret)); crypto.deriveOutgoingIvKey(bobSecret));
// Check that Alice's outgoing keys match Bob's incoming keys // Check that Alice's outgoing keys match Bob's incoming keys
assertEquals(crypto.deriveOutgoingMacKey(aliceSecret), assertEquals(crypto.deriveOutgoingMacKey(aliceSecret),
crypto.deriveIncomingMacKey(bobSecret)); crypto.deriveIncomingMacKey(bobSecret));
assertEquals(crypto.deriveOutgoingFrameKey(aliceSecret), assertEquals(crypto.deriveOutgoingFrameKey(aliceSecret),
crypto.deriveIncomingFrameKey(bobSecret)); crypto.deriveIncomingFrameKey(bobSecret));
assertEquals(crypto.deriveOutgoingTagKey(aliceSecret), assertEquals(crypto.deriveOutgoingIvKey(aliceSecret),
crypto.deriveIncomingTagKey(bobSecret)); crypto.deriveIncomingIvKey(bobSecret));
// Check that Alice's incoming and outgoing keys are different // Check that Alice's incoming and outgoing keys are different
assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals( assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals(
crypto.deriveOutgoingMacKey(aliceSecret))); crypto.deriveOutgoingMacKey(aliceSecret)));
assertFalse(crypto.deriveIncomingFrameKey(aliceSecret).equals( assertFalse(crypto.deriveIncomingFrameKey(aliceSecret).equals(
crypto.deriveOutgoingFrameKey(aliceSecret))); crypto.deriveOutgoingFrameKey(aliceSecret)));
assertFalse(crypto.deriveIncomingTagKey(aliceSecret).equals( assertFalse(crypto.deriveIncomingIvKey(aliceSecret).equals(
crypto.deriveOutgoingTagKey(aliceSecret))); crypto.deriveOutgoingIvKey(aliceSecret)));
} }
} }

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_LENGTH;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.Arrays; import java.util.Arrays;
@@ -54,16 +54,16 @@ public class ConnectionDecrypterImplTest extends TestCase {
public void testDecryption() throws Exception { public void testDecryption() throws Exception {
// Calculate the expected plaintext for the first frame // Calculate the expected plaintext for the first frame
byte[] ciphertext = new byte[123]; byte[] ciphertext = new byte[123];
byte[] ivBytes = new byte[TAG_LENGTH]; byte[] iv = new byte[IV_LENGTH];
TagEncoder.encodeTag(ivBytes, transportId, connection, 0L); IvEncoder.encodeIv(iv, transportId, connection, 0L);
IvParameterSpec iv = new IvParameterSpec(ivBytes); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
byte[] plaintext = frameCipher.doFinal(ciphertext); byte[] plaintext = frameCipher.doFinal(ciphertext);
// Calculate the expected plaintext for the second frame // Calculate the expected plaintext for the second frame
byte[] ciphertext1 = new byte[1234]; byte[] ciphertext1 = new byte[1234];
TagEncoder.encodeTag(ivBytes, transportId, connection, 1L); IvEncoder.encodeIv(iv, transportId, connection, 1L);
iv = new IvParameterSpec(ivBytes); ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
byte[] plaintext1 = frameCipher.doFinal(ciphertext1); byte[] plaintext1 = frameCipher.doFinal(ciphertext1);
assertEquals(ciphertext1.length, plaintext1.length); assertEquals(ciphertext1.length, plaintext1.length);
// Concatenate the ciphertexts // Concatenate the ciphertexts

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_LENGTH;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.Arrays; import java.util.Arrays;
@@ -22,8 +22,8 @@ public class ConnectionEncrypterImplTest extends TestCase {
private static final int MAC_LENGTH = 32; private static final int MAC_LENGTH = 32;
private final Cipher tagCipher, frameCipher; private final Cipher ivCipher, frameCipher;
private final SecretKey tagKey, frameKey; private final SecretKey ivKey, frameKey;
private final int transportId = 1234; private final int transportId = 1234;
private final long connection = 12345L; private final long connection = 12345L;
@@ -31,9 +31,9 @@ public class ConnectionEncrypterImplTest extends TestCase {
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(); ivCipher = crypto.getIvCipher();
frameCipher = crypto.getFrameCipher(); frameCipher = crypto.getFrameCipher();
tagKey = crypto.generateSecretKey(); ivKey = crypto.generateSecretKey();
frameKey = crypto.generateSecretKey(); frameKey = crypto.generateSecretKey();
} }
@@ -41,27 +41,26 @@ public class ConnectionEncrypterImplTest extends TestCase {
public void testSingleByteFrame() throws Exception { public void testSingleByteFrame() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId, ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId,
connection, tagCipher, frameCipher, tagKey, frameKey); connection, ivCipher, frameCipher, ivKey, frameKey);
e.getOutputStream().write((byte) 0); e.getOutputStream().write((byte) 0);
e.writeMac(new byte[MAC_LENGTH]); e.writeMac(new byte[MAC_LENGTH]);
assertEquals(TAG_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length); assertEquals(IV_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length);
} }
@Test @Test
public void testEncryption() throws Exception { public void testEncryption() throws Exception {
// Calculate the expected ciphertext for the tag // Calculate the expected ciphertext for the IV
byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection); byte[] iv = IvEncoder.encodeIv(transportId, connection);
assertEquals(TAG_LENGTH, plaintextTag.length); assertEquals(IV_LENGTH, iv.length);
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
byte[] tag = tagCipher.doFinal(plaintextTag); byte[] encryptedIv = ivCipher.doFinal(iv);
assertEquals(TAG_LENGTH, tag.length); assertEquals(IV_LENGTH, encryptedIv.length);
// Calculate the expected ciphertext for the first frame // Calculate the expected ciphertext for the first frame
byte[] plaintext = new byte[123]; byte[] plaintext = new byte[123];
byte[] plaintextMac = new byte[MAC_LENGTH]; byte[] plaintextMac = new byte[MAC_LENGTH];
byte[] ivBytes = new byte[TAG_LENGTH]; IvEncoder.encodeIv(iv, transportId, connection, 0L);
TagEncoder.encodeTag(ivBytes, transportId, connection, 0L); IvParameterSpec ivSpec = new IvParameterSpec(iv);
IvParameterSpec iv = new IvParameterSpec(ivBytes); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv);
byte[] ciphertext = new byte[plaintext.length + plaintextMac.length]; byte[] ciphertext = new byte[plaintext.length + plaintextMac.length];
int offset = frameCipher.update(plaintext, 0, plaintext.length, int offset = frameCipher.update(plaintext, 0, plaintext.length,
ciphertext); ciphertext);
@@ -69,9 +68,9 @@ public class ConnectionEncrypterImplTest extends TestCase {
offset); offset);
// Calculate the expected ciphertext for the second frame // Calculate the expected ciphertext for the second frame
byte[] plaintext1 = new byte[1234]; byte[] plaintext1 = new byte[1234];
TagEncoder.encodeTag(ivBytes, transportId, connection, 1L); IvEncoder.encodeIv(iv, transportId, connection, 1L);
iv = new IvParameterSpec(ivBytes); ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = new byte[plaintext1.length + plaintextMac.length]; byte[] ciphertext1 = new byte[plaintext1.length + plaintextMac.length];
offset = frameCipher.update(plaintext1, 0, plaintext1.length, offset = frameCipher.update(plaintext1, 0, plaintext1.length,
ciphertext1); ciphertext1);
@@ -79,14 +78,14 @@ public class ConnectionEncrypterImplTest extends TestCase {
offset); offset);
// Concatenate the ciphertexts // Concatenate the ciphertexts
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag); out.write(encryptedIv);
out.write(ciphertext); out.write(ciphertext);
out.write(ciphertext1); out.write(ciphertext1);
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();
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId, ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId,
connection, tagCipher, frameCipher, tagKey, frameKey); connection, ivCipher, frameCipher, ivKey, 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

@@ -33,6 +33,8 @@ public class ConnectionReaderImplTest extends TestCase {
mac.init(crypto.generateSecretKey()); mac.init(crypto.generateSecretKey());
} }
// FIXME: Test corner cases and corrupt frames
@Test @Test
public void testSingleByteFrame() throws Exception { public void testSingleByteFrame() throws Exception {
// Six bytes for the header, one for the payload // Six bytes for the header, one for the payload

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_LENGTH;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -41,7 +41,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
} }
@Test @Test
public void testUnexpectedTag() throws Exception { public void testUnexpectedIv() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -55,18 +55,18 @@ public class ConnectionRecogniserImplTest extends TestCase {
}}); }});
final ConnectionRecogniserImpl c = final ConnectionRecogniserImpl c =
new ConnectionRecogniserImpl(transportId, crypto, db); new ConnectionRecogniserImpl(transportId, crypto, db);
assertNull(c.acceptConnection(new byte[TAG_LENGTH])); assertNull(c.acceptConnection(new byte[IV_LENGTH]));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testExpectedTag() throws Exception { public void testExpectedIv() throws Exception {
// Calculate the expected tag for connection number 3 // Calculate the expected IV for connection number 3
SecretKey tagKey = crypto.deriveIncomingTagKey(secret); SecretKey ivKey = crypto.deriveIncomingIvKey(secret);
Cipher tagCipher = crypto.getTagCipher(); Cipher ivCipher = crypto.getIvCipher();
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
byte[] tag = TagEncoder.encodeTag(transportId, 3L); byte[] iv = IvEncoder.encodeIv(transportId, 3L);
byte[] encryptedTag = tagCipher.doFinal(tag); byte[] encryptedIv = ivCipher.doFinal(iv);
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
@@ -83,10 +83,10 @@ public class ConnectionRecogniserImplTest extends TestCase {
}}); }});
final ConnectionRecogniserImpl c = final ConnectionRecogniserImpl c =
new ConnectionRecogniserImpl(transportId, crypto, db); new ConnectionRecogniserImpl(transportId, crypto, db);
// First time - the tag should be expected // First time - the IV should be expected
assertEquals(contactId, c.acceptConnection(encryptedTag)); assertEquals(contactId, c.acceptConnection(encryptedIv));
// Second time - the tag should no longer be expected // Second time - the IV should no longer be expected
assertNull(c.acceptConnection(encryptedTag)); assertNull(c.acceptConnection(encryptedIv));
// The window should have advanced // The window should have advanced
assertEquals(4L, connectionWindow.getCentre()); assertEquals(4L, connectionWindow.getCentre());
Collection<Long> unseen = connectionWindow.getUnseenConnectionNumbers(); Collection<Long> unseen = connectionWindow.getUnseenConnectionNumbers();

View File

@@ -30,6 +30,8 @@ public class ConnectionWriterImplTest extends TestCase {
mac.init(crypto.generateSecretKey()); mac.init(crypto.generateSecretKey());
} }
// FIXME: Test corner cases
@Test @Test
public void testSingleByteFrame() throws Exception { public void testSingleByteFrame() throws Exception {
// Six bytes for the header, one for the payload // Six bytes for the header, one for the payload

View File

@@ -1,6 +1,6 @@
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.api.transport.TransportConstants.IV_LENGTH;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -27,8 +27,8 @@ import com.google.inject.Injector;
public class FrameReadWriteTest extends TestCase { public class FrameReadWriteTest extends TestCase {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final Cipher tagCipher, frameCipher; private final Cipher ivCipher, frameCipher;
private final SecretKey macKey, tagKey, frameKey; private final SecretKey ivKey, frameKey, macKey;
private final Mac mac; private final Mac mac;
private final Random random; private final Random random;
private final byte[] secret = new byte[100]; private final byte[] secret = new byte[100];
@@ -39,24 +39,24 @@ public class FrameReadWriteTest extends TestCase {
super(); super();
Injector i = Guice.createInjector(new CryptoModule()); Injector i = Guice.createInjector(new CryptoModule());
crypto = i.getInstance(CryptoComponent.class); crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher(); ivCipher = crypto.getIvCipher();
frameCipher = crypto.getFrameCipher(); frameCipher = crypto.getFrameCipher();
// Since we're sending packets to ourselves, we only need outgoing keys // Since we're sending frames to ourselves, we only need outgoing keys
macKey = crypto.deriveOutgoingMacKey(secret); ivKey = crypto.deriveOutgoingIvKey(secret);
tagKey = crypto.deriveOutgoingTagKey(secret);
frameKey = crypto.deriveOutgoingFrameKey(secret); frameKey = crypto.deriveOutgoingFrameKey(secret);
macKey = crypto.deriveOutgoingMacKey(secret);
mac = crypto.getMac(); mac = crypto.getMac();
random = new Random(); random = new Random();
} }
@Test @Test
public void testWriteAndRead() throws Exception { public void testWriteAndRead() throws Exception {
// Calculate the expected ciphertext for the tag // Calculate the expected ciphertext for the IV
byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection); byte[] iv = IvEncoder.encodeIv(transportId, connection);
assertEquals(TAG_LENGTH, plaintextTag.length); assertEquals(IV_LENGTH, iv.length);
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
byte[] tag = tagCipher.doFinal(plaintextTag); byte[] encryptedIv = ivCipher.doFinal(iv);
assertEquals(TAG_LENGTH, tag.length); assertEquals(IV_LENGTH, encryptedIv.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);
@@ -65,7 +65,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,
transportId, connection, tagCipher, frameCipher, tagKey, transportId, connection, ivCipher, frameCipher, ivKey,
frameKey); frameKey);
mac.init(macKey); mac.init(macKey);
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac); ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac);
@@ -76,9 +76,9 @@ public class FrameReadWriteTest extends TestCase {
out1.flush(); out1.flush();
// Read the frames back // Read the frames back
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
byte[] recoveredTag = new byte[TAG_LENGTH]; byte[] recoveredIv = new byte[IV_LENGTH];
assertEquals(TAG_LENGTH, in.read(recoveredTag)); assertEquals(IV_LENGTH, in.read(recoveredIv));
assertTrue(Arrays.equals(tag, recoveredTag)); assertTrue(Arrays.equals(encryptedIv, recoveredIv));
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
transportId, connection, frameCipher, frameKey); transportId, connection, frameCipher, frameKey);
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac); ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac);