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 KEY_PAIR_ALGO = "ECDSA";
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 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 SIGNATURE_ALGO = "ECDSA";
private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
// Labels for key derivation, null-terminated
@@ -104,7 +102,7 @@ class CryptoComponentImpl implements CryptoComponent {
assert ivBytes[ivBytes.length - 1] == 0;
IvParameterSpec iv = new IvParameterSpec(ivBytes);
try {
Cipher cipher = Cipher.getInstance(KEY_DERIVATION_ALGO, PROVIDER);
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] output = cipher.doFinal(KEY_DERIVATION_INPUT);
assert output.length == SECRET_KEY_BYTES;
@@ -137,7 +135,7 @@ class CryptoComponentImpl implements CryptoComponent {
public Cipher getFrameCipher() {
try {
return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
return Cipher.getInstance(CIPHER_ALGO, PROVIDER);
} catch(GeneralSecurityException e) {
throw new RuntimeException(e);
}
@@ -178,7 +176,7 @@ class CryptoComponentImpl implements CryptoComponent {
public Cipher getTagCipher() {
try {
return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
return Cipher.getInstance(CIPHER_ALGO, PROVIDER);
} catch(GeneralSecurityException e) {
throw new RuntimeException(e);
}

View File

@@ -1,6 +1,5 @@
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 java.io.EOFException;
@@ -13,10 +12,11 @@ import java.security.InvalidKeyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import net.sf.briar.api.crypto.ErasableKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.crypto.ErasableKey;
class ConnectionDecrypterImpl extends FilterInputStream
implements ConnectionDecrypter {
@@ -28,14 +28,13 @@ implements ConnectionDecrypter {
private long frame = 0L;
private boolean betweenFrames = true;
ConnectionDecrypterImpl(InputStream in, byte[] iv, Cipher frameCipher,
ConnectionDecrypterImpl(InputStream in, Cipher frameCipher,
ErasableKey frameKey) {
super(in);
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
this.iv = iv;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
buf = new byte[TAG_LENGTH];
iv = IvEncoder.encodeIv(0, frameCipher.getBlockSize());
buf = new byte[frameCipher.getBlockSize()];
}
public InputStream getInputStream() {

View File

@@ -25,27 +25,17 @@ implements ConnectionEncrypter {
private long capacity, frame = 0L;
private boolean tagWritten = false, betweenFrames = false;
ConnectionEncrypterImpl(OutputStream out, long capacity, byte[] iv,
Cipher tagCipher, Cipher frameCipher, ErasableKey tagKey,
ErasableKey frameKey) {
ConnectionEncrypterImpl(OutputStream out, long capacity, Cipher tagCipher,
Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey) {
super(out);
this.capacity = capacity;
this.iv = iv;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
iv = IvEncoder.encodeIv(0, frameCipher.getBlockSize());
// Encrypt the tag
try {
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();
tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
tagKey.erase();
if(tag.length != TAG_LENGTH) throw new IllegalArgumentException();
}
public OutputStream getOutputStream() {

View File

@@ -1,11 +1,8 @@
package net.sf.briar.transport;
import java.io.InputStream;
import java.security.InvalidKeyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import net.sf.briar.api.crypto.CryptoComponent;
@@ -28,26 +25,12 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
public ConnectionReader createConnectionReader(InputStream in,
ConnectionContext ctx, byte[] tag) {
// Decrypt the tag
// Validate the tag
Cipher tagCipher = crypto.getTagCipher();
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
byte[] iv;
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);
}
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey);
tagKey.erase();
// Validate the tag
int index = ctx.getTransportIndex().getInt();
long connection = ctx.getConnectionNumber();
if(!IvEncoder.validateIv(iv, index, connection))
throw new IllegalArgumentException();
if(!valid) throw new IllegalArgumentException();
return createConnectionReader(in, true, ctx);
}
@@ -64,11 +47,8 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
ByteUtils.erase(secret);
// Create the decrypter
int index = ctx.getTransportIndex().getInt();
long connection = ctx.getConnectionNumber();
byte[] iv = IvEncoder.encodeIv(index, connection);
Cipher frameCipher = crypto.getFrameCipher();
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
frameCipher, frameKey);
// Create the reader
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 java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -15,9 +14,7 @@ import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import net.sf.briar.api.Bytes;
import net.sf.briar.api.ContactId;
@@ -103,22 +100,10 @@ DatabaseListener {
// Locking: this
private Bytes calculateTag(Context ctx, byte[] secret) {
byte[] iv = IvEncoder.encodeIv(ctx.transportIndex.getInt(),
ctx.connection);
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
try {
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
byte[] tag = tagCipher.doFinal(iv);
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();
}
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
tagKey.erase();
return new Bytes(tag);
}
public void acceptConnection(final TransportId t, final byte[] tag,

View File

@@ -1,11 +1,8 @@
package net.sf.briar.transport;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import net.sf.briar.api.crypto.CryptoComponent;
@@ -36,23 +33,9 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
// Decrypt the tag
Cipher tagCipher = crypto.getTagCipher();
ErasableKey tagKey = crypto.deriveTagKey(ctx.getSecret(), true);
byte[] iv;
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);
}
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey);
tagKey.erase();
// Validate the tag
int index = ctx.getTransportIndex().getInt();
long connection = ctx.getConnectionNumber();
if(!IvEncoder.validateIv(iv, index, connection))
throw new IllegalArgumentException();
if(!valid) throw new IllegalArgumentException();
return createConnectionWriter(out, capacity, false, ctx);
}
@@ -65,13 +48,10 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
ByteUtils.erase(secret);
// Create the encrypter
int index = ctx.getTransportIndex().getInt();
long connection = ctx.getConnectionNumber();
byte[] iv = IvEncoder.encodeIv(index, connection);
Cipher tagCipher = crypto.getTagCipher();
Cipher frameCipher = crypto.getFrameCipher();
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
capacity, iv, tagCipher, frameCipher, tagKey, frameKey);
capacity, tagCipher, frameCipher, tagKey, frameKey);
// Create the writer
Mac mac = crypto.getMac();
return new ConnectionWriterImpl(encrypter, mac, macKey);

View File

@@ -1,45 +1,19 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import net.sf.briar.util.ByteUtils;
class IvEncoder {
static byte[] encodeIv(int index, long connection) {
byte[] iv = new byte[TAG_LENGTH];
// Encode the transport index as an unsigned 16-bit integer
ByteUtils.writeUint16(index, iv, 4);
// Encode the connection number as an unsigned 32-bit integer
ByteUtils.writeUint32(connection, iv, 6);
static byte[] encodeIv(long frame, int blockSize) {
if(frame > ByteUtils.MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
byte[] iv = new byte[blockSize];
updateIv(iv, frame);
return iv;
}
static void updateIv(byte[] iv, long frame) {
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
// Encode the frame number as an unsigned 32-bit integer
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);
// Encode the frame number as a uint32, leaving 2 bytes for the counter
ByteUtils.writeUint32(frame, iv, iv.length - 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;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayInputStream;
import javax.crypto.Cipher;
import net.sf.briar.api.crypto.ErasableKey;
import javax.crypto.spec.IvParameterSpec;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
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 org.apache.commons.io.output.ByteArrayOutputStream;
@@ -25,18 +23,14 @@ public class ConnectionDecrypterImplTest extends TestCase {
private static final int MAC_LENGTH = 32;
private final Cipher tagCipher, frameCipher;
private final ErasableKey tagKey, frameKey;
private final TransportIndex transportIndex = new TransportIndex(13);
private final long connection = 12345L;
private final Cipher frameCipher;
private final ErasableKey frameKey;
public ConnectionDecrypterImplTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
frameCipher = crypto.getFrameCipher();
tagKey = crypto.generateTestKey();
frameKey = crypto.generateTestKey();
}
@@ -51,12 +45,8 @@ public class ConnectionDecrypterImplTest extends TestCase {
}
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
byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] ciphertext = new byte[123];
byte[] ciphertextMac = new byte[MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
@@ -84,9 +74,8 @@ public class ConnectionDecrypterImplTest extends TestCase {
out.write(ciphertextMac);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Use a ConnectionDecrypter to decrypt the ciphertext
ConnectionDecrypter d = new ConnectionDecrypterImpl(in,
IvEncoder.encodeIv(transportIndex.getInt(), connection),
frameCipher, frameKey);
ConnectionDecrypter d = new ConnectionDecrypterImpl(in, frameCipher,
frameKey);
// First frame
byte[] decrypted = new byte[ciphertext.length];
TestUtils.readFully(d.getInputStream(), decrypted);

View File

@@ -1,17 +1,15 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayOutputStream;
import javax.crypto.Cipher;
import net.sf.briar.api.crypto.ErasableKey;
import javax.crypto.spec.IvParameterSpec;
import junit.framework.TestCase;
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 org.junit.Test;
@@ -25,8 +23,6 @@ public class ConnectionEncrypterImplTest extends TestCase {
private final Cipher tagCipher, frameCipher;
private final ErasableKey tagKey, frameKey;
private final TransportIndex transportIndex = new TransportIndex(13);
private final long connection = 12345L;
public ConnectionEncrypterImplTest() {
super();
@@ -49,12 +45,10 @@ public class ConnectionEncrypterImplTest extends TestCase {
}
private void testEncryption(boolean initiator) throws Exception {
// Calculate the expected 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 tag
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame
byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] plaintext = new byte[123];
byte[] plaintextMac = new byte[MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
@@ -82,9 +76,8 @@ public class ConnectionEncrypterImplTest extends TestCase {
byte[] expected = out.toByteArray();
// Use a ConnectionEncrypter to encrypt the plaintext
out.reset();
iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
iv, tagCipher, frameCipher, tagKey, frameKey);
tagCipher, frameCipher, tagKey, frameKey);
e.getOutputStream().write(plaintext);
e.writeMac(plaintextMac);
e.getOutputStream().write(plaintext1);

View File

@@ -617,8 +617,6 @@ public class ConnectionRecogniserImplTest extends TestCase {
// Calculate the expected tag for connection number 3
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
Cipher tagCipher = crypto.getTagCipher();
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
byte[] iv = IvEncoder.encodeIv(remoteIndex.getInt(), 3);
return tagCipher.doFinal(iv);
return TagEncoder.encodeTag(0, tagCipher, tagKey);
}
}

View File

@@ -11,11 +11,10 @@ import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import net.sf.briar.api.crypto.ErasableKey;
import junit.framework.TestCase;
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.ConnectionWriter;
import net.sf.briar.crypto.CryptoModule;
@@ -33,8 +32,6 @@ public class FrameReadWriteTest extends TestCase {
private final byte[] outSecret;
private final ErasableKey tagKey, frameKey, macKey;
private final Mac mac;
private final TransportIndex transportIndex = new TransportIndex(13);
private final long connection = 12345L;
public FrameReadWriteTest() {
super();
@@ -63,11 +60,8 @@ public class FrameReadWriteTest extends TestCase {
}
private void testWriteAndRead(boolean initiator) throws Exception {
// Create and encrypt 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);
// Encode the tag
byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey);
// Generate two random frames
byte[] frame = new byte[12345];
random.nextBytes(frame);
@@ -80,7 +74,7 @@ public class FrameReadWriteTest extends TestCase {
// Write the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
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,
macCopy);
OutputStream out1 = writer.getOutputStream();
@@ -88,18 +82,14 @@ public class FrameReadWriteTest extends TestCase {
out1.flush();
out1.write(frame1);
out1.flush();
// Read the IV back
// Read the tag back
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
byte[] recoveredTag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(recoveredTag));
assertArrayEquals(tag, recoveredTag);
// Decrypt the IV
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
byte[] recoveredIv = tagCipher.doFinal(recoveredTag);
iv = IvEncoder.encodeIv(transportIndex.getInt(), connection);
assertArrayEquals(iv, recoveredIv);
assertTrue(TagEncoder.validateTag(tag, 0, tagCipher, tagKey));
// Read the frames back
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
frameCipher, frameKey);
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac,
macKey);