diff --git a/briar-api/src/org/briarproject/api/transport/TransportConstants.java b/briar-api/src/org/briarproject/api/transport/TransportConstants.java index 797a47667a5e61aea45817186a343d924139c86c..fcab0049da804a79375a876b63dd4b710f4714e6 100644 --- a/briar-api/src/org/briarproject/api/transport/TransportConstants.java +++ b/briar-api/src/org/briarproject/api/transport/TransportConstants.java @@ -1,25 +1,35 @@ package org.briarproject.api.transport; +import org.briarproject.api.crypto.SecretKey; + public interface TransportConstants { /** The length of the pseudo-random tag in bytes. */ int TAG_LENGTH = 16; - /** The maximum length of a frame in bytes, including the header and MAC. */ - int MAX_FRAME_LENGTH = 1024; + /** The length of the stream header IV in bytes. */ + int STREAM_HEADER_IV_LENGTH = 12; /** The length of the message authentication code (MAC) in bytes. */ int MAC_LENGTH = 16; + /** The length of the stream header in bytes. */ + int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH + + MAC_LENGTH; + + /** The length of the frame initalisation vector (IV) in bytes. */ + int FRAME_IV_LENGTH = 12; + /** The length of the frame header in bytes. */ - int HEADER_LENGTH = 4 + MAC_LENGTH; + int FRAME_HEADER_LENGTH = 4 + MAC_LENGTH; - /** The maximum total length of the frame payload and padding in bytes. */ - int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + /** The maximum length of a frame in bytes, including the header and MAC. */ + int MAX_FRAME_LENGTH = 1024; - /** The length of the initalisation vector (IV) in bytes. */ - int IV_LENGTH = 12; + /** The maximum total length of the frame payload and padding in bytes. */ + int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH + - MAC_LENGTH; /** * The minimum stream length in bytes that all transport plugins must diff --git a/briar-core/src/org/briarproject/crypto/FrameEncoder.java b/briar-core/src/org/briarproject/crypto/FrameEncoder.java index 641e9a4664337826b3f26631774d31ca480fd374..efd03ed49bad9c530eb7851571f78fae0f5509a0 100644 --- a/briar-core/src/org/briarproject/crypto/FrameEncoder.java +++ b/briar-core/src/org/briarproject/crypto/FrameEncoder.java @@ -1,27 +1,28 @@ package org.briarproject.crypto; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; +import org.briarproject.util.ByteUtils; + +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; -import org.briarproject.util.ByteUtils; - class FrameEncoder { static void encodeIv(byte[] iv, long frameNumber, boolean header) { - if (iv.length < IV_LENGTH) throw new IllegalArgumentException(); + if (iv.length < FRAME_IV_LENGTH) throw new IllegalArgumentException(); if (frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); ByteUtils.writeUint32(frameNumber, iv, 0); if (header) iv[4] = 1; else iv[4] = 0; - for (int i = 5; i < IV_LENGTH; i++) iv[i] = 0; + for (int i = 5; i < FRAME_IV_LENGTH; i++) iv[i] = 0; } static void encodeHeader(byte[] header, boolean finalFrame, int payloadLength, int paddingLength) { - if (header.length < HEADER_LENGTH) throw new IllegalArgumentException(); + if (header.length < FRAME_HEADER_LENGTH) + throw new IllegalArgumentException(); if (payloadLength < 0) throw new IllegalArgumentException(); if (paddingLength < 0) throw new IllegalArgumentException(); if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH) @@ -32,17 +33,20 @@ class FrameEncoder { } static boolean isFinalFrame(byte[] header) { - if (header.length < HEADER_LENGTH) throw new IllegalArgumentException(); + if (header.length < FRAME_HEADER_LENGTH) + throw new IllegalArgumentException(); return (header[0] & 0x80) == 0x80; } static int getPayloadLength(byte[] header) { - if (header.length < HEADER_LENGTH) throw new IllegalArgumentException(); + if (header.length < FRAME_HEADER_LENGTH) + throw new IllegalArgumentException(); return ByteUtils.readUint16(header, 0) & 0x7FFF; } static int getPaddingLength(byte[] header) { - if (header.length < HEADER_LENGTH) throw new IllegalArgumentException(); + if (header.length < FRAME_HEADER_LENGTH) + throw new IllegalArgumentException(); return ByteUtils.readUint16(header, 2); } } diff --git a/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java index 19d3c69c456084e511d0c59a8c6b5d8c66f0d341..89e18bee6119fea1b02809d6f582a9f2696d93e5 100644 --- a/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java +++ b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java @@ -9,31 +9,34 @@ import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_LENGTH; -// FIXME: Implementation is incomplete, doesn't read the stream header class StreamDecrypterImpl implements StreamDecrypter { private final InputStream in; - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - private final byte[] iv, frameHeader, frameCiphertext; + private final AuthenticatedCipher cipher; + private final SecretKey streamHeaderKey; + private final byte[] frameIv, frameHeader, frameCiphertext; + private SecretKey frameKey; private long frameNumber; private boolean finalFrame; - StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher, - SecretKey headerKey) { + StreamDecrypterImpl(InputStream in, AuthenticatedCipher cipher, + SecretKey streamHeaderKey) { this.in = in; - this.frameCipher = frameCipher; - this.frameKey = headerKey; // FIXME - iv = new byte[IV_LENGTH]; - frameHeader = new byte[HEADER_LENGTH]; + this.cipher = cipher; + this.streamHeaderKey = streamHeaderKey; + frameIv = new byte[FRAME_IV_LENGTH]; + frameHeader = new byte[FRAME_HEADER_LENGTH]; frameCiphertext = new byte[MAX_FRAME_LENGTH]; + frameKey = null; frameNumber = 0; finalFrame = false; } @@ -43,20 +46,23 @@ class StreamDecrypterImpl implements StreamDecrypter { if (payload.length < MAX_PAYLOAD_LENGTH) throw new IllegalArgumentException(); if (finalFrame) return -1; + // Read the stream header if required + if (frameKey == null) readStreamHeader(); // Read the frame header int offset = 0; - while (offset < HEADER_LENGTH) { - int read = in.read(frameCiphertext, offset, HEADER_LENGTH - offset); + while (offset < FRAME_HEADER_LENGTH) { + int read = in.read(frameCiphertext, offset, + FRAME_HEADER_LENGTH - offset); if (read == -1) throw new EOFException(); offset += read; } // Decrypt and authenticate the frame header - FrameEncoder.encodeIv(iv, frameNumber, true); + FrameEncoder.encodeIv(frameIv, frameNumber, true); try { - frameCipher.init(false, frameKey, iv); - int decrypted = frameCipher.process(frameCiphertext, 0, - HEADER_LENGTH, frameHeader, 0); - if (decrypted != HEADER_LENGTH - MAC_LENGTH) + cipher.init(false, frameKey, frameIv); + int decrypted = cipher.process(frameCiphertext, 0, + FRAME_HEADER_LENGTH, frameHeader, 0); + if (decrypted != FRAME_HEADER_LENGTH - MAC_LENGTH) throw new RuntimeException(); } catch (GeneralSecurityException e) { throw new FormatException(); @@ -68,7 +74,7 @@ class StreamDecrypterImpl implements StreamDecrypter { if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH) throw new FormatException(); // Read the payload and padding - int frameLength = HEADER_LENGTH + payloadLength + paddingLength + int frameLength = FRAME_HEADER_LENGTH + payloadLength + paddingLength + MAC_LENGTH; while (offset < frameLength) { int read = in.read(frameCiphertext, offset, frameLength - offset); @@ -76,10 +82,10 @@ class StreamDecrypterImpl implements StreamDecrypter { offset += read; } // Decrypt and authenticate the payload and padding - FrameEncoder.encodeIv(iv, frameNumber, false); + FrameEncoder.encodeIv(frameIv, frameNumber, false); try { - frameCipher.init(false, frameKey, iv); - int decrypted = frameCipher.process(frameCiphertext, HEADER_LENGTH, + cipher.init(false, frameKey, frameIv); + int decrypted = cipher.process(frameCiphertext, FRAME_HEADER_LENGTH, payloadLength + paddingLength + MAC_LENGTH, payload, 0); if (decrypted != payloadLength + paddingLength) throw new RuntimeException(); @@ -92,4 +98,31 @@ class StreamDecrypterImpl implements StreamDecrypter { frameNumber++; return payloadLength; } + + private void readStreamHeader() throws IOException { + byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; + byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH]; + byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH]; + // Read the stream header + int offset = 0; + while (offset < STREAM_HEADER_LENGTH) { + int read = in.read(streamHeaderCiphertext, offset, + STREAM_HEADER_LENGTH - offset); + if (read == -1) throw new EOFException(); + offset += read; + } + // Decrypt and authenticate the stream header + System.arraycopy(streamHeaderCiphertext, 0, streamHeaderIv, 0, + STREAM_HEADER_IV_LENGTH); + try { + cipher.init(false, streamHeaderKey, streamHeaderIv); + int decrypted = cipher.process(streamHeaderCiphertext, + STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH, + streamHeaderPlaintext, 0); + if (decrypted != SecretKey.LENGTH) throw new RuntimeException(); + } catch (GeneralSecurityException e) { + throw new FormatException(); + } + frameKey = new SecretKey(streamHeaderPlaintext); + } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java index e8e6fada7f34ec49d2e5a1bf8e81bd266e59354a..989292064752851962f90b8eab1e358ec93f7575 100644 --- a/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java @@ -1,17 +1,18 @@ package org.briarproject.crypto; -import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.api.crypto.StreamEncrypterFactory; +import org.briarproject.api.transport.StreamContext; import java.io.OutputStream; import javax.inject.Inject; import javax.inject.Provider; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.api.crypto.StreamEncrypter; -import org.briarproject.api.crypto.StreamEncrypterFactory; -import org.briarproject.api.transport.StreamContext; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { @@ -27,15 +28,23 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { public StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx) { + AuthenticatedCipher cipher = cipherProvider.get(); byte[] tag = new byte[TAG_LENGTH]; crypto.encodeTag(tag, ctx.getTagKey(), ctx.getStreamNumber()); - AuthenticatedCipher cipher = cipherProvider.get(); - return new StreamEncrypterImpl(out, cipher, ctx.getHeaderKey(), tag); + byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; + crypto.getSecureRandom().nextBytes(streamHeaderIv); + SecretKey frameKey = crypto.generateSecretKey(); + return new StreamEncrypterImpl(out, cipher, tag, streamHeaderIv, + ctx.getHeaderKey(), frameKey); } public StreamEncrypter createInvitationStreamEncrypter(OutputStream out, SecretKey headerKey) { AuthenticatedCipher cipher = cipherProvider.get(); - return new StreamEncrypterImpl(out, cipher, headerKey, null); + byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; + crypto.getSecureRandom().nextBytes(streamHeaderIv); + SecretKey frameKey = crypto.generateSecretKey(); + return new StreamEncrypterImpl(out, cipher, null, streamHeaderIv, + headerKey, frameKey); } } diff --git a/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java index 89fde2a91da9e7bfc506b04a77dfc63a0c119dcd..988be7e0293c9b91e0833af20fdff5543bbebdd8 100644 --- a/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java @@ -7,35 +7,41 @@ import java.io.IOException; import java.io.OutputStream; import java.security.GeneralSecurityException; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_LENGTH; import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; -// FIXME: Implementation is incomplete, doesn't write the stream header class StreamEncrypterImpl implements StreamEncrypter { private final OutputStream out; - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - private final byte[] tag, iv, framePlaintext, frameCiphertext; + private final AuthenticatedCipher cipher; + private final SecretKey streamHeaderKey, frameKey; + private final byte[] tag, streamHeaderIv; + private final byte[] frameIv, framePlaintext, frameCiphertext; private long frameNumber; - private boolean writeTag; + private boolean writeTag, writeStreamHeader; - StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher, - SecretKey headerKey, byte[] tag) { + StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher, + byte[] tag, byte[] streamHeaderIv, SecretKey streamHeaderKey, + SecretKey frameKey) { this.out = out; - this.frameCipher = frameCipher; - this.frameKey = headerKey; // FIXME + this.cipher = cipher; this.tag = tag; - iv = new byte[IV_LENGTH]; - framePlaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH]; + this.streamHeaderIv = streamHeaderIv; + this.streamHeaderKey = streamHeaderKey; + this.frameKey = frameKey; + frameIv = new byte[FRAME_IV_LENGTH]; + framePlaintext = new byte[FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH]; frameCiphertext = new byte[MAX_FRAME_LENGTH]; frameNumber = 0; writeTag = (tag != null); + writeStreamHeader = true; } public void writeFrame(byte[] payload, int payloadLength, @@ -45,52 +51,75 @@ class StreamEncrypterImpl implements StreamEncrypter { // Don't allow the frame counter to wrap if (frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException(); // Write the tag if required - if (writeTag) { - out.write(tag, 0, tag.length); - writeTag = false; - } + if (writeTag) writeTag(); + // Write the stream header if required + if (writeStreamHeader) writeStreamHeader(); // Encode the frame header FrameEncoder.encodeHeader(framePlaintext, finalFrame, payloadLength, paddingLength); // Encrypt and authenticate the frame header - FrameEncoder.encodeIv(iv, frameNumber, true); + FrameEncoder.encodeIv(frameIv, frameNumber, true); try { - frameCipher.init(true, frameKey, iv); - int encrypted = frameCipher.process(framePlaintext, 0, - HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 0); - if (encrypted != HEADER_LENGTH) throw new RuntimeException(); + cipher.init(true, frameKey, frameIv); + int encrypted = cipher.process(framePlaintext, 0, + FRAME_HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 0); + if (encrypted != FRAME_HEADER_LENGTH) throw new RuntimeException(); } catch (GeneralSecurityException badCipher) { throw new RuntimeException(badCipher); } // Combine the payload and padding - System.arraycopy(payload, 0, framePlaintext, HEADER_LENGTH, + System.arraycopy(payload, 0, framePlaintext, FRAME_HEADER_LENGTH, payloadLength); for (int i = 0; i < paddingLength; i++) - framePlaintext[HEADER_LENGTH + payloadLength + i] = 0; + framePlaintext[FRAME_HEADER_LENGTH + payloadLength + i] = 0; // Encrypt and authenticate the payload and padding - FrameEncoder.encodeIv(iv, frameNumber, false); + FrameEncoder.encodeIv(frameIv, frameNumber, false); try { - frameCipher.init(true, frameKey, iv); - int encrypted = frameCipher.process(framePlaintext, HEADER_LENGTH, + cipher.init(true, frameKey, frameIv); + int encrypted = cipher.process(framePlaintext, FRAME_HEADER_LENGTH, payloadLength + paddingLength, frameCiphertext, - HEADER_LENGTH); + FRAME_HEADER_LENGTH); if (encrypted != payloadLength + paddingLength + MAC_LENGTH) throw new RuntimeException(); } catch (GeneralSecurityException badCipher) { throw new RuntimeException(badCipher); } // Write the frame - out.write(frameCiphertext, 0, HEADER_LENGTH + payloadLength + out.write(frameCiphertext, 0, FRAME_HEADER_LENGTH + payloadLength + paddingLength + MAC_LENGTH); frameNumber++; } + private void writeTag() throws IOException { + out.write(tag, 0, tag.length); + writeTag = false; + } + + private void writeStreamHeader() throws IOException { + byte[] streamHeaderPlaintext = frameKey.getBytes(); + byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH]; + System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0, + STREAM_HEADER_IV_LENGTH); + // Encrypt and authenticate the frame key + try { + cipher.init(true, streamHeaderKey, streamHeaderIv); + int encrypted = cipher.process(streamHeaderPlaintext, 0, + SecretKey.LENGTH, streamHeaderCiphertext, + STREAM_HEADER_IV_LENGTH); + if (encrypted != SecretKey.LENGTH + MAC_LENGTH) + throw new RuntimeException(); + } catch (GeneralSecurityException badCipher) { + throw new RuntimeException(badCipher); + } + out.write(streamHeaderCiphertext); + writeStreamHeader = false; + } + public void flush() throws IOException { // Write the tag if required - if (writeTag) { - out.write(tag, 0, tag.length); - writeTag = false; - } + if (writeTag) writeTag(); + // Write the stream header if required + if (writeStreamHeader) writeStreamHeader(); out.flush(); } } \ No newline at end of file diff --git a/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java index c907f37e246939b7db1c13491317772fb0aa44b5..4ba24e7ea48f9036dd73982eedd9b332d3dabfbf 100644 --- a/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java +++ b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java @@ -1,37 +1,226 @@ package org.briarproject.crypto; import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.util.ByteUtils; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Random; + +import static junit.framework.Assert.assertEquals; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.fail; + public class StreamDecrypterImplTest extends BriarTestCase { + private final AuthenticatedCipher cipher; + private final SecretKey streamHeaderKey, frameKey; + private final byte[] streamHeaderIv; + private final Random random; + + public StreamDecrypterImplTest() { + cipher = new TestAuthenticatedCipher(); // Null cipher + streamHeaderKey = TestUtils.createSecretKey(); + frameKey = TestUtils.createSecretKey(); + streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; + random = new Random(); + random.nextBytes(streamHeaderIv); + } + @Test public void testReadValidFrames() throws Exception { - // FIXME + byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; + int payloadLength = 123, paddingLength = 234; + FrameEncoder.encodeHeader(frameHeader, false, payloadLength, + paddingLength); + byte[] payload = new byte[payloadLength]; + random.nextBytes(payload); + + byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH]; + int payloadLength1 = 345, paddingLength1 = 456; + FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1, + paddingLength1); + byte[] payload1 = new byte[payloadLength1]; + random.nextBytes(payload1); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(streamHeaderIv); + out.write(frameKey.getBytes()); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader); + out.write(payload); + out.write(new byte[paddingLength]); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader1); + out.write(payload1); + out.write(new byte[paddingLength1]); + out.write(new byte[MAC_LENGTH]); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher, + streamHeaderKey); + + // Read the first frame + byte[] buffer = new byte[MAX_PAYLOAD_LENGTH]; + assertEquals(payloadLength, s.readFrame(buffer)); + assertArrayStartsWith(payload, buffer, payloadLength); + + // Read the second frame + assertEquals(payloadLength1, s.readFrame(buffer)); + assertArrayStartsWith(payload1, buffer, payloadLength1); + + // End of stream + assertEquals(-1, s.readFrame(buffer)); } @Test public void testTruncatedFrameThrowsException() throws Exception { - // FIXME - } + byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; + int payloadLength = 123, paddingLength = 234; + FrameEncoder.encodeHeader(frameHeader, false, payloadLength, + paddingLength); + byte[] payload = new byte[payloadLength]; + random.nextBytes(payload); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(streamHeaderIv); + out.write(frameKey.getBytes()); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader); + out.write(payload); + out.write(new byte[paddingLength]); + out.write(new byte[MAC_LENGTH - 1]); // Chop off the last byte - @Test - public void testModifiedFrameThrowsException() throws Exception { - // FIXME + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher, + streamHeaderKey); + + // Try to read the truncated frame + byte[] buffer = new byte[MAX_PAYLOAD_LENGTH]; + try { + s.readFrame(buffer); + fail(); + } catch (IOException expected) { + // Expected + } } @Test - public void testInvalidPayloadLengthThrowsException() throws Exception { - // FIXME + public void testInvalidPayloadAndPaddingLengthThrowsException() + throws Exception { + byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; + // The payload length plus padding length is invalid + int payloadLength = MAX_PAYLOAD_LENGTH - 1, paddingLength = 2; + ByteUtils.writeUint16(payloadLength, frameHeader, 0); + ByteUtils.writeUint16(paddingLength, frameHeader, 2); + byte[] payload = new byte[payloadLength]; + random.nextBytes(payload); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(streamHeaderIv); + out.write(frameKey.getBytes()); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader); + out.write(payload); + out.write(new byte[paddingLength]); + out.write(new byte[MAC_LENGTH]); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher, + streamHeaderKey); + + // Try to read the invalid frame + byte[] buffer = new byte[MAX_PAYLOAD_LENGTH]; + try { + s.readFrame(buffer); + fail(); + } catch (IOException expected) { + // Expected + } } @Test public void testNonZeroPaddingThrowsException() throws Exception { - // FIXME + byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; + int payloadLength = 123, paddingLength = 234; + FrameEncoder.encodeHeader(frameHeader, false, payloadLength, + paddingLength); + byte[] payload = new byte[payloadLength]; + random.nextBytes(payload); + // Set one of the padding bytes non-zero + byte[] padding = new byte[paddingLength]; + padding[paddingLength - 1] = 1; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(streamHeaderIv); + out.write(frameKey.getBytes()); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader); + out.write(payload); + out.write(padding); + out.write(new byte[MAC_LENGTH]); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher, + streamHeaderKey); + + // Try to read the invalid frame + byte[] buffer = new byte[MAX_PAYLOAD_LENGTH]; + try { + s.readFrame(buffer); + fail(); + } catch (IOException expected) { + // Expected + } } @Test public void testCannotReadBeyondFinalFrame() throws Exception { - // FIXME + byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; + int payloadLength = 123, paddingLength = 234; + FrameEncoder.encodeHeader(frameHeader, true, payloadLength, + paddingLength); + byte[] payload = new byte[payloadLength]; + random.nextBytes(payload); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(streamHeaderIv); + out.write(frameKey.getBytes()); + out.write(new byte[MAC_LENGTH]); + out.write(frameHeader); + out.write(payload); + out.write(new byte[paddingLength]); + out.write(new byte[MAC_LENGTH]); + // Add some data beyond the final frame + out.write(new byte[1024]); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher, + streamHeaderKey); + + // Read the first frame + byte[] buffer = new byte[MAX_PAYLOAD_LENGTH]; + assertEquals(payloadLength, s.readFrame(buffer)); + assertArrayStartsWith(payload, buffer, payloadLength); + + // End of stream + assertEquals(-1, s.readFrame(buffer)); + + // Yup, definitely end of stream + assertEquals(-1, s.readFrame(buffer)); + } + + private static void assertArrayStartsWith(byte[] expected, byte[] actual, + int len) { + byte[] prefix = new byte[len]; + System.arraycopy(actual, 0, prefix, 0, len); + assertArrayEquals(expected, prefix); } } diff --git a/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java index 947c4bee1287599b6f321e2c64d7eb9d8ec4b4ce..d67980a1cdb84ec8322988b346cd96a86944c10b 100644 --- a/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java +++ b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java @@ -1,261 +1,345 @@ package org.briarproject.crypto; import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; import org.briarproject.api.crypto.SecretKey; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.util.Random; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; public class StreamEncrypterImplTest extends BriarTestCase { - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - private final byte[] tag; + private final AuthenticatedCipher cipher; + private final SecretKey streamHeaderKey, frameKey; + private final byte[] tag, streamHeaderIv; + private final Random random; public StreamEncrypterImplTest() { - frameCipher = new TestAuthenticatedCipher(); - frameKey = new SecretKey(new byte[32]); + cipher = new TestAuthenticatedCipher(); // Null cipher + streamHeaderKey = TestUtils.createSecretKey(); + frameKey = TestUtils.createSecretKey(); tag = new byte[TAG_LENGTH]; - new Random().nextBytes(tag); + streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; + random = new Random(); + random.nextBytes(tag); + random.nextBytes(streamHeaderIv); } @Test public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, 0, false); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, false, payloadLength, 0); - int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH; - byte[] expected = new byte[TAG_LENGTH + frameLength]; - System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); - System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, - payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the tag, stream header, frame header, payload and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, 0); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWriteUnpaddedFinalFrameWithTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123; - int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, 0, true); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, true, payloadLength, 0); - byte[] expected = new byte[TAG_LENGTH + frameLength]; - System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); - System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, - payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the tag, stream header, frame header, payload and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, true, payloadLength, 0); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123; - int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, 0, false); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, false, payloadLength, 0); - byte[] expected = new byte[frameLength]; - System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the stream header, frame header, payload and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, 0); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123; - int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, 0, true); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, true, payloadLength, 0); - byte[] expected = new byte[frameLength]; - System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the stream header, frame header, payload and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, true, payloadLength, 0); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWritePaddedNonFinalFrameWithTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123, paddingLength = 234; - int frameLength = HEADER_LENGTH + payloadLength + paddingLength - + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, paddingLength, false); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); - byte[] expected = new byte[TAG_LENGTH + frameLength]; - System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); - System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, - payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the tag, stream header, frame header, payload, padding and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, + paddingLength); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[paddingLength]); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWritePaddedFinalFrameWithTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123, paddingLength = 234; - int frameLength = HEADER_LENGTH + payloadLength + paddingLength - + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, paddingLength, true); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength); - byte[] expected = new byte[TAG_LENGTH + frameLength]; - System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); - System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, - payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the tag, stream header, frame header, payload, padding and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, true, payloadLength, + paddingLength); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[paddingLength]); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWritePaddedNonFinalFrameWithoutTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123, paddingLength = 234; - int frameLength = HEADER_LENGTH + payloadLength + paddingLength - + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, paddingLength, false); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); - byte[] expected = new byte[frameLength]; - System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the stream header, frame header, payload, padding and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, + paddingLength); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[paddingLength]); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testWritePaddedFinalFrameWithoutTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123, paddingLength = 234; - int frameLength = HEADER_LENGTH + payloadLength + paddingLength - + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); s.writeFrame(payload, payloadLength, paddingLength, true); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength); - byte[] expected = new byte[frameLength]; - System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); - assertArrayEquals(expected, out.toByteArray()); + // Expect the stream header, frame header, payload, padding and MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, true, payloadLength, + paddingLength); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[paddingLength]); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test - public void testWriteTwoFrames() throws Exception { + public void testWriteTwoFramesWithTag() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); int payloadLength = 123, paddingLength = 234; - int frameLength = HEADER_LENGTH + payloadLength + paddingLength - + MAC_LENGTH; byte[] payload = new byte[payloadLength]; - new Random().nextBytes(payload); + random.nextBytes(payload); int payloadLength1 = 345, paddingLength1 = 456; - int frameLength1 = HEADER_LENGTH + payloadLength1 + paddingLength1 - + MAC_LENGTH; byte[] payload1 = new byte[payloadLength1]; - new Random().nextBytes(payload1); + random.nextBytes(payload1); s.writeFrame(payload, payloadLength, paddingLength, false); s.writeFrame(payload1, payloadLength1, paddingLength1, true); - byte[] header = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); - byte[] header1 = new byte[HEADER_LENGTH]; - FrameEncoder.encodeHeader(header1, true, payloadLength1, + // Expect the tag, stream header, first frame header, payload, padding, + // MAC, second frame header, payload, padding, MAC + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, + paddingLength); + expected.write(expectedFrameHeader); + expected.write(payload); + expected.write(new byte[paddingLength]); + expected.write(new byte[MAC_LENGTH]); + byte[] expectedFrameHeader1 = new byte[FRAME_HEADER_LENGTH]; + FrameEncoder.encodeHeader(expectedFrameHeader1, true, payloadLength1, paddingLength1); - byte[] expected = new byte[frameLength + frameLength1]; - System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); - System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); - System.arraycopy(header1, 0, expected, frameLength, HEADER_LENGTH); - System.arraycopy(payload1, 0, expected, frameLength + HEADER_LENGTH, - payloadLength1); - assertArrayEquals(expected, out.toByteArray()); + expected.write(expectedFrameHeader1); + expected.write(payload1); + expected.write(new byte[paddingLength1]); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test - public void testFlushWritesTagIfNotAlreadyWritten() throws Exception { + public void testFlushWritesTagAndStreamHeaderIfNotAlreadyWritten() + throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); + + // Flush the stream once s.flush(); - assertArrayEquals(tag, out.toByteArray()); + + // Expect the tag and stream header + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test - public void testFlushDoesNotWriteTagIfAlreadyWritten() throws Exception { + public void testFlushDoesNotWriteTagOrStreamHeaderIfAlreadyWritten() + throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, tag); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag, + streamHeaderIv, streamHeaderKey, frameKey); + + // Flush the stream twice s.flush(); s.flush(); - assertArrayEquals(tag, out.toByteArray()); + + // Expect the tag and stream header + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(tag); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } @Test public void testFlushDoesNotWriteTagIfNull() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, - frameKey, null); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null, + streamHeaderIv, streamHeaderKey, frameKey); + + // Flush the stream once s.flush(); - assertEquals(0, out.size()); + + // Expect the stream header + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(streamHeaderIv); + expected.write(frameKey.getBytes()); + expected.write(new byte[MAC_LENGTH]); + + assertArrayEquals(expected.toByteArray(), out.toByteArray()); } } diff --git a/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java index 9d06e52a4f1389abbc556dd6c1c08e297dd384a5..5a47083e83a4fde3c55cd38d2c4206f07fe844ec 100644 --- a/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java +++ b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java @@ -8,7 +8,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; @@ -24,8 +24,8 @@ class TestStreamDecrypter implements StreamDecrypter { public int readFrame(byte[] payload) throws IOException { int offset = 0; - while (offset < HEADER_LENGTH) { - int read = in.read(frame, offset, HEADER_LENGTH - offset); + while (offset < FRAME_HEADER_LENGTH) { + int read = in.read(frame, offset, FRAME_HEADER_LENGTH - offset); if (read == -1) throw new EOFException(); offset += read; } @@ -37,9 +37,9 @@ class TestStreamDecrypter implements StreamDecrypter { offset += read; } if (!finalFrame && offset < frame.length) throw new EOFException(); - if (offset < HEADER_LENGTH + payloadLength + MAC_LENGTH) + if (offset < FRAME_HEADER_LENGTH + payloadLength + MAC_LENGTH) throw new FormatException(); - System.arraycopy(frame, HEADER_LENGTH, payload, 0, payloadLength); + System.arraycopy(frame, FRAME_HEADER_LENGTH, payload, 0, payloadLength); return payloadLength; } } diff --git a/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java index 6aa4c806843ff51067beef93e0b7aadfcdb5dfb6..24a0dc783a61561b5b2332c30317aa190a063c9d 100644 --- a/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java +++ b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java @@ -6,7 +6,7 @@ import org.briarproject.util.ByteUtils; import java.io.IOException; import java.io.OutputStream; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; @@ -31,11 +31,11 @@ class TestStreamEncrypter implements StreamEncrypter { } ByteUtils.writeUint16(payloadLength, frame, 0); if (finalFrame) frame[0] |= 0x80; - System.arraycopy(payload, 0, frame, HEADER_LENGTH, payloadLength); - for (int i = HEADER_LENGTH + payloadLength; i < frame.length; i++) + System.arraycopy(payload, 0, frame, FRAME_HEADER_LENGTH, payloadLength); + for (int i = FRAME_HEADER_LENGTH + payloadLength; i < frame.length; i++) frame[i] = 0; if (finalFrame) - out.write(frame, 0, HEADER_LENGTH + payloadLength + MAC_LENGTH); + out.write(frame, 0, FRAME_HEADER_LENGTH + payloadLength + MAC_LENGTH); else out.write(frame, 0, frame.length); }