diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index d3f8d0353..59e5ae547 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -6,7 +6,6 @@ import java.security.SecureRandom;
import java.security.Signature;
import javax.crypto.Cipher;
-import javax.crypto.Mac;
public interface CryptoComponent {
@@ -14,8 +13,6 @@ public interface CryptoComponent {
ErasableKey deriveFrameKey(byte[] secret, boolean initiator);
- ErasableKey deriveMacKey(byte[] secret, boolean initiator);
-
byte[][] deriveInitialSecrets(byte[] ourPublicKey, byte[] theirPublicKey,
PrivateKey ourPrivateKey, int invitationCode, boolean initiator);
@@ -41,7 +38,11 @@ public interface CryptoComponent {
Cipher getFrameCipher();
- Signature getSignature();
+ Cipher getFramePeekingCipher();
- Mac getMac();
+ IvEncoder getFrameIvEncoder();
+
+ IvEncoder getFramePeekingIvEncoder();
+
+ Signature getSignature();
}
diff --git a/api/net/sf/briar/api/crypto/IvEncoder.java b/api/net/sf/briar/api/crypto/IvEncoder.java
new file mode 100644
index 000000000..12d24a545
--- /dev/null
+++ b/api/net/sf/briar/api/crypto/IvEncoder.java
@@ -0,0 +1,8 @@
+package net.sf.briar.api.crypto;
+
+public interface IvEncoder {
+
+ byte[] encodeIv(long frameNumber);
+
+ void updateIv(byte[] iv, long frameNumber);
+}
diff --git a/api/net/sf/briar/api/transport/TransportConstants.java b/api/net/sf/briar/api/transport/TransportConstants.java
index afd01abd8..a37b81007 100644
--- a/api/net/sf/briar/api/transport/TransportConstants.java
+++ b/api/net/sf/briar/api/transport/TransportConstants.java
@@ -12,7 +12,7 @@ public interface TransportConstants {
static final int FRAME_HEADER_LENGTH = 9;
/** The length of the MAC in bytes. */
- static final int MAC_LENGTH = 48;
+ static final int MAC_LENGTH = 16;
/**
* The minimum connection length in bytes that all transport plugins must
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 4fd74b05f..226c99221 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -13,11 +13,11 @@ import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.api.crypto.KeyParser;
import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.crypto.PseudoRandom;
@@ -35,20 +35,19 @@ class CryptoComponentImpl implements CryptoComponent {
private static final String AGREEMENT_ALGO = "ECDHC";
private static final String SECRET_KEY_ALGO = "AES";
private static final int SECRET_KEY_BYTES = 32; // 256 bits
- private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
+ private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
private static final String DIGEST_ALGO = "SHA-384";
private static final String SIGNATURE_KEY_PAIR_ALGO = "ECDSA";
private static final int SIGNATURE_KEY_PAIR_BITS = 384;
private static final String SIGNATURE_ALGO = "ECDSA";
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
- private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
- private static final String MAC_ALGO = "HMacSHA384";
+ private static final String FRAME_CIPHER_ALGO = "AES/GCM/NoPadding";
+ private static final String FRAME_PEEKING_CIPHER_ALGO = "AES/CTR/NoPadding";
// Labels for key derivation
private static final byte[] TAG = { 'T', 'A', 'G' };
private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E' };
- private static final byte[] MAC = { 'M', 'A', 'C' };
// Labels for secret derivation
private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T' };
private static final byte[] NEXT = { 'N', 'E', 'X', 'T' };
@@ -96,11 +95,6 @@ class CryptoComponentImpl implements CryptoComponent {
else return deriveKey(secret, FRAME, RESPONDER);
}
- public ErasableKey deriveMacKey(byte[] secret, boolean initiator) {
- if(initiator) return deriveKey(secret, MAC, INITIATOR);
- else return deriveKey(secret, MAC, RESPONDER);
- }
-
private ErasableKey deriveKey(byte[] secret, byte[] label, byte[] context) {
byte[] key = counterModeKdf(secret, label, context);
return new ErasableKeyImpl(key, SECRET_KEY_ALGO);
@@ -289,11 +283,19 @@ class CryptoComponentImpl implements CryptoComponent {
}
}
- public Mac getMac() {
+ public Cipher getFramePeekingCipher() {
try {
- return Mac.getInstance(MAC_ALGO, PROVIDER);
+ return Cipher.getInstance(FRAME_PEEKING_CIPHER_ALGO, PROVIDER);
} catch(GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
+
+ public IvEncoder getFrameIvEncoder() {
+ return new FrameIvEncoder();
+ }
+
+ public IvEncoder getFramePeekingIvEncoder() {
+ return new FramePeekingIvEncoder();
+ }
}
diff --git a/components/net/sf/briar/crypto/FrameIvEncoder.java b/components/net/sf/briar/crypto/FrameIvEncoder.java
new file mode 100644
index 000000000..6703669f6
--- /dev/null
+++ b/components/net/sf/briar/crypto/FrameIvEncoder.java
@@ -0,0 +1,26 @@
+package net.sf.briar.crypto;
+
+import net.sf.briar.api.crypto.IvEncoder;
+import net.sf.briar.util.ByteUtils;
+
+class FrameIvEncoder implements IvEncoder {
+
+ // AES-GCM uses a 96-bit IV; the bytes 0x00, 0x00, 0x00, 0x02 are
+ // appended internally (see NIST SP 800-38D, section 7.1)
+ private static final int IV_LENGTH = 12;
+
+ public byte[] encodeIv(long frame) {
+ if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
+ throw new IllegalArgumentException();
+ byte[] iv = new byte[IV_LENGTH];
+ updateIv(iv, frame);
+ return iv;
+ }
+
+ public void updateIv(byte[] iv, long frame) {
+ if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
+ throw new IllegalArgumentException();
+ // Encode the frame number as a uint32
+ ByteUtils.writeUint32(frame, iv, 0);
+ }
+}
diff --git a/components/net/sf/briar/crypto/FramePeekingIvEncoder.java b/components/net/sf/briar/crypto/FramePeekingIvEncoder.java
new file mode 100644
index 000000000..85d5dbb06
--- /dev/null
+++ b/components/net/sf/briar/crypto/FramePeekingIvEncoder.java
@@ -0,0 +1,20 @@
+package net.sf.briar.crypto;
+
+import net.sf.briar.util.ByteUtils;
+
+class FramePeekingIvEncoder extends FrameIvEncoder {
+
+ // AES/CTR uses a 128-bit IV; to match the AES/GCM IV we have to append
+ // the bytes 0x00, 0x00, 0x00, 0x02 (see NIST SP 800-38D, section 7.1)
+ private static final int IV_LENGTH = 16;
+
+ @Override
+ public byte[] encodeIv(long frame) {
+ if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
+ throw new IllegalArgumentException();
+ byte[] iv = new byte[IV_LENGTH];
+ iv[IV_LENGTH - 1] = 2;
+ updateIv(iv, frame);
+ return iv;
+ }
+}
diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index 8163e047f..0bed032dd 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -3,10 +3,10 @@ package net.sf.briar.transport;
import java.io.InputStream;
import javax.crypto.Cipher;
-import javax.crypto.Mac;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.util.ByteUtils;
@@ -27,18 +27,16 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// Derive the keys and erase the secret
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
- ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
ByteUtils.erase(secret);
- // Encryption
+ // Create the reader
Cipher tagCipher = crypto.getTagCipher();
Cipher frameCipher = crypto.getFrameCipher();
+ Cipher framePeekingCipher = crypto.getFramePeekingCipher();
+ IvEncoder frameIvEncoder = crypto.getFrameIvEncoder();
+ IvEncoder framePeekingIvEncoder = crypto.getFramePeekingIvEncoder();
FrameReader encryption = new IncomingEncryptionLayerImpl(in, tagCipher,
- frameCipher, tagKey, frameKey, !initiator);
- // Authentication
- Mac mac = crypto.getMac();
- FrameReader authentication = new IncomingAuthenticationLayerImpl(
- encryption, mac, macKey);
- // Create the reader
- return new ConnectionReaderImpl(authentication);
+ frameCipher, framePeekingCipher, frameIvEncoder,
+ framePeekingIvEncoder, tagKey, frameKey, !initiator);
+ return new ConnectionReaderImpl(encryption);
}
}
diff --git a/components/net/sf/briar/transport/ConnectionReaderImpl.java b/components/net/sf/briar/transport/ConnectionReaderImpl.java
index cb84c45cd..4ef2de96c 100644
--- a/components/net/sf/briar/transport/ConnectionReaderImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderImpl.java
@@ -53,14 +53,20 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
private boolean readFrame() throws IOException {
assert length == 0;
- if(HeaderEncoder.isLastFrame(frame.getBuffer())) {
+ byte[] buf = frame.getBuffer();
+ if(HeaderEncoder.isLastFrame(buf)) {
length = -1;
return false;
}
frame.reset();
if(!in.readFrame(frame)) throw new FormatException();
offset = FRAME_HEADER_LENGTH;
- length = HeaderEncoder.getPayloadLength(frame.getBuffer());
+ length = HeaderEncoder.getPayloadLength(buf);
+ // The padding must be all zeroes
+ int padding = HeaderEncoder.getPaddingLength(buf);
+ for(int i = offset + length; i < offset + length + padding; i++) {
+ if(buf[i] != 0) throw new FormatException();
+ }
return true;
}
}
diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
index cffa55218..ea36c2c2c 100644
--- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
@@ -3,10 +3,10 @@ package net.sf.briar.transport;
import java.io.OutputStream;
import javax.crypto.Cipher;
-import javax.crypto.Mac;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.util.ByteUtils;
@@ -27,18 +27,14 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
// Derive the keys and erase the secret
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
- ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
ByteUtils.erase(secret);
- // Encryption
+ // Create the writer
Cipher tagCipher = crypto.getTagCipher();
Cipher frameCipher = crypto.getFrameCipher();
+ IvEncoder frameIvEncoder = crypto.getFrameIvEncoder();
FrameWriter encryption = new OutgoingEncryptionLayerImpl(
- out, capacity, tagCipher, frameCipher, tagKey, frameKey);
- // Authentication
- Mac mac = crypto.getMac();
- FrameWriter authentication =
- new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
- // Create the writer
- return new ConnectionWriterImpl(authentication);
+ out, capacity, tagCipher, frameCipher, frameIvEncoder, tagKey,
+ frameKey);
+ return new ConnectionWriterImpl(encryption);
}
}
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index e0a1a16bc..be97ff2de 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -91,7 +91,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
assert payload >= 0;
HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0,
lastFrame);
- frame.setLength(offset + MAC_LENGTH);
+ frame.setLength(offset);
out.writeFrame(frame);
frame.reset();
offset = FRAME_HEADER_LENGTH;
diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java
index 93ef395c3..23271149c 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -1,7 +1,6 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
-import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
class Frame {
@@ -24,7 +23,7 @@ class Frame {
}
public void setLength(int length) {
- if(length < FRAME_HEADER_LENGTH + MAC_LENGTH || length > buf.length)
+ if(length < FRAME_HEADER_LENGTH || length > buf.length)
throw new IllegalArgumentException();
this.length = length;
}
diff --git a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
deleted file mode 100644
index d682c9e62..000000000
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
-import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
-import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-
-import javax.crypto.Mac;
-
-import net.sf.briar.api.FormatException;
-import net.sf.briar.api.crypto.ErasableKey;
-
-class IncomingAuthenticationLayerImpl implements FrameReader {
-
- private final FrameReader in;
- private final Mac mac;
-
- IncomingAuthenticationLayerImpl(FrameReader in, Mac mac,
- ErasableKey macKey) {
- this.in = in;
- this.mac = mac;
- try {
- mac.init(macKey);
- } catch(InvalidKeyException e) {
- throw new IllegalArgumentException(e);
- }
- macKey.erase();
- if(mac.getMacLength() != MAC_LENGTH)
- throw new IllegalArgumentException();
- }
-
- public boolean readFrame(Frame f) throws IOException {
- // Read a frame
- if(!in.readFrame(f)) return false;
- // Check that the length is legal
- int length = f.getLength();
- if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
- throw new FormatException();
- if(length > MAX_FRAME_LENGTH) throw new FormatException();
- // Check that the header fields are legal and match the length
- byte[] buf = f.getBuffer();
- if(!HeaderEncoder.checkHeader(buf, length)) throw new FormatException();
- // Check that the padding is all zeroes
- int payload = HeaderEncoder.getPayloadLength(buf);
- int padding = HeaderEncoder.getPaddingLength(buf);
- int paddingStart = FRAME_HEADER_LENGTH + payload;
- for(int i = paddingStart; i < paddingStart + padding; i++) {
- if(buf[i] != 0) throw new FormatException();
- }
- // Verify the MAC
- int macStart = FRAME_HEADER_LENGTH + payload + padding;
- mac.update(buf, 0, macStart);
- byte[] expectedMac = mac.doFinal();
- for(int i = 0; i < expectedMac.length; i++) {
- if(expectedMac[i] != buf[macStart + i]) throw new FormatException();
- }
- return true;
- }
-}
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
index ef8e1f61e..66c49ee80 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
@@ -15,31 +15,38 @@ import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
class IncomingEncryptionLayerImpl implements FrameReader {
private final InputStream in;
- private final Cipher tagCipher, frameCipher;
+ private final Cipher tagCipher, frameCipher, framePeekingCipher;
+ private final IvEncoder frameIvEncoder, framePeekingIvEncoder;
private final ErasableKey tagKey, frameKey;
private final int blockSize;
- private final byte[] iv, ciphertext;
+ private final byte[] frameIv, framePeekingIv, ciphertext;
private boolean readTag;
private long frameNumber;
IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
- Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
- boolean readTag) {
+ Cipher frameCipher, Cipher framePeekingCipher,
+ IvEncoder frameIvEncoder, IvEncoder framePeekingIvEncoder,
+ ErasableKey tagKey, ErasableKey frameKey, boolean readTag) {
this.in = in;
this.tagCipher = tagCipher;
this.frameCipher = frameCipher;
+ this.framePeekingCipher = framePeekingCipher;
+ this.frameIvEncoder = frameIvEncoder;
+ this.framePeekingIvEncoder = framePeekingIvEncoder;
this.tagKey = tagKey;
this.frameKey = frameKey;
this.readTag = readTag;
blockSize = frameCipher.getBlockSize();
if(blockSize < FRAME_HEADER_LENGTH)
throw new IllegalArgumentException();
- iv = IvEncoder.encodeIv(0L, blockSize);
+ frameIv = frameIvEncoder.encodeIv(0L);
+ framePeekingIv = framePeekingIvEncoder.encodeIv(0L);
ciphertext = new byte[MAX_FRAME_LENGTH];
frameNumber = 0L;
}
@@ -65,21 +72,18 @@ class IncomingEncryptionLayerImpl implements FrameReader {
int offset = 0;
while(offset < blockSize) {
int read = in.read(ciphertext, offset, blockSize - offset);
- if(read == -1) {
- if(offset == 0 && !readTag) return false;
- throw new EOFException();
- }
+ if(read == -1) throw new EOFException();
offset += read;
}
readTag = false;
- // Decrypt the first block of the frame
+ // Decrypt the first block of the frame to peek at the header
+ framePeekingIvEncoder.updateIv(framePeekingIv, frameNumber);
+ IvParameterSpec ivSpec = new IvParameterSpec(framePeekingIv);
byte[] plaintext = f.getBuffer();
try {
- IvEncoder.updateIv(iv, frameNumber);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
- frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
- int decrypted = frameCipher.update(ciphertext, 0, blockSize,
- plaintext);
+ framePeekingCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
+ int decrypted = framePeekingCipher.update(ciphertext, 0,
+ blockSize, plaintext);
if(decrypted != blockSize) throw new RuntimeException();
} catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);
@@ -95,16 +99,19 @@ class IncomingEncryptionLayerImpl implements FrameReader {
if(read == -1) throw new EOFException();
offset += read;
}
- // Decrypt the remainder of the frame
+ // Decrypt and authenticate the entire frame
+ frameIvEncoder.updateIv(frameIv, frameNumber);
+ ivSpec = new IvParameterSpec(frameIv);
try {
- int decrypted = frameCipher.doFinal(ciphertext, blockSize,
- length - blockSize, plaintext, blockSize);
- if(decrypted != length - blockSize)
+ frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
+ int decrypted = frameCipher.doFinal(ciphertext, 0, length,
+ plaintext);
+ if(decrypted != length - MAC_LENGTH)
throw new RuntimeException();
} catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);
}
- f.setLength(length);
+ f.setLength(length - MAC_LENGTH);
frameNumber++;
return true;
} catch(IOException e) {
diff --git a/components/net/sf/briar/transport/IvEncoder.java b/components/net/sf/briar/transport/IvEncoder.java
deleted file mode 100644
index c922cadc8..000000000
--- a/components/net/sf/briar/transport/IvEncoder.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.sf.briar.transport;
-
-import net.sf.briar.util.ByteUtils;
-
-class IvEncoder {
-
- static byte[] encodeIv(long frame, int blockSize) {
- if(frame < 0 || 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) {
- // Encode the frame number as a uint32
- ByteUtils.writeUint32(frame, iv, 0);
- }
-}
diff --git a/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java b/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
deleted file mode 100644
index 2e3798dc3..000000000
--- a/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-
-import javax.crypto.Mac;
-import javax.crypto.ShortBufferException;
-
-import net.sf.briar.api.crypto.ErasableKey;
-
-class OutgoingAuthenticationLayerImpl implements FrameWriter {
-
- private final FrameWriter out;
- private final Mac mac;
-
- OutgoingAuthenticationLayerImpl(FrameWriter out, Mac mac,
- ErasableKey macKey) {
- this.out = out;
- this.mac = mac;
- try {
- mac.init(macKey);
- } catch(InvalidKeyException badKey) {
- throw new IllegalArgumentException(badKey);
- }
- macKey.erase();
- if(mac.getMacLength() != MAC_LENGTH)
- throw new IllegalArgumentException();
- }
-
- public void writeFrame(Frame f) throws IOException {
- byte[] buf = f.getBuffer();
- int length = f.getLength() - MAC_LENGTH;
- mac.update(buf, 0, length);
- try {
- mac.doFinal(buf, length);
- } catch(ShortBufferException badMac) {
- throw new RuntimeException(badMac);
- }
- out.writeFrame(f);
- }
-
- public void flush() throws IOException {
- out.flush();
- }
-
- public long getRemainingCapacity() {
- return out.getRemainingCapacity();
- }
-}
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
index f79b6831d..d79d498b1 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
@@ -1,5 +1,6 @@
package net.sf.briar.transport;
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
@@ -11,56 +12,58 @@ import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
class OutgoingEncryptionLayerImpl implements FrameWriter {
private final OutputStream out;
private final Cipher tagCipher, frameCipher;
+ private final IvEncoder frameIvEncoder;
private final ErasableKey tagKey, frameKey;
- private final byte[] iv, ciphertext;
+ private final byte[] frameIv, ciphertext;
private long capacity, frameNumber;
OutgoingEncryptionLayerImpl(OutputStream out, long capacity,
- Cipher tagCipher, Cipher frameCipher, ErasableKey tagKey,
- ErasableKey frameKey) {
+ Cipher tagCipher, Cipher frameCipher, IvEncoder frameIvEncoder,
+ ErasableKey tagKey, ErasableKey frameKey) {
this.out = out;
this.capacity = capacity;
this.tagCipher = tagCipher;
this.frameCipher = frameCipher;
+ this.frameIvEncoder = frameIvEncoder;
this.tagKey = tagKey;
this.frameKey = frameKey;
- iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
+ frameIv = frameIvEncoder.encodeIv(0L);
ciphertext = new byte[TAG_LENGTH + MAX_FRAME_LENGTH];
frameNumber = 0L;
}
public void writeFrame(Frame f) throws IOException {
byte[] plaintext = f.getBuffer();
- int length = f.getLength();
- int offset = 0;
+ int offset = 0, length = f.getLength();
if(frameNumber == 0) {
TagEncoder.encodeTag(ciphertext, tagCipher, tagKey);
offset = TAG_LENGTH;
}
- IvEncoder.updateIv(iv, frameNumber);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ frameIvEncoder.updateIv(frameIv, frameNumber);
+ IvParameterSpec ivSpec = new IvParameterSpec(frameIv);
try {
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
int encrypted = frameCipher.doFinal(plaintext, 0, length,
ciphertext, offset);
- if(encrypted != length) throw new RuntimeException();
+ if(encrypted != length + MAC_LENGTH) throw new RuntimeException();
} catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);
}
try {
- out.write(ciphertext, 0, offset + length);
+ out.write(ciphertext, 0, offset + length + MAC_LENGTH);
} catch(IOException e) {
frameKey.erase();
tagKey.erase();
throw e;
}
- capacity -= offset + length;
+ capacity -= offset + length + MAC_LENGTH;
frameNumber++;
}
diff --git a/test/build.xml b/test/build.xml
index 725693a36..9fe9705e6 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -18,6 +18,7 @@
+
diff --git a/test/net/sf/briar/crypto/FramePeekingTest.java b/test/net/sf/briar/crypto/FramePeekingTest.java
new file mode 100644
index 000000000..f99a9ba19
--- /dev/null
+++ b/test/net/sf/briar/crypto/FramePeekingTest.java
@@ -0,0 +1,44 @@
+package net.sf.briar.crypto;
+
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+
+import net.sf.briar.BriarTestCase;
+import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
+import net.sf.briar.util.ByteUtils;
+
+import org.junit.Test;
+
+public class FramePeekingTest extends BriarTestCase {
+
+ @Test
+ public void testFramePeeking() throws Exception {
+ CryptoComponent crypto = new CryptoComponentImpl();
+ ErasableKey key = crypto.generateTestKey();
+
+ Cipher frameCipher = crypto.getFrameCipher();
+ IvEncoder frameIvEncoder = crypto.getFrameIvEncoder();
+ byte[] iv = frameIvEncoder.encodeIv(ByteUtils.MAX_32_BIT_UNSIGNED);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ frameCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+
+ Cipher framePeekingCipher = crypto.getFramePeekingCipher();
+ IvEncoder framePeekingIvEncoder = crypto.getFramePeekingIvEncoder();
+ iv = framePeekingIvEncoder.encodeIv(ByteUtils.MAX_32_BIT_UNSIGNED);
+ ivSpec = new IvParameterSpec(iv);
+ framePeekingCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+
+ // The ciphers should produce the same ciphertext, apart from the MAC
+ byte[] plaintext = new byte[123];
+ byte[] ciphertext = frameCipher.doFinal(plaintext);
+ byte[] peekingCiphertext = framePeekingCipher.doFinal(plaintext);
+ assertEquals(ciphertext.length, peekingCiphertext.length + MAC_LENGTH);
+ for(int i = 0; i < peekingCiphertext.length; i++) {
+ assertEquals(ciphertext[i], peekingCiphertext[i]);
+ }
+ }
+}
diff --git a/test/net/sf/briar/crypto/KeyDerivationTest.java b/test/net/sf/briar/crypto/KeyDerivationTest.java
index b777cd792..cc4335c9a 100644
--- a/test/net/sf/briar/crypto/KeyDerivationTest.java
+++ b/test/net/sf/briar/crypto/KeyDerivationTest.java
@@ -25,17 +25,15 @@ public class KeyDerivationTest extends BriarTestCase {
}
@Test
- public void testSixKeysAreDistinct() {
+ public void testKeysAreDistinct() {
List keys = new ArrayList();
keys.add(crypto.deriveFrameKey(secret, true));
keys.add(crypto.deriveFrameKey(secret, false));
keys.add(crypto.deriveTagKey(secret, true));
keys.add(crypto.deriveTagKey(secret, false));
- keys.add(crypto.deriveMacKey(secret, true));
- keys.add(crypto.deriveMacKey(secret, false));
- for(int i = 0; i < 6; i++) {
+ for(int i = 0; i < 4; i++) {
byte[] keyI = keys.get(i).getEncoded();
- for(int j = 0; j < 6; j++) {
+ for(int j = 0; j < 4; j++) {
byte[] keyJ = keys.get(j).getEncoded();
assertEquals(i == j, Arrays.equals(keyI, keyJ));
}
diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
index da0c140a2..78b024c91 100644
--- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
@@ -23,14 +23,8 @@ public class ConnectionReaderImplTest extends TransportTest {
@Test
public void testLengthZero() throws Exception {
- int payloadLength = 0;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, true);
- // Calculate the MAC
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
+ byte[] frame = new byte[FRAME_HEADER_LENGTH + MAC_LENGTH];
+ HeaderEncoder.encodeHeader(frame, 0, 0, 0, true);
// Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame);
ConnectionReader r = createConnectionReader(in);
@@ -40,14 +34,8 @@ public class ConnectionReaderImplTest extends TransportTest {
@Test
public void testLengthOne() throws Exception {
- int payloadLength = 1;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, true);
- // Calculate the MAC
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
+ byte[] frame = new byte[FRAME_HEADER_LENGTH + 1 + MAC_LENGTH];
+ HeaderEncoder.encodeHeader(frame, 0, 1, 0, true);
// Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame);
ConnectionReader r = createConnectionReader(in);
@@ -61,14 +49,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// First frame: max payload length
byte[] frame = new byte[MAX_FRAME_LENGTH];
HeaderEncoder.encodeHeader(frame, 0, MAX_PAYLOAD_LENGTH, 0, false);
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
// Second frame: max payload length plus one
byte[] frame1 = new byte[MAX_FRAME_LENGTH + 1];
HeaderEncoder.encodeHeader(frame1, 1, MAX_PAYLOAD_LENGTH + 1, 0, false);
- mac.update(frame1, 0, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH + 1);
- mac.doFinal(frame1, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH + 1);
// Concatenate the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(frame);
@@ -93,15 +76,10 @@ public class ConnectionReaderImplTest extends TransportTest {
byte[] frame = new byte[MAX_FRAME_LENGTH];
HeaderEncoder.encodeHeader(frame, 0, MAX_PAYLOAD_LENGTH - paddingLength,
paddingLength, false);
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
// Second frame: max payload length plus one, including padding
byte[] frame1 = new byte[MAX_FRAME_LENGTH + 1];
HeaderEncoder.encodeHeader(frame1, 1,
MAX_PAYLOAD_LENGTH + 1 - paddingLength, paddingLength, false);
- mac.update(frame1, 0, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH + 1);
- mac.doFinal(frame1, FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH + 1);
// Concatenate the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(frame);
@@ -128,10 +106,6 @@ public class ConnectionReaderImplTest extends TransportTest {
false);
// Set a byte of the padding to a non-zero value
frame[FRAME_HEADER_LENGTH + payloadLength] = 1;
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength
- + paddingLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength + paddingLength);
// Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame);
ConnectionReader r = createConnectionReader(in);
@@ -149,16 +123,11 @@ public class ConnectionReaderImplTest extends TransportTest {
byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
+ MAC_LENGTH];
HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
// Second frame: 1234-byte payload
int payloadLength1 = 1234;
byte[] frame1 = new byte[FRAME_HEADER_LENGTH + payloadLength1
+ MAC_LENGTH];
HeaderEncoder.encodeHeader(frame1, 1, payloadLength1, 0, true);
- mac.update(frame1, 0, FRAME_HEADER_LENGTH + payloadLength1);
- mac.doFinal(frame1, FRAME_HEADER_LENGTH + payloadLength1);
// Concatenate the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(frame);
@@ -182,16 +151,11 @@ public class ConnectionReaderImplTest extends TransportTest {
byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
+ MAC_LENGTH];
HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
// Second frame: 1234-byte payload
int payloadLength1 = 1234;
byte[] frame1 = new byte[FRAME_HEADER_LENGTH + payloadLength1
+ MAC_LENGTH];
HeaderEncoder.encodeHeader(frame1, 1, payloadLength1, 0, false);
- mac.update(frame1, 0, FRAME_HEADER_LENGTH + payloadLength1);
- mac.doFinal(frame1, FRAME_HEADER_LENGTH + payloadLength1);
// Concatenate the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(frame);
@@ -211,52 +175,8 @@ public class ConnectionReaderImplTest extends TransportTest {
} catch(FormatException expected) {}
}
- @Test
- public void testCorruptPayload() throws Exception {
- int payloadLength = 8;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- // Calculate the MAC
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
- // Modify the payload
- frame[12] ^= 1;
- // Try to read the frame - not a single byte should be read
- ByteArrayInputStream in = new ByteArrayInputStream(frame);
- ConnectionReader r = createConnectionReader(in);
- try {
- r.getInputStream().read();
- fail();
- } catch(FormatException expected) {}
- }
-
- @Test
- public void testCorruptMac() throws Exception {
- int payloadLength = 8;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- // Calculate the MAC
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
- // Modify the MAC
- frame[17] ^= 1;
- // Try to read the frame - not a single byte should be read
- ByteArrayInputStream in = new ByteArrayInputStream(frame);
- ConnectionReader r = createConnectionReader(in);
- try {
- r.getInputStream().read();
- fail();
- } catch(FormatException expected) {}
- }
-
private ConnectionReader createConnectionReader(InputStream in) {
FrameReader encryption = new NullIncomingEncryptionLayer(in);
- FrameReader authentication = new IncomingAuthenticationLayerImpl(
- encryption, mac, macKey);
- return new ConnectionReaderImpl(authentication);
+ return new ConnectionReaderImpl(encryption);
}
}
diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
index 5f825e171..9f37b1b03 100644
--- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
@@ -30,14 +30,9 @@ public class ConnectionWriterImplTest extends TransportTest {
@Test
public void testSingleByteFrame() throws Exception {
- int payloadLength = 1;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- // Calculate the MAC
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
+ // Create a single-byte frame
+ byte[] frame = new byte[FRAME_HEADER_LENGTH + 1 + MAC_LENGTH];
+ HeaderEncoder.encodeHeader(frame, 0, 1, 0, false);
// Check that the ConnectionWriter gets the same results
ByteArrayOutputStream out = new ByteArrayOutputStream();
ConnectionWriter w = createConnectionWriter(out);
@@ -75,20 +70,11 @@ public class ConnectionWriterImplTest extends TransportTest {
@Test
public void testMultipleFrames() throws Exception {
// First frame: 123-byte payload
- int payloadLength = 123;
- byte[] frame = new byte[FRAME_HEADER_LENGTH + payloadLength
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame, 0, payloadLength, 0, false);
- mac.init(macKey);
- mac.update(frame, 0, FRAME_HEADER_LENGTH + payloadLength);
- mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
+ byte[] frame = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
+ HeaderEncoder.encodeHeader(frame, 0, 123, 0, false);
// Second frame: 1234-byte payload
- int payloadLength1 = 1234;
- byte[] frame1 = new byte[FRAME_HEADER_LENGTH + payloadLength1
- + MAC_LENGTH];
- HeaderEncoder.encodeHeader(frame1, 1, payloadLength1, 0, false);
- mac.update(frame1, 0, FRAME_HEADER_LENGTH + 1234);
- mac.doFinal(frame1, FRAME_HEADER_LENGTH + 1234);
+ byte[] frame1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
+ HeaderEncoder.encodeHeader(frame1, 1, 1234, 0, false);
// Concatenate the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(frame);
@@ -107,8 +93,6 @@ public class ConnectionWriterImplTest extends TransportTest {
private ConnectionWriter createConnectionWriter(OutputStream out) {
FrameWriter encryption = new NullOutgoingEncryptionLayer(out);
- FrameWriter authentication =
- new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
- return new ConnectionWriterImpl(authentication);
+ return new ConnectionWriterImpl(encryption);
}
}
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index 97af64c4b..11de90329 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -10,11 +10,11 @@ import java.io.OutputStream;
import java.util.Random;
import javax.crypto.Cipher;
-import javax.crypto.Mac;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.crypto.CryptoModule;
@@ -27,11 +27,11 @@ import com.google.inject.Injector;
public class FrameReadWriteTest extends BriarTestCase {
private final CryptoComponent crypto;
- private final Cipher tagCipher, frameCipher;
- private final Mac mac;
+ private final Cipher tagCipher, frameCipher, framePeekingCipher;
+ private final IvEncoder frameIvEncoder, framePeekingIvEncoder;
private final Random random;
private final byte[] outSecret;
- private final ErasableKey tagKey, frameKey, macKey;
+ private final ErasableKey tagKey, frameKey;
public FrameReadWriteTest() {
super();
@@ -39,14 +39,15 @@ public class FrameReadWriteTest extends BriarTestCase {
crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
frameCipher = crypto.getFrameCipher();
- mac = crypto.getMac();
+ framePeekingCipher = crypto.getFramePeekingCipher();
+ frameIvEncoder = crypto.getFrameIvEncoder();
+ framePeekingIvEncoder = crypto.getFramePeekingIvEncoder();
random = new Random();
// Since we're sending frames to ourselves, we only need outgoing keys
outSecret = new byte[32];
random.nextBytes(outSecret);
tagKey = crypto.deriveTagKey(outSecret, true);
frameKey = crypto.deriveFrameKey(outSecret, true);
- macKey = crypto.deriveMacKey(outSecret, true);
}
@Test
@@ -71,14 +72,12 @@ public class FrameReadWriteTest extends BriarTestCase {
// Copy the keys - the copies will be erased
ErasableKey tagCopy = tagKey.copy();
ErasableKey frameCopy = frameKey.copy();
- ErasableKey macCopy = macKey.copy();
// Write the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
FrameWriter encryptionOut = new OutgoingEncryptionLayerImpl(out,
- Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy);
- FrameWriter authenticationOut = new OutgoingAuthenticationLayerImpl(
- encryptionOut, mac, macCopy);
- ConnectionWriter writer = new ConnectionWriterImpl(authenticationOut);
+ Long.MAX_VALUE, tagCipher, frameCipher, frameIvEncoder, tagCopy,
+ frameCopy);
+ ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut);
OutputStream out1 = writer.getOutputStream();
out1.write(frame);
out1.flush();
@@ -92,10 +91,9 @@ public class FrameReadWriteTest extends BriarTestCase {
assertTrue(TagEncoder.decodeTag(recoveredTag, tagCipher, tagKey));
// Read the frames back
FrameReader encryptionIn = new IncomingEncryptionLayerImpl(in,
- tagCipher, frameCipher, tagKey, frameKey, false);
- FrameReader authenticationIn = new IncomingAuthenticationLayerImpl(
- encryptionIn, mac, macKey);
- ConnectionReader reader = new ConnectionReaderImpl(authenticationIn);
+ tagCipher, frameCipher, framePeekingCipher, frameIvEncoder,
+ framePeekingIvEncoder, tagKey, frameKey, false);
+ ConnectionReader reader = new ConnectionReaderImpl(encryptionIn);
InputStream in1 = reader.getInputStream();
byte[] recovered = new byte[frame.length];
int offset = 0;
diff --git a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
index 1b3f09759..06fa5d5e0 100644
--- a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
@@ -1,7 +1,6 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
-import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.ByteArrayInputStream;
@@ -12,6 +11,7 @@ import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.crypto.CryptoModule;
import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -22,7 +22,8 @@ import com.google.inject.Injector;
public class IncomingEncryptionLayerImplTest extends BriarTestCase {
- private final Cipher tagCipher, frameCipher;
+ private final Cipher tagCipher, frameCipher, framePeekingCipher;
+ private final IvEncoder frameIvEncoder, framePeekingIvEncoder;
private final ErasableKey tagKey, frameKey;
public IncomingEncryptionLayerImplTest() {
@@ -31,6 +32,9 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
frameCipher = crypto.getFrameCipher();
+ framePeekingCipher = crypto.getFramePeekingCipher();
+ frameIvEncoder = crypto.getFrameIvEncoder();
+ framePeekingIvEncoder = crypto.getFramePeekingIvEncoder();
tagKey = crypto.generateTestKey();
frameKey = crypto.generateTestKey();
}
@@ -41,16 +45,16 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, tagCipher, tagKey);
// Calculate the ciphertext for the first frame
- byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
+ byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0, false);
- byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
+ byte[] iv = frameIvEncoder.encodeIv(0L);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
- byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+ byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the ciphertext for the second frame
- byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
+ byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0, false);
- IvEncoder.updateIv(iv, 1L);
+ frameIvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
@@ -63,7 +67,8 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Use the encryption layer to decrypt the ciphertext
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
- frameCipher, tagKey, frameKey, true);
+ frameCipher, framePeekingCipher, frameIvEncoder,
+ framePeekingIvEncoder, tagKey, frameKey, true);
// First frame
Frame f = new Frame();
assertTrue(decrypter.readFrame(f));
@@ -86,16 +91,16 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
@Test
public void testDecryptionWithoutTag() throws Exception {
// Calculate the ciphertext for the first frame
- byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
+ byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0, false);
- byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
+ byte[] iv = frameIvEncoder.encodeIv(0L);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
- byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+ byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the ciphertext for the second frame
- byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
+ byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0, false);
- IvEncoder.updateIv(iv, 1L);
+ frameIvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
@@ -107,7 +112,8 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Use the encryption layer to decrypt the ciphertext
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
- frameCipher, tagKey, frameKey, false);
+ frameCipher, framePeekingCipher, frameIvEncoder,
+ framePeekingIvEncoder, tagKey, frameKey, false);
// First frame
Frame f = new Frame();
assertTrue(decrypter.readFrame(f));
diff --git a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
index dcf038670..c5d4410f5 100644
--- a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
@@ -42,7 +42,7 @@ class NullIncomingEncryptionLayer implements FrameReader {
if(read == -1) throw new EOFException();
offset += read;
}
- f.setLength(length);
+ f.setLength(length - MAC_LENGTH);
return true;
}
}
diff --git a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
index 598562877..ae7ccdd17 100644
--- a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
@@ -1,5 +1,7 @@
package net.sf.briar.transport;
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
+
import java.io.IOException;
import java.io.OutputStream;
@@ -21,7 +23,7 @@ class NullOutgoingEncryptionLayer implements FrameWriter {
}
public void writeFrame(Frame f) throws IOException {
- out.write(f.getBuffer(), 0, f.getLength());
+ out.write(f.getBuffer(), 0, f.getLength() + MAC_LENGTH);
capacity -= f.getLength();
}
diff --git a/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
index aca3f7192..3393f9b6b 100644
--- a/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
@@ -11,6 +11,7 @@ import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.IvEncoder;
import net.sf.briar.crypto.CryptoModule;
import org.junit.Test;
@@ -20,9 +21,8 @@ import com.google.inject.Injector;
public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
- private static final int MAC_LENGTH = 32;
-
private final Cipher tagCipher, frameCipher;
+ private final IvEncoder frameIvEncoder;
private final ErasableKey tagKey, frameKey;
public OutgoingEncryptionLayerImplTest() {
@@ -31,6 +31,7 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
frameCipher = crypto.getFrameCipher();
+ frameIvEncoder = crypto.getFrameIvEncoder();
tagKey = crypto.generateTestKey();
frameKey = crypto.generateTestKey();
}
@@ -41,14 +42,14 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame
- byte[] iv = new byte[frameCipher.getBlockSize()];
- byte[] plaintext = new byte[123 + MAC_LENGTH];
+ byte[] iv = frameIvEncoder.encodeIv(0L);
+ byte[] plaintext = new byte[123];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the expected ciphertext for the second frame
- byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
- IvEncoder.updateIv(iv, 1L);
+ byte[] plaintext1 = new byte[1234];
+ frameIvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1);
@@ -61,7 +62,8 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
// Use the encryption layer to encrypt the plaintext
out.reset();
FrameWriter encrypter = new OutgoingEncryptionLayerImpl(out,
- Long.MAX_VALUE, tagCipher, frameCipher, tagKey, frameKey);
+ Long.MAX_VALUE, tagCipher, frameCipher, frameIvEncoder, tagKey,
+ frameKey);
Frame f = new Frame();
System.arraycopy(plaintext, 0, f.getBuffer(), 0, plaintext.length);
f.setLength(plaintext.length);
diff --git a/test/net/sf/briar/transport/TransportTest.java b/test/net/sf/briar/transport/TransportTest.java
index c44e6fc2c..9764ec71d 100644
--- a/test/net/sf/briar/transport/TransportTest.java
+++ b/test/net/sf/briar/transport/TransportTest.java
@@ -4,7 +4,7 @@ import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
-import javax.crypto.Mac;
+import javax.crypto.Cipher;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
@@ -19,14 +19,14 @@ public abstract class TransportTest extends BriarTestCase {
static final int MAX_PAYLOAD_LENGTH =
MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - MAC_LENGTH;
- protected final Mac mac;
- protected final ErasableKey macKey;
+ protected final Cipher frameCipher;
+ protected final ErasableKey frameKey;
public TransportTest() throws Exception {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
- mac = crypto.getMac();
- macKey = crypto.generateTestKey();
+ frameCipher = crypto.getFrameCipher();
+ frameKey = crypto.generateTestKey();
}
}