diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index d3f8d0353219d8a7a065910208082ab64f317fe9..59e5ae547b5886fa0e96d784f59e89fb93dbb800 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(); + + IvEncoder getFrameIvEncoder(); - Mac getMac(); + 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 0000000000000000000000000000000000000000..12d24a545e87a2920a647cf86fd1a1c4404d457c --- /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 afd01abd8e231e559fb267ac4fd461aa303bd4f1..a37b810071d8952f03b4665a09b5c6cdf5907214 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 4fd74b05f29644792a652935282647f441cca913..226c992215902f88b6f2410ef7c724e3e549eddc 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 0000000000000000000000000000000000000000..6703669f69f66da50af0c34f97627120a9b4d03f --- /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 0000000000000000000000000000000000000000..85d5dbb06d4c0f0889dd3cc6c1814f8ec1bf3d8a --- /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 8163e047ffa6b31e371ba30ab63e7a6bb2d2e14a..0bed032dd28587f8ede3cae72a9677b3dcbb6eb7 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 cb84c45cd18c4e168a758ddb6191461c0faa6c3f..4ef2de96c10203aacbacac6844fba29be9f6fcd6 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 cffa5521861e6455282129057a26ac5688ec5671..ea36c2c2c51a9c30ec944e788333f726ac55538b 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 e0a1a16bc0e281de6fdd940114683f6cdb4eae0e..be97ff2de48b87766c57e40ebdb28f3a7659f6b1 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 93ef395c36e7ce6151bb76c2e8f229e51f9d9ff7..23271149c22f864bdaeea0587f08c4355ffbe265 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 d682c9e622eaa352038b6dac9d1857816ceca2da..0000000000000000000000000000000000000000 --- 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 ef8e1f61e0956ffa9b871c92e9966ab15bfb1e0d..66c49ee806bbd585ad966de4a25f298d6d4fc8eb 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 c922cadc81fe94b39e9a9c3d6d2c213bef3b33ad..0000000000000000000000000000000000000000 --- 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 2e3798dc36b54560f73872578b9eef8852e07536..0000000000000000000000000000000000000000 --- 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 f79b6831d6c4f9642287f2c803973e2212a4eb98..d79d498b1f76dd77b14f681ef4b42cd32c7c65e2 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 725693a361aa9d68e1fddd767fec1759836a3182..9fe9705e6536e7ac5d4c7d260ee937a358d4e235 100644 --- a/test/build.xml +++ b/test/build.xml @@ -18,6 +18,7 @@ <test name='net.sf.briar.ProtocolIntegrationTest'/> <test name='net.sf.briar.crypto.CounterModeTest'/> <test name='net.sf.briar.crypto.ErasableKeyTest'/> + <test name='net.sf.briar.crypto.FramePeekingTest'/> <test name='net.sf.briar.crypto.KeyDerivationTest'/> <test name='net.sf.briar.db.BasicH2Test'/> <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> diff --git a/test/net/sf/briar/crypto/FramePeekingTest.java b/test/net/sf/briar/crypto/FramePeekingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f99a9ba1920bdce216140fd5ede3948685ec5bdb --- /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 b777cd7921491bafd8a483db0a65d9b8bec5b803..cc4335c9ae36e2843451b7d1fd8f28516da4abb0 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<ErasableKey> keys = new ArrayList<ErasableKey>(); 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 da0c140a29071d43926822a3a4757cf4e5deedb3..78b024c9110771e99b833028cfa505de2df2e196 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 5f825e1711839412a1be295132c30af0a45348b6..9f37b1b032ca26cb661d8d737b7c395b27ea0dcd 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 97af64c4b3bf05b0dfb9a791ef975948f6a0e220..11de90329ec8a8b1898df65ed04ab0e69da2e945 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 1b3f09759393e1607980200a81846b9693b6abb7..06fa5d5e00926c9da794ca3fb849b96641e61be2 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 dcf0386702c2eced15b8afb961f28bc259890f9e..c5d4410f5397d0fd862bfc30419178e6061b4fb8 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 598562877bcdf93c99c4dcc0df74805d663a0523..ae7ccdd174b43e439dfe282462f98c0da38d99ba 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 aca3f7192be20c26a4d5867e29542abaf7156abf..3393f9b6bcf76379b6b712f6fda527bafeedeb90 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 c44e6fc2c76000ad8f8199168b9ea16e00f20f99..9764ec71d861104568411d87e3767fb9436acff6 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(); } }