mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Implement stream header for BTPv2. #111
This commit is contained in:
@@ -1,25 +1,35 @@
|
|||||||
package org.briarproject.api.transport;
|
package org.briarproject.api.transport;
|
||||||
|
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
|
||||||
public interface TransportConstants {
|
public interface TransportConstants {
|
||||||
|
|
||||||
/** The length of the pseudo-random tag in bytes. */
|
/** The length of the pseudo-random tag in bytes. */
|
||||||
int TAG_LENGTH = 16;
|
int TAG_LENGTH = 16;
|
||||||
|
|
||||||
/** The maximum length of a frame in bytes, including the header and MAC. */
|
/** The length of the stream header IV in bytes. */
|
||||||
int MAX_FRAME_LENGTH = 1024;
|
int STREAM_HEADER_IV_LENGTH = 12;
|
||||||
|
|
||||||
/** The length of the message authentication code (MAC) in bytes. */
|
/** The length of the message authentication code (MAC) in bytes. */
|
||||||
int MAC_LENGTH = 16;
|
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. */
|
/** The length of the frame header in bytes. */
|
||||||
int HEADER_LENGTH = 4 + MAC_LENGTH;
|
int FRAME_HEADER_LENGTH = 4 + MAC_LENGTH;
|
||||||
|
|
||||||
|
/** The maximum length of a frame in bytes, including the header and MAC. */
|
||||||
|
int MAX_FRAME_LENGTH = 1024;
|
||||||
|
|
||||||
/** The maximum total length of the frame payload and padding in bytes. */
|
/** The maximum total length of the frame payload and padding in bytes. */
|
||||||
int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH
|
||||||
|
- MAC_LENGTH;
|
||||||
/** The length of the initalisation vector (IV) in bytes. */
|
|
||||||
int IV_LENGTH = 12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum stream length in bytes that all transport plugins must
|
* The minimum stream length in bytes that all transport plugins must
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH;
|
import org.briarproject.util.ByteUtils;
|
||||||
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.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
import org.briarproject.util.ByteUtils;
|
|
||||||
|
|
||||||
class FrameEncoder {
|
class FrameEncoder {
|
||||||
|
|
||||||
static void encodeIv(byte[] iv, long frameNumber, boolean header) {
|
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)
|
if (frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
ByteUtils.writeUint32(frameNumber, iv, 0);
|
ByteUtils.writeUint32(frameNumber, iv, 0);
|
||||||
if (header) iv[4] = 1;
|
if (header) iv[4] = 1;
|
||||||
else iv[4] = 0;
|
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,
|
static void encodeHeader(byte[] header, boolean finalFrame,
|
||||||
int payloadLength, int paddingLength) {
|
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 (payloadLength < 0) throw new IllegalArgumentException();
|
||||||
if (paddingLength < 0) throw new IllegalArgumentException();
|
if (paddingLength < 0) throw new IllegalArgumentException();
|
||||||
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
||||||
@@ -32,17 +33,20 @@ class FrameEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isFinalFrame(byte[] header) {
|
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;
|
return (header[0] & 0x80) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getPayloadLength(byte[] header) {
|
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;
|
return ByteUtils.readUint16(header, 0) & 0x7FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getPaddingLength(byte[] header) {
|
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);
|
return ByteUtils.readUint16(header, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,31 +9,34 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
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.IV_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.MAC_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_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.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 {
|
class StreamDecrypterImpl implements StreamDecrypter {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey frameKey;
|
private final SecretKey streamHeaderKey;
|
||||||
private final byte[] iv, frameHeader, frameCiphertext;
|
private final byte[] frameIv, frameHeader, frameCiphertext;
|
||||||
|
|
||||||
|
private SecretKey frameKey;
|
||||||
private long frameNumber;
|
private long frameNumber;
|
||||||
private boolean finalFrame;
|
private boolean finalFrame;
|
||||||
|
|
||||||
StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher,
|
StreamDecrypterImpl(InputStream in, AuthenticatedCipher cipher,
|
||||||
SecretKey headerKey) {
|
SecretKey streamHeaderKey) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.frameCipher = frameCipher;
|
this.cipher = cipher;
|
||||||
this.frameKey = headerKey; // FIXME
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
iv = new byte[IV_LENGTH];
|
frameIv = new byte[FRAME_IV_LENGTH];
|
||||||
frameHeader = new byte[HEADER_LENGTH];
|
frameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
||||||
|
frameKey = null;
|
||||||
frameNumber = 0;
|
frameNumber = 0;
|
||||||
finalFrame = false;
|
finalFrame = false;
|
||||||
}
|
}
|
||||||
@@ -43,20 +46,23 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (payload.length < MAX_PAYLOAD_LENGTH)
|
if (payload.length < MAX_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (finalFrame) return -1;
|
if (finalFrame) return -1;
|
||||||
|
// Read the stream header if required
|
||||||
|
if (frameKey == null) readStreamHeader();
|
||||||
// Read the frame header
|
// Read the frame header
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (offset < HEADER_LENGTH) {
|
while (offset < FRAME_HEADER_LENGTH) {
|
||||||
int read = in.read(frameCiphertext, offset, HEADER_LENGTH - offset);
|
int read = in.read(frameCiphertext, offset,
|
||||||
|
FRAME_HEADER_LENGTH - offset);
|
||||||
if (read == -1) throw new EOFException();
|
if (read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
// Decrypt and authenticate the frame header
|
// Decrypt and authenticate the frame header
|
||||||
FrameEncoder.encodeIv(iv, frameNumber, true);
|
FrameEncoder.encodeIv(frameIv, frameNumber, true);
|
||||||
try {
|
try {
|
||||||
frameCipher.init(false, frameKey, iv);
|
cipher.init(false, frameKey, frameIv);
|
||||||
int decrypted = frameCipher.process(frameCiphertext, 0,
|
int decrypted = cipher.process(frameCiphertext, 0,
|
||||||
HEADER_LENGTH, frameHeader, 0);
|
FRAME_HEADER_LENGTH, frameHeader, 0);
|
||||||
if (decrypted != HEADER_LENGTH - MAC_LENGTH)
|
if (decrypted != FRAME_HEADER_LENGTH - MAC_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
@@ -68,7 +74,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
// Read the payload and padding
|
// Read the payload and padding
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
int frameLength = FRAME_HEADER_LENGTH + payloadLength + paddingLength
|
||||||
+ MAC_LENGTH;
|
+ MAC_LENGTH;
|
||||||
while (offset < frameLength) {
|
while (offset < frameLength) {
|
||||||
int read = in.read(frameCiphertext, offset, frameLength - offset);
|
int read = in.read(frameCiphertext, offset, frameLength - offset);
|
||||||
@@ -76,10 +82,10 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
// Decrypt and authenticate the payload and padding
|
// Decrypt and authenticate the payload and padding
|
||||||
FrameEncoder.encodeIv(iv, frameNumber, false);
|
FrameEncoder.encodeIv(frameIv, frameNumber, false);
|
||||||
try {
|
try {
|
||||||
frameCipher.init(false, frameKey, iv);
|
cipher.init(false, frameKey, frameIv);
|
||||||
int decrypted = frameCipher.process(frameCiphertext, HEADER_LENGTH,
|
int decrypted = cipher.process(frameCiphertext, FRAME_HEADER_LENGTH,
|
||||||
payloadLength + paddingLength + MAC_LENGTH, payload, 0);
|
payloadLength + paddingLength + MAC_LENGTH, payload, 0);
|
||||||
if (decrypted != payloadLength + paddingLength)
|
if (decrypted != payloadLength + paddingLength)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
@@ -92,4 +98,31 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
frameNumber++;
|
frameNumber++;
|
||||||
return payloadLength;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
import org.briarproject.api.crypto.StreamEncrypter;
|
import org.briarproject.api.crypto.StreamEncrypter;
|
||||||
import org.briarproject.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.api.crypto.StreamEncrypterFactory;
|
||||||
import org.briarproject.api.transport.StreamContext;
|
import org.briarproject.api.transport.StreamContext;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
||||||
|
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
@@ -27,15 +28,23 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
|
|
||||||
public StreamEncrypter createStreamEncrypter(OutputStream out,
|
public StreamEncrypter createStreamEncrypter(OutputStream out,
|
||||||
StreamContext ctx) {
|
StreamContext ctx) {
|
||||||
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, ctx.getTagKey(), ctx.getStreamNumber());
|
crypto.encodeTag(tag, ctx.getTagKey(), ctx.getStreamNumber());
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
||||||
return new StreamEncrypterImpl(out, cipher, ctx.getHeaderKey(), tag);
|
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
||||||
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
|
return new StreamEncrypterImpl(out, cipher, tag, streamHeaderIv,
|
||||||
|
ctx.getHeaderKey(), frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,35 +7,41 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
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.IV_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.MAC_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_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.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;
|
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
// FIXME: Implementation is incomplete, doesn't write the stream header
|
|
||||||
class StreamEncrypterImpl implements StreamEncrypter {
|
class StreamEncrypterImpl implements StreamEncrypter {
|
||||||
|
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey frameKey;
|
private final SecretKey streamHeaderKey, frameKey;
|
||||||
private final byte[] tag, iv, framePlaintext, frameCiphertext;
|
private final byte[] tag, streamHeaderIv;
|
||||||
|
private final byte[] frameIv, framePlaintext, frameCiphertext;
|
||||||
|
|
||||||
private long frameNumber;
|
private long frameNumber;
|
||||||
private boolean writeTag;
|
private boolean writeTag, writeStreamHeader;
|
||||||
|
|
||||||
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher,
|
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
||||||
SecretKey headerKey, byte[] tag) {
|
byte[] tag, byte[] streamHeaderIv, SecretKey streamHeaderKey,
|
||||||
|
SecretKey frameKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.frameCipher = frameCipher;
|
this.cipher = cipher;
|
||||||
this.frameKey = headerKey; // FIXME
|
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
iv = new byte[IV_LENGTH];
|
this.streamHeaderIv = streamHeaderIv;
|
||||||
framePlaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH];
|
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];
|
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
||||||
frameNumber = 0;
|
frameNumber = 0;
|
||||||
writeTag = (tag != null);
|
writeTag = (tag != null);
|
||||||
|
writeStreamHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeFrame(byte[] payload, int payloadLength,
|
public void writeFrame(byte[] payload, int payloadLength,
|
||||||
@@ -45,52 +51,75 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
// Don't allow the frame counter to wrap
|
// Don't allow the frame counter to wrap
|
||||||
if (frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException();
|
if (frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException();
|
||||||
// Write the tag if required
|
// Write the tag if required
|
||||||
if (writeTag) {
|
if (writeTag) writeTag();
|
||||||
out.write(tag, 0, tag.length);
|
// Write the stream header if required
|
||||||
writeTag = false;
|
if (writeStreamHeader) writeStreamHeader();
|
||||||
}
|
|
||||||
// Encode the frame header
|
// Encode the frame header
|
||||||
FrameEncoder.encodeHeader(framePlaintext, finalFrame, payloadLength,
|
FrameEncoder.encodeHeader(framePlaintext, finalFrame, payloadLength,
|
||||||
paddingLength);
|
paddingLength);
|
||||||
// Encrypt and authenticate the frame header
|
// Encrypt and authenticate the frame header
|
||||||
FrameEncoder.encodeIv(iv, frameNumber, true);
|
FrameEncoder.encodeIv(frameIv, frameNumber, true);
|
||||||
try {
|
try {
|
||||||
frameCipher.init(true, frameKey, iv);
|
cipher.init(true, frameKey, frameIv);
|
||||||
int encrypted = frameCipher.process(framePlaintext, 0,
|
int encrypted = cipher.process(framePlaintext, 0,
|
||||||
HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 0);
|
FRAME_HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 0);
|
||||||
if (encrypted != HEADER_LENGTH) throw new RuntimeException();
|
if (encrypted != FRAME_HEADER_LENGTH) throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
}
|
}
|
||||||
// Combine the payload and padding
|
// Combine the payload and padding
|
||||||
System.arraycopy(payload, 0, framePlaintext, HEADER_LENGTH,
|
System.arraycopy(payload, 0, framePlaintext, FRAME_HEADER_LENGTH,
|
||||||
payloadLength);
|
payloadLength);
|
||||||
for (int i = 0; i < paddingLength; i++)
|
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
|
// Encrypt and authenticate the payload and padding
|
||||||
FrameEncoder.encodeIv(iv, frameNumber, false);
|
FrameEncoder.encodeIv(frameIv, frameNumber, false);
|
||||||
try {
|
try {
|
||||||
frameCipher.init(true, frameKey, iv);
|
cipher.init(true, frameKey, frameIv);
|
||||||
int encrypted = frameCipher.process(framePlaintext, HEADER_LENGTH,
|
int encrypted = cipher.process(framePlaintext, FRAME_HEADER_LENGTH,
|
||||||
payloadLength + paddingLength, frameCiphertext,
|
payloadLength + paddingLength, frameCiphertext,
|
||||||
HEADER_LENGTH);
|
FRAME_HEADER_LENGTH);
|
||||||
if (encrypted != payloadLength + paddingLength + MAC_LENGTH)
|
if (encrypted != payloadLength + paddingLength + MAC_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
}
|
}
|
||||||
// Write the frame
|
// Write the frame
|
||||||
out.write(frameCiphertext, 0, HEADER_LENGTH + payloadLength
|
out.write(frameCiphertext, 0, FRAME_HEADER_LENGTH + payloadLength
|
||||||
+ paddingLength + MAC_LENGTH);
|
+ paddingLength + MAC_LENGTH);
|
||||||
frameNumber++;
|
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 {
|
public void flush() throws IOException {
|
||||||
// Write the tag if required
|
// Write the tag if required
|
||||||
if (writeTag) {
|
if (writeTag) writeTag();
|
||||||
out.write(tag, 0, tag.length);
|
// Write the stream header if required
|
||||||
writeTag = false;
|
if (writeStreamHeader) writeStreamHeader();
|
||||||
}
|
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,226 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.util.ByteUtils;
|
||||||
import org.junit.Test;
|
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 {
|
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
|
@Test
|
||||||
public void testReadValidFrames() throws Exception {
|
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
|
@Test
|
||||||
public void testTruncatedFrameThrowsException() throws Exception {
|
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
|
||||||
|
|
||||||
|
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
|
@Test
|
||||||
public void testModifiedFrameThrowsException() throws Exception {
|
public void testInvalidPayloadAndPaddingLengthThrowsException()
|
||||||
// FIXME
|
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);
|
||||||
|
|
||||||
@Test
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
public void testInvalidPayloadLengthThrowsException() throws Exception {
|
out.write(streamHeaderIv);
|
||||||
// FIXME
|
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
|
@Test
|
||||||
public void testNonZeroPaddingThrowsException() throws Exception {
|
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
|
@Test
|
||||||
public void testCannotReadBeyondFinalFrame() throws Exception {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,261 +1,345 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.Random;
|
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.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.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class StreamEncrypterImplTest extends BriarTestCase {
|
public class StreamEncrypterImplTest extends BriarTestCase {
|
||||||
|
|
||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey frameKey;
|
private final SecretKey streamHeaderKey, frameKey;
|
||||||
private final byte[] tag;
|
private final byte[] tag, streamHeaderIv;
|
||||||
|
private final Random random;
|
||||||
|
|
||||||
public StreamEncrypterImplTest() {
|
public StreamEncrypterImplTest() {
|
||||||
frameCipher = new TestAuthenticatedCipher();
|
cipher = new TestAuthenticatedCipher(); // Null cipher
|
||||||
frameKey = new SecretKey(new byte[32]);
|
streamHeaderKey = TestUtils.createSecretKey();
|
||||||
|
frameKey = TestUtils.createSecretKey();
|
||||||
tag = new byte[TAG_LENGTH];
|
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
|
@Test
|
||||||
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123;
|
int payloadLength = 123;
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, false);
|
s.writeFrame(payload, payloadLength, 0, false);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the tag, stream header, frame header, payload and MAC
|
||||||
FrameEncoder.encodeHeader(header, false, payloadLength, 0);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH;
|
expected.write(tag);
|
||||||
byte[] expected = new byte[TAG_LENGTH + frameLength];
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(tag, 0, expected, 0, TAG_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH,
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
payloadLength);
|
FrameEncoder.encodeHeader(expectedFrameHeader, false, payloadLength, 0);
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
expected.write(expectedFrameHeader);
|
||||||
|
expected.write(payload);
|
||||||
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
|
assertArrayEquals(expected.toByteArray(), out.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
|
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123;
|
int payloadLength = 123;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, true);
|
s.writeFrame(payload, payloadLength, 0, true);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the tag, stream header, frame header, payload and MAC
|
||||||
FrameEncoder.encodeHeader(header, true, payloadLength, 0);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[TAG_LENGTH + frameLength];
|
expected.write(tag);
|
||||||
System.arraycopy(tag, 0, expected, 0, TAG_LENGTH);
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH,
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
payloadLength);
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
FrameEncoder.encodeHeader(expectedFrameHeader, true, payloadLength, 0);
|
||||||
|
expected.write(expectedFrameHeader);
|
||||||
|
expected.write(payload);
|
||||||
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
|
assertArrayEquals(expected.toByteArray(), out.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
|
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123;
|
int payloadLength = 123;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, false);
|
s.writeFrame(payload, payloadLength, 0, false);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the stream header, frame header, payload and MAC
|
||||||
FrameEncoder.encodeHeader(header, false, payloadLength, 0);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[frameLength];
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, 0, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
|
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123;
|
int payloadLength = 123;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, true);
|
s.writeFrame(payload, payloadLength, 0, true);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the stream header, frame header, payload and MAC
|
||||||
FrameEncoder.encodeHeader(header, true, payloadLength, 0);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[frameLength];
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, 0, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
|
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123, paddingLength = 234;
|
int payloadLength = 123, paddingLength = 234;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, false);
|
s.writeFrame(payload, payloadLength, paddingLength, false);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the tag, stream header, frame header, payload, padding and MAC
|
||||||
FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[TAG_LENGTH + frameLength];
|
expected.write(tag);
|
||||||
System.arraycopy(tag, 0, expected, 0, TAG_LENGTH);
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH,
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
payloadLength);
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWritePaddedFinalFrameWithTag() throws Exception {
|
public void testWritePaddedFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123, paddingLength = 234;
|
int payloadLength = 123, paddingLength = 234;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, true);
|
s.writeFrame(payload, payloadLength, paddingLength, true);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the tag, stream header, frame header, payload, padding and MAC
|
||||||
FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[TAG_LENGTH + frameLength];
|
expected.write(tag);
|
||||||
System.arraycopy(tag, 0, expected, 0, TAG_LENGTH);
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH,
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
payloadLength);
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
|
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123, paddingLength = 234;
|
int payloadLength = 123, paddingLength = 234;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, false);
|
s.writeFrame(payload, payloadLength, paddingLength, false);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the stream header, frame header, payload, padding and MAC
|
||||||
FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[frameLength];
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, 0, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
|
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123, paddingLength = 234;
|
int payloadLength = 123, paddingLength = 234;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, true);
|
s.writeFrame(payload, payloadLength, paddingLength, true);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the stream header, frame header, payload, padding and MAC
|
||||||
FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength);
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
byte[] expected = new byte[frameLength];
|
expected.write(streamHeaderIv);
|
||||||
System.arraycopy(header, 0, expected, 0, HEADER_LENGTH);
|
expected.write(frameKey.getBytes());
|
||||||
System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
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
|
@Test
|
||||||
public void testWriteTwoFrames() throws Exception {
|
public void testWriteTwoFramesWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
int payloadLength = 123, paddingLength = 234;
|
int payloadLength = 123, paddingLength = 234;
|
||||||
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload = new byte[payloadLength];
|
byte[] payload = new byte[payloadLength];
|
||||||
new Random().nextBytes(payload);
|
random.nextBytes(payload);
|
||||||
int payloadLength1 = 345, paddingLength1 = 456;
|
int payloadLength1 = 345, paddingLength1 = 456;
|
||||||
int frameLength1 = HEADER_LENGTH + payloadLength1 + paddingLength1
|
|
||||||
+ MAC_LENGTH;
|
|
||||||
byte[] payload1 = new byte[payloadLength1];
|
byte[] payload1 = new byte[payloadLength1];
|
||||||
new Random().nextBytes(payload1);
|
random.nextBytes(payload1);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, false);
|
s.writeFrame(payload, payloadLength, paddingLength, false);
|
||||||
s.writeFrame(payload1, payloadLength1, paddingLength1, true);
|
s.writeFrame(payload1, payloadLength1, paddingLength1, true);
|
||||||
|
|
||||||
byte[] header = new byte[HEADER_LENGTH];
|
// Expect the tag, stream header, first frame header, payload, padding,
|
||||||
FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength);
|
// MAC, second frame header, payload, padding, MAC
|
||||||
byte[] header1 = new byte[HEADER_LENGTH];
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
FrameEncoder.encodeHeader(header1, true, payloadLength1,
|
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);
|
paddingLength1);
|
||||||
byte[] expected = new byte[frameLength + frameLength1];
|
expected.write(expectedFrameHeader1);
|
||||||
System.arraycopy(header, 0, expected, 0, HEADER_LENGTH);
|
expected.write(payload1);
|
||||||
System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength);
|
expected.write(new byte[paddingLength1]);
|
||||||
System.arraycopy(header1, 0, expected, frameLength, HEADER_LENGTH);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
System.arraycopy(payload1, 0, expected, frameLength + HEADER_LENGTH,
|
|
||||||
payloadLength1);
|
assertArrayEquals(expected.toByteArray(), out.toByteArray());
|
||||||
assertArrayEquals(expected, out.toByteArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFlushWritesTagIfNotAlreadyWritten() throws Exception {
|
public void testFlushWritesTagAndStreamHeaderIfNotAlreadyWritten()
|
||||||
|
throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
// Flush the stream once
|
||||||
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
|
@Test
|
||||||
public void testFlushDoesNotWriteTagIfAlreadyWritten() throws Exception {
|
public void testFlushDoesNotWriteTagOrStreamHeaderIfAlreadyWritten()
|
||||||
|
throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, tag,
|
||||||
frameKey, tag);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
// Flush the stream twice
|
||||||
s.flush();
|
s.flush();
|
||||||
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
|
@Test
|
||||||
public void testFlushDoesNotWriteTagIfNull() throws Exception {
|
public void testFlushDoesNotWriteTagIfNull() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, null,
|
||||||
frameKey, null);
|
streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
// Flush the stream once
|
||||||
s.flush();
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import java.io.EOFException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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.MAC_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_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 {
|
public int readFrame(byte[] payload) throws IOException {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (offset < HEADER_LENGTH) {
|
while (offset < FRAME_HEADER_LENGTH) {
|
||||||
int read = in.read(frame, offset, HEADER_LENGTH - offset);
|
int read = in.read(frame, offset, FRAME_HEADER_LENGTH - offset);
|
||||||
if (read == -1) throw new EOFException();
|
if (read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
@@ -37,9 +37,9 @@ class TestStreamDecrypter implements StreamDecrypter {
|
|||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
if (!finalFrame && offset < frame.length) throw new EOFException();
|
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();
|
throw new FormatException();
|
||||||
System.arraycopy(frame, HEADER_LENGTH, payload, 0, payloadLength);
|
System.arraycopy(frame, FRAME_HEADER_LENGTH, payload, 0, payloadLength);
|
||||||
return payloadLength;
|
return payloadLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.util.ByteUtils;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
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.MAC_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
@@ -31,11 +31,11 @@ class TestStreamEncrypter implements StreamEncrypter {
|
|||||||
}
|
}
|
||||||
ByteUtils.writeUint16(payloadLength, frame, 0);
|
ByteUtils.writeUint16(payloadLength, frame, 0);
|
||||||
if (finalFrame) frame[0] |= 0x80;
|
if (finalFrame) frame[0] |= 0x80;
|
||||||
System.arraycopy(payload, 0, frame, HEADER_LENGTH, payloadLength);
|
System.arraycopy(payload, 0, frame, FRAME_HEADER_LENGTH, payloadLength);
|
||||||
for (int i = HEADER_LENGTH + payloadLength; i < frame.length; i++)
|
for (int i = FRAME_HEADER_LENGTH + payloadLength; i < frame.length; i++)
|
||||||
frame[i] = 0;
|
frame[i] = 0;
|
||||||
if (finalFrame)
|
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);
|
else out.write(frame, 0, frame.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user