Converted incoming encryption layer from frames to segments.

This commit is contained in:
akwizgran
2012-01-17 16:45:25 +00:00
parent 8c0020873c
commit f6ed6dd60b
23 changed files with 285 additions and 180 deletions

View File

@@ -2,8 +2,6 @@ package net.sf.briar.api.plugins;
public interface Segment { public interface Segment {
void clear();
byte[] getBuffer(); byte[] getBuffer();
int getLength(); int getLength();

View File

@@ -30,9 +30,10 @@ class CryptoComponentImpl implements CryptoComponent {
private static final String CIPHER_ALGO = "AES/CTR/NoPadding"; private static final String CIPHER_ALGO = "AES/CTR/NoPadding";
private static final String SECRET_KEY_ALGO = "AES"; private static final String SECRET_KEY_ALGO = "AES";
private static final int SECRET_KEY_BYTES = 32; // 256 bits private static final int SECRET_KEY_BYTES = 32; // 256 bits
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
private static final String MAC_ALGO = "HMacSHA256"; private static final String MAC_ALGO = "HMacSHA256";
private static final String SIGNATURE_ALGO = "ECDSA"; private static final String SIGNATURE_ALGO = "ECDSA";
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
// Labels for key derivation, null-terminated // Labels for key derivation, null-terminated
private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 }; private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 };
@@ -176,7 +177,7 @@ class CryptoComponentImpl implements CryptoComponent {
public Cipher getTagCipher() { public Cipher getTagCipher() {
try { try {
return Cipher.getInstance(CIPHER_ALGO, PROVIDER); return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -27,9 +27,9 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// Validate the tag // Validate the tag
Cipher tagCipher = crypto.getTagCipher(); Cipher tagCipher = crypto.getTagCipher();
ErasableKey tagKey = crypto.deriveTagKey(secret, true); ErasableKey tagKey = crypto.deriveTagKey(secret, true);
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey); long segmentNumber = TagEncoder.decodeTag(tag, tagCipher, tagKey);
tagKey.erase(); tagKey.erase();
if(!valid) throw new IllegalArgumentException(); if(segmentNumber != 0) throw new IllegalArgumentException();
return createConnectionReader(in, true, secret); return createConnectionReader(in, true, secret);
} }
@@ -51,7 +51,10 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
Mac mac = crypto.getMac(); Mac mac = crypto.getMac();
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in, IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, frameCipher, tagKey, frameKey, false); tagCipher, frameCipher, tagKey, frameKey, false);
// No error correction
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
// Create the reader // Create the reader
return new ConnectionReaderImpl(decrypter, mac, macKey); return new ConnectionReaderImpl(correcter, mac, macKey);
} }
} }

View File

@@ -2,12 +2,13 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.util.Collection;
import java.util.Collections;
import javax.crypto.Mac; import javax.crypto.Mac;
@@ -17,16 +18,16 @@ import net.sf.briar.api.transport.ConnectionReader;
class ConnectionReaderImpl extends InputStream implements ConnectionReader { class ConnectionReaderImpl extends InputStream implements ConnectionReader {
private final IncomingEncryptionLayer decrypter; private final IncomingErrorCorrectionLayer in;
private final Mac mac; private final Mac mac;
private final byte[] buf; private final Frame frame;
private long frame = 0L; private long frameNumber = 0L;
private int offset = 0, length = 0; private int offset = 0, length = 0;
ConnectionReaderImpl(IncomingEncryptionLayer decrypter, Mac mac, ConnectionReaderImpl(IncomingErrorCorrectionLayer in, Mac mac,
ErasableKey macKey) { ErasableKey macKey) {
this.decrypter = decrypter; this.in = in;
this.mac = mac; this.mac = mac;
// Initialise the MAC // Initialise the MAC
try { try {
@@ -37,7 +38,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
macKey.erase(); macKey.erase();
if(mac.getMacLength() != MAC_LENGTH) if(mac.getMacLength() != MAC_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
buf = new byte[MAX_FRAME_LENGTH]; frame = new Frame();
} }
public InputStream getInputStream() { public InputStream getInputStream() {
@@ -47,7 +48,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
@Override @Override
public int read() throws IOException { public int read() throws IOException {
while(length == 0) if(!readFrame()) return -1; while(length == 0) if(!readFrame()) return -1;
int b = buf[offset] & 0xff; int b = frame.getBuffer()[offset] & 0xff;
offset++; offset++;
length--; length--;
return b; return b;
@@ -62,7 +63,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
while(length == 0) if(!readFrame()) return -1; while(length == 0) if(!readFrame()) return -1;
len = Math.min(len, length); len = Math.min(len, length);
System.arraycopy(buf, offset, b, off, len); System.arraycopy(frame.getBuffer(), offset, b, off, len);
offset += len; offset += len;
length -= len; length -= len;
return len; return len;
@@ -71,17 +72,19 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
private boolean readFrame() throws IOException { private boolean readFrame() throws IOException {
assert length == 0; assert length == 0;
// Don't allow more than 2^32 frames to be read // Don't allow more than 2^32 frames to be read
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
// Read a frame // Read a frame
int frameLength = decrypter.readFrame(buf); Collection<Long> window = Collections.singleton(frameNumber);
if(frameLength == -1) return false; if(!in.readFrame(frame, window)) return false;
// Check that the frame number is correct and the length is legal // Check that the frame number is correct and the length is legal
if(!HeaderEncoder.validateHeader(buf, frame)) byte[] buf = frame.getBuffer();
if(!HeaderEncoder.validateHeader(buf, frameNumber))
throw new FormatException(); throw new FormatException();
// Check that the payload and padding lengths are correct
int payload = HeaderEncoder.getPayloadLength(buf); int payload = HeaderEncoder.getPayloadLength(buf);
int padding = HeaderEncoder.getPaddingLength(buf); int padding = HeaderEncoder.getPaddingLength(buf);
if(frameLength != FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH) if(frame.getLength() != FRAME_HEADER_LENGTH + payload + padding
throw new FormatException(); + MAC_LENGTH) throw new FormatException();
// Check that the padding is all zeroes // Check that the padding is all zeroes
int paddingStart = FRAME_HEADER_LENGTH + payload; int paddingStart = FRAME_HEADER_LENGTH + payload;
for(int i = paddingStart; i < paddingStart + padding; i++) { for(int i = paddingStart; i < paddingStart + padding; i++) {
@@ -96,7 +99,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
} }
offset = FRAME_HEADER_LENGTH; offset = FRAME_HEADER_LENGTH;
length = payload; length = payload;
frame++; frameNumber++;
return true; return true;
} }
} }

View File

@@ -103,7 +103,7 @@ DatabaseListener {
private Bytes calculateTag(Context ctx, byte[] secret) { private Bytes calculateTag(Context ctx, byte[] secret) {
ErasableKey tagKey = crypto.deriveTagKey(secret, true); ErasableKey tagKey = crypto.deriveTagKey(secret, true);
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
tagKey.erase(); tagKey.erase();
return new Bytes(tag); return new Bytes(tag);
} }

View File

@@ -29,12 +29,12 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
public ConnectionWriter createConnectionWriter(OutputStream out, public ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, byte[] secret, byte[] tag) { long capacity, byte[] secret, byte[] tag) {
// Decrypt the tag // Validate the tag
Cipher tagCipher = crypto.getTagCipher(); Cipher tagCipher = crypto.getTagCipher();
ErasableKey tagKey = crypto.deriveTagKey(secret, true); ErasableKey tagKey = crypto.deriveTagKey(secret, true);
boolean valid = TagEncoder.validateTag(tag, 0, tagCipher, tagKey); long segmentNumber = TagEncoder.decodeTag(tag, tagCipher, tagKey);
tagKey.erase(); tagKey.erase();
if(!valid) throw new IllegalArgumentException(); if(segmentNumber != 0) throw new IllegalArgumentException();
return createConnectionWriter(out, capacity, false, secret); return createConnectionWriter(out, capacity, false, secret);
} }

View File

@@ -0,0 +1,25 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
class Frame {
private final byte[] buf = new byte[MAX_FRAME_LENGTH];
private int length = -1;
public byte[] getBuffer() {
return buf;
}
public int getLength() {
if(length == -1) throw new IllegalStateException();
return length;
}
public void setLength(int length) {
if(length < 0 || length > buf.length)
throw new IllegalArgumentException();
this.length = length;
}
}

View File

@@ -2,11 +2,13 @@ package net.sf.briar.transport;
import java.io.IOException; import java.io.IOException;
import net.sf.briar.api.plugins.Segment;
interface IncomingEncryptionLayer { interface IncomingEncryptionLayer {
/** /**
* Reads a frame into the given buffer and returns its length, or -1 if no * Reads a segment, excluding its tag, into the given buffer. Returns false
* more frames can be read. * if no more segments can be read from the connection.
*/ */
int readFrame(byte[] b) throws IOException; boolean readSegment(Segment s) throws IOException;
} }

View File

@@ -2,8 +2,9 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
@@ -15,6 +16,7 @@ import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.FormatException; import net.sf.briar.api.FormatException;
import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.plugins.Segment;
class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer { class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
@@ -22,10 +24,11 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
private final Cipher tagCipher, frameCipher; private final Cipher tagCipher, frameCipher;
private final ErasableKey tagKey, frameKey; private final ErasableKey tagKey, frameKey;
private final int blockSize; private final int blockSize;
private final byte[] iv; private final byte[] iv, ciphertext;
private final boolean tagEverySegment; private final boolean tagEverySegment;
private long frame = 0L; private boolean firstSegment = true;
private long segmentNumber = 0L;
IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher, IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey, Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
@@ -40,71 +43,73 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
if(blockSize < FRAME_HEADER_LENGTH) if(blockSize < FRAME_HEADER_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
iv = IvEncoder.encodeIv(0, blockSize); iv = IvEncoder.encodeIv(0, blockSize);
ciphertext = new byte[MAX_SEGMENT_LENGTH];
} }
public int readFrame(byte[] b) throws IOException { public boolean readSegment(Segment s) throws IOException {
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); boolean tag = tagEverySegment && !firstSegment;
boolean tag = tagEverySegment && frame > 0;
// Clear the buffer before exposing it to the transport plugin
for(int i = 0; i < b.length; i++) b[i] = 0;
try { try {
// If a tag is expected then read, decrypt and validate it // If a tag is expected then read, decrypt and validate it
if(tag) { if(tag) {
int offset = 0; int offset = 0;
while(offset < TAG_LENGTH) { while(offset < TAG_LENGTH) {
int read = in.read(b, offset, TAG_LENGTH - offset); int read = in.read(ciphertext, offset, TAG_LENGTH - offset);
if(read == -1) { if(read == -1) {
if(offset == 0) return -1; if(offset == 0) return false;
throw new EOFException(); throw new EOFException();
} }
offset += read; offset += read;
} }
if(!TagEncoder.validateTag(b, frame, tagCipher, tagKey)) long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
throw new FormatException(); if(seg == -1) throw new FormatException();
segmentNumber = seg;
} }
// Read the first block // Read the first block of the frame/segment
int offset = 0; int offset = 0;
while(offset < blockSize) { while(offset < blockSize) {
int read = in.read(b, offset, blockSize - offset); int read = in.read(ciphertext, offset, blockSize - offset);
if(read == -1) { if(read == -1) {
if(offset == 0 && !tag) return -1; if(offset == 0 && !tag && !firstSegment) return false;
throw new EOFException(); throw new EOFException();
} }
offset += read; offset += read;
} }
// Decrypt the first block // Decrypt the first block of the frame/segment
byte[] plaintext = s.getBuffer();
try { try {
IvEncoder.updateIv(iv, frame); IvEncoder.updateIv(iv, segmentNumber);
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
int decrypted = frameCipher.update(b, 0, blockSize, b); int decrypted = frameCipher.update(ciphertext, 0, blockSize,
plaintext);
if(decrypted != blockSize) throw new RuntimeException(); if(decrypted != blockSize) throw new RuntimeException();
} catch(GeneralSecurityException badCipher) { } catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} }
// Validate and parse the header // Parse the frame header
if(!HeaderEncoder.validateHeader(b, frame)) int payload = HeaderEncoder.getPayloadLength(plaintext);
throw new FormatException(); int padding = HeaderEncoder.getPaddingLength(plaintext);
int payload = HeaderEncoder.getPayloadLength(b);
int padding = HeaderEncoder.getPaddingLength(b);
int length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH; int length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
// Read the remainder of the frame if(length > MAX_FRAME_LENGTH) throw new FormatException();
// Read the remainder of the frame/segment
while(offset < length) { while(offset < length) {
int read = in.read(b, offset, length - offset); int read = in.read(ciphertext, offset, length - offset);
if(read == -1) throw new EOFException(); if(read == -1) throw new EOFException();
offset += read; offset += read;
} }
// Decrypt the remainder of the frame // Decrypt the remainder of the frame/segment
try { try {
int decrypted = frameCipher.doFinal(b, blockSize, int decrypted = frameCipher.doFinal(ciphertext, blockSize,
length - blockSize, b, blockSize); length - blockSize, plaintext, blockSize);
if(decrypted != length - blockSize) if(decrypted != length - blockSize)
throw new RuntimeException(); throw new RuntimeException();
} catch(GeneralSecurityException badCipher) { } catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} }
frame++; s.setLength(length);
return length; s.setSegmentNumber(segmentNumber++);
firstSegment = false;
return true;
} catch(IOException e) { } catch(IOException e) {
frameKey.erase(); frameKey.erase();
tagKey.erase(); tagKey.erase();

View File

@@ -0,0 +1,14 @@
package net.sf.briar.transport;
import java.io.IOException;
import java.util.Collection;
interface IncomingErrorCorrectionLayer {
/**
* Reads a frame into the given buffer. The frame number must be contained
* in the given window. Returns false if no more frames can be read from
* the connection.
*/
boolean readFrame(Frame f, Collection<Long> window) throws IOException;
}

View File

@@ -4,7 +4,6 @@ import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@@ -27,7 +26,8 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
private final Segment segment; private final Segment segment;
private final boolean tagEverySegment; private final boolean tagEverySegment;
private long frame = 0L; private boolean firstSegment = true;
private long segmentNumber = 0L;
IncomingSegmentedEncryptionLayer(SegmentSource in, Cipher tagCipher, IncomingSegmentedEncryptionLayer(SegmentSource in, Cipher tagCipher,
Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey, Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
@@ -45,41 +45,37 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
segment = new SegmentImpl(); segment = new SegmentImpl();
} }
public int readFrame(byte[] b) throws IOException { public boolean readSegment(Segment s) throws IOException {
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); boolean tag = tagEverySegment && !firstSegment;
boolean tag = tagEverySegment && frame > 0;
// Clear the buffer before exposing it to the transport plugin
segment.clear();
try { try {
// Read the segment // Read the segment
if(!in.readSegment(segment)) return -1; if(!in.readSegment(segment)) return false;
int offset = tag ? TAG_LENGTH : 0, length = segment.getLength(); int offset = tag ? TAG_LENGTH : 0, length = segment.getLength();
if(length > MAX_SEGMENT_LENGTH) throw new FormatException(); if(length > MAX_SEGMENT_LENGTH) throw new FormatException();
if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH) if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new FormatException(); throw new FormatException();
// If a tag is expected, decrypt and validate it byte[] ciphertext = segment.getBuffer();
if(tag && !TagEncoder.validateTag(segment.getBuffer(), frame, // If a tag is expected then decrypt and validate it
tagCipher, tagKey)) throw new FormatException(); if(tag) {
// Decrypt the frame long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
if(seg == -1) throw new FormatException();
segmentNumber = seg;
}
// Decrypt the segment
try { try {
IvEncoder.updateIv(iv, frame); IvEncoder.updateIv(iv, segmentNumber);
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
int decrypted = frameCipher.doFinal(segment.getBuffer(), offset, int decrypted = frameCipher.doFinal(ciphertext, offset,
length - offset, b); length - offset, s.getBuffer());
if(decrypted != length - offset) throw new RuntimeException(); if(decrypted != length - offset) throw new RuntimeException();
} catch(GeneralSecurityException badCipher) { } catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} }
// Validate and parse the header s.setLength(length - offset);
if(!HeaderEncoder.validateHeader(b, frame)) s.setSegmentNumber(segmentNumber++);
throw new FormatException(); firstSegment = false;
int payload = HeaderEncoder.getPayloadLength(b); return true;
int padding = HeaderEncoder.getPaddingLength(b);
if(length != offset + FRAME_HEADER_LENGTH + payload + padding
+ MAC_LENGTH) throw new FormatException();
frame++;
return length - offset;
} catch(IOException e) { } catch(IOException e) {
frameKey.erase(); frameKey.erase();
tagKey.erase(); tagKey.erase();

View File

@@ -0,0 +1,31 @@
package net.sf.briar.transport;
import java.io.IOException;
import java.util.Collection;
import net.sf.briar.api.plugins.Segment;
class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
private final IncomingEncryptionLayer in;
private final Segment segment;
NullIncomingErrorCorrectionLayer(IncomingEncryptionLayer in) {
this.in = in;
segment = new SegmentImpl();
}
public boolean readFrame(Frame f, Collection<Long> window)
throws IOException {
while(true) {
if(!in.readSegment(segment)) return false;
byte[] buf = segment.getBuffer();
if(window.contains(HeaderEncoder.getFrameNumber(buf))) break;
}
int length = segment.getLength();
// FIXME: Unnecessary copy
System.arraycopy(segment.getBuffer(), 0, f.getBuffer(), 0, length);
f.setLength(length);
return true;
}
}

View File

@@ -11,12 +11,6 @@ class SegmentImpl implements Segment {
private int length = -1; private int length = -1;
private long segmentNumber = -1; private long segmentNumber = -1;
public void clear() {
for(int i = 0; i < buf.length; i++) buf[i] = 0;
length = -1;
segmentNumber = -1;
}
public byte[] getBuffer() { public byte[] getBuffer() {
return buf; return buf;
} }

View File

@@ -6,28 +6,22 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.util.ByteUtils; import net.sf.briar.util.ByteUtils;
class TagEncoder { class TagEncoder {
private static final byte[] BLANK = new byte[TAG_LENGTH]; static void encodeTag(byte[] tag, long segmentNumber, Cipher tagCipher,
static void encodeTag(byte[] tag, long frame, Cipher tagCipher,
ErasableKey tagKey) { ErasableKey tagKey) {
if(tag.length < TAG_LENGTH) throw new IllegalArgumentException(); if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if(frame < 0 || frame > MAX_32_BIT_UNSIGNED) if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// Encode the frame number as a uint32 at the end of the IV // Encode the segment number as a uint32 at the end of the tag
byte[] iv = new byte[tagCipher.getBlockSize()]; ByteUtils.writeUint32(segmentNumber, tag, TAG_LENGTH - 4);
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
ByteUtils.writeUint32(frame, iv, iv.length - 4);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
try { try {
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey, ivSpec); tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
int encrypted = tagCipher.doFinal(BLANK, 0, TAG_LENGTH, tag); int encrypted = tagCipher.doFinal(tag, 0, TAG_LENGTH, tag);
if(encrypted != TAG_LENGTH) throw new IllegalArgumentException(); if(encrypted != TAG_LENGTH) throw new IllegalArgumentException();
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
// Unsuitable cipher or key // Unsuitable cipher or key
@@ -35,26 +29,18 @@ class TagEncoder {
} }
} }
static boolean validateTag(byte[] tag, long frame, Cipher tagCipher, static long decodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
ErasableKey tagKey) { if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if(frame < 0 || frame > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
if(tag.length < TAG_LENGTH) return false;
// Encode the frame number as a uint32 at the end of the IV
byte[] iv = new byte[tagCipher.getBlockSize()];
if(iv.length != TAG_LENGTH) throw new IllegalArgumentException();
ByteUtils.writeUint32(frame, iv, iv.length - 4);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
try { try {
tagCipher.init(Cipher.DECRYPT_MODE, tagKey, ivSpec); tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
byte[] plaintext = tagCipher.doFinal(tag, 0, TAG_LENGTH); byte[] plaintext = tagCipher.doFinal(tag, 0, TAG_LENGTH);
if(plaintext.length != TAG_LENGTH) if(plaintext.length != TAG_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// The plaintext should be blank // All but the last four bytes of the plaintext should be blank
for(int i = 0; i < plaintext.length; i++) { for(int i = 0; i < TAG_LENGTH - 4; i++) {
if(plaintext[i] != 0) return false; if(plaintext[i] != 0) return -1;
} }
return true; return ByteUtils.readUint32(plaintext, TAG_LENGTH - 4);
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
// Unsuitable cipher or key // Unsuitable cipher or key
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);

View File

@@ -33,7 +33,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the frame // Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame); ByteArrayInputStream in = new ByteArrayInputStream(frame);
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
// There should be no bytes available before EOF // There should be no bytes available before EOF
assertEquals(-1, r.getInputStream().read()); assertEquals(-1, r.getInputStream().read());
} }
@@ -51,7 +53,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the frame // Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame); ByteArrayInputStream in = new ByteArrayInputStream(frame);
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
// There should be one byte available before EOF // There should be one byte available before EOF
assertEquals(0, r.getInputStream().read()); assertEquals(0, r.getInputStream().read());
assertEquals(-1, r.getInputStream().read()); assertEquals(-1, r.getInputStream().read());
@@ -77,7 +81,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the first frame // Read the first frame
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
byte[] read = new byte[MAX_PAYLOAD_LENGTH]; byte[] read = new byte[MAX_PAYLOAD_LENGTH];
TestUtils.readFully(r.getInputStream(), read); TestUtils.readFully(r.getInputStream(), read);
// Try to read the second frame // Try to read the second frame
@@ -111,7 +117,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the first frame // Read the first frame
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
byte[] read = new byte[MAX_PAYLOAD_LENGTH - paddingLength]; byte[] read = new byte[MAX_PAYLOAD_LENGTH - paddingLength];
TestUtils.readFully(r.getInputStream(), read); TestUtils.readFully(r.getInputStream(), read);
// Try to read the second frame // Try to read the second frame
@@ -137,7 +145,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the frame // Read the frame
ByteArrayInputStream in = new ByteArrayInputStream(frame); ByteArrayInputStream in = new ByteArrayInputStream(frame);
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
// The non-zero padding should be rejected // The non-zero padding should be rejected
try { try {
r.getInputStream().read(); r.getInputStream().read();
@@ -169,7 +179,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Read the frames // Read the frames
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
byte[] read = new byte[payloadLength]; byte[] read = new byte[payloadLength];
TestUtils.readFully(r.getInputStream(), read); TestUtils.readFully(r.getInputStream(), read);
assertArrayEquals(new byte[payloadLength], read); assertArrayEquals(new byte[payloadLength], read);
@@ -193,7 +205,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Try to read the frame - not a single byte should be read // Try to read the frame - not a single byte should be read
ByteArrayInputStream in = new ByteArrayInputStream(frame); ByteArrayInputStream in = new ByteArrayInputStream(frame);
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
try { try {
r.getInputStream().read(); r.getInputStream().read();
fail(); fail();
@@ -215,7 +229,9 @@ public class ConnectionReaderImplTest extends TransportTest {
// Try to read the frame - not a single byte should be read // Try to read the frame - not a single byte should be read
ByteArrayInputStream in = new ByteArrayInputStream(frame); ByteArrayInputStream in = new ByteArrayInputStream(frame);
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in); IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
ConnectionReader r = new ConnectionReaderImpl(decrypter, mac, macKey); IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader r = new ConnectionReaderImpl(correcter, mac, macKey);
try { try {
r.getInputStream().read(); r.getInputStream().read();
fail(); fail();

View File

@@ -618,7 +618,7 @@ public class ConnectionRecogniserImplTest extends BriarTestCase {
ErasableKey tagKey = crypto.deriveTagKey(secret, true); ErasableKey tagKey = crypto.deriveTagKey(secret, true);
Cipher tagCipher = crypto.getTagCipher(); Cipher tagCipher = crypto.getTagCipher();
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
return tag; return tag;
} }
} }

View File

@@ -62,7 +62,7 @@ public class FrameReadWriteTest extends BriarTestCase {
private void testWriteAndRead(boolean initiator) throws Exception { private void testWriteAndRead(boolean initiator) throws Exception {
// Encode the tag // Encode the tag
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Generate two random frames // Generate two random frames
byte[] frame = new byte[12345]; byte[] frame = new byte[12345];
random.nextBytes(frame); random.nextBytes(frame);
@@ -89,11 +89,13 @@ public class FrameReadWriteTest extends BriarTestCase {
byte[] recoveredTag = new byte[TAG_LENGTH]; byte[] recoveredTag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(recoveredTag)); assertEquals(TAG_LENGTH, in.read(recoveredTag));
assertArrayEquals(tag, recoveredTag); assertArrayEquals(tag, recoveredTag);
assertTrue(TagEncoder.validateTag(tag, 0, tagCipher, tagKey)); assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
// Read the frames back // Read the frames back
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in, IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, frameCipher, tagKey, frameKey, false); tagCipher, frameCipher, tagKey, frameKey, false);
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac, IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
ConnectionReader reader = new ConnectionReaderImpl(correcter, mac,
macKey); macKey);
InputStream in1 = reader.getInputStream(); InputStream in1 = reader.getInputStream();
byte[] recovered = new byte[frame.length]; byte[] recovered = new byte[frame.length];

View File

@@ -2,7 +2,6 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -13,6 +12,7 @@ import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.plugins.Segment;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -38,14 +38,14 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
@Test @Test
public void testDecryptionWithFirstSegmentTagged() throws Exception { public void testDecryptionWithFirstSegmentTagged() throws Exception {
// Calculate the ciphertext for the first frame // Calculate the ciphertext for the first segment
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH]; byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0); HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize()); byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length); byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
// Calculate the ciphertext for the second frame // Calculate the ciphertext for the second segment
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
@@ -62,13 +62,19 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in, IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, frameCipher, tagKey, frameKey, false); tagCipher, frameCipher, tagKey, frameKey, false);
// First frame // First frame
byte[] decrypted = new byte[MAX_FRAME_LENGTH]; Segment s = new SegmentImpl();
assertEquals(plaintext.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) { for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]); assertEquals(plaintext[i], decrypted[i]);
} }
// Second frame // Second frame
assertEquals(plaintext1.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) { for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]); assertEquals(plaintext1[i], decrypted[i]);
} }
@@ -76,18 +82,18 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
@Test @Test
public void testDecryptionWithEverySegmentTagged() throws Exception { public void testDecryptionWithEverySegmentTagged() throws Exception {
// Calculate the ciphertext for the first frame // Calculate the ciphertext for the first segment
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH]; byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0); HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize()); byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length); byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
// Calculate the ciphertext for the second frame, including its tag // Calculate the ciphertext for the second segment, including its tag
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length]; byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length];
TagEncoder.encodeTag(ciphertext1, 1, tagCipher, tagKey); TagEncoder.encodeTag(ciphertext1, 1L, tagCipher, tagKey);
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
@@ -102,13 +108,19 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in, IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, frameCipher, tagKey, frameKey, true); tagCipher, frameCipher, tagKey, frameKey, true);
// First frame // First frame
byte[] decrypted = new byte[MAX_FRAME_LENGTH]; Segment s = new SegmentImpl();
assertEquals(plaintext.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) { for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]); assertEquals(plaintext[i], decrypted[i]);
} }
// Second frame // Second frame
assertEquals(plaintext1.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) { for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]); assertEquals(plaintext1[i], decrypted[i]);
} }

View File

@@ -2,7 +2,6 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.IOException; import java.io.IOException;
@@ -39,14 +38,14 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
@Test @Test
public void testDecryptionWithFirstSegmentTagged() throws Exception { public void testDecryptionWithFirstSegmentTagged() throws Exception {
// Calculate the ciphertext for the first frame // Calculate the ciphertext for the first segment
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH]; byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0); HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize()); byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length); byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
// Calculate the ciphertext for the second frame // Calculate the ciphertext for the second segment
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
@@ -61,13 +60,19 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher, new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher,
tagKey, frameKey, false); tagKey, frameKey, false);
// First frame // First frame
byte[] decrypted = new byte[MAX_FRAME_LENGTH]; Segment s = new SegmentImpl();
assertEquals(plaintext.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) { for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]); assertEquals(plaintext[i], decrypted[i]);
} }
// Second frame // Second frame
assertEquals(plaintext1.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) { for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]); assertEquals(plaintext1[i], decrypted[i]);
} }
@@ -86,7 +91,7 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length]; byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length];
TagEncoder.encodeTag(ciphertext1, 1, tagCipher, tagKey); TagEncoder.encodeTag(ciphertext1, 1L, tagCipher, tagKey);
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
@@ -99,13 +104,19 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher, new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher,
tagKey, frameKey, true); tagKey, frameKey, true);
// First frame // First frame
byte[] decrypted = new byte[MAX_FRAME_LENGTH]; Segment s = new SegmentImpl();
assertEquals(plaintext.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) { for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]); assertEquals(plaintext[i], decrypted[i]);
} }
// Second frame // Second frame
assertEquals(plaintext1.length, decrypter.readFrame(decrypted)); assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) { for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]); assertEquals(plaintext1[i], decrypted[i]);
} }

View File

@@ -9,38 +9,44 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import net.sf.briar.api.FormatException; import net.sf.briar.api.FormatException;
import net.sf.briar.api.plugins.Segment;
/** An encryption layer that performs no encryption. */ /** An encryption layer that performs no encryption. */
class NullIncomingEncryptionLayer implements IncomingEncryptionLayer { class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
private final InputStream in; private final InputStream in;
private long segmentNumber = 0L;
NullIncomingEncryptionLayer(InputStream in) { NullIncomingEncryptionLayer(InputStream in) {
this.in = in; this.in = in;
} }
public int readFrame(byte[] b) throws IOException { public boolean readSegment(Segment s) throws IOException {
// Read the header to determine the frame length byte[] buf = s.getBuffer();
// Read the frame header
int offset = 0, length = FRAME_HEADER_LENGTH; int offset = 0, length = FRAME_HEADER_LENGTH;
while(offset < length) { while(offset < length) {
int read = in.read(b, offset, length - offset); int read = in.read(buf, offset, length - offset);
if(read == -1) { if(read == -1) {
if(offset == 0) return -1; if(offset == 0) return false;
throw new EOFException(); throw new EOFException();
} }
offset += read; offset += read;
} }
// Parse the header // Parse the frame header
int payload = HeaderEncoder.getPayloadLength(b); int payload = HeaderEncoder.getPayloadLength(buf);
int padding = HeaderEncoder.getPaddingLength(b); int padding = HeaderEncoder.getPaddingLength(buf);
length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH; length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
if(length > MAX_FRAME_LENGTH) throw new FormatException(); if(length > MAX_FRAME_LENGTH) throw new FormatException();
// Read the remainder of the frame // Read the remainder of the frame/segment
while(offset < length) { while(offset < length) {
int read = in.read(b, offset, length - offset); int read = in.read(buf, offset, length - offset);
if(read == -1) throw new EOFException(); if(read == -1) throw new EOFException();
offset += read; offset += read;
} }
return length; s.setLength(length);
s.setSegmentNumber(segmentNumber++);
return true;
} }
} }

View File

@@ -39,14 +39,14 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
public void testEncryptionWithFirstSegmentTagged() throws Exception { public void testEncryptionWithFirstSegmentTagged() throws Exception {
// Calculate the expected tag // Calculate the expected tag
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame // Calculate the expected ciphertext for the first segment
byte[] iv = new byte[frameCipher.getBlockSize()]; byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH]; byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext); byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the expected ciphertext for the second frame // Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);
@@ -76,17 +76,17 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
public void testEncryptionWithEverySegmentTagged() throws Exception { public void testEncryptionWithEverySegmentTagged() throws Exception {
// Calculate the expected tag for the first segment // Calculate the expected tag for the first segment
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame // Calculate the expected ciphertext for the first segment
byte[] iv = new byte[frameCipher.getBlockSize()]; byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH]; byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext); byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the expected tag for the second frame // Calculate the expected tag for the second segment
byte[] tag1 = new byte[TAG_LENGTH]; byte[] tag1 = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag1, 1, tagCipher, tagKey); TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
// Calculate the expected ciphertext for the second frame // Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);

View File

@@ -42,14 +42,14 @@ public class OutgoingSegmentedEncryptionLayerTest extends BriarTestCase {
public void testEncryptionWithFirstSegmentTagged() throws Exception { public void testEncryptionWithFirstSegmentTagged() throws Exception {
// Calculate the expected tag // Calculate the expected tag
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame // Calculate the expected ciphertext for the first segment
byte[] iv = new byte[frameCipher.getBlockSize()]; byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH]; byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext); byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the expected ciphertext for the second frame // Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);
@@ -78,19 +78,19 @@ public class OutgoingSegmentedEncryptionLayerTest extends BriarTestCase {
@Test @Test
public void testEncryptionWithEverySegmentTagged() throws Exception { public void testEncryptionWithEverySegmentTagged() throws Exception {
// Calculate the expected tag for the first frame // Calculate the expected tag for the first segment
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0, tagCipher, tagKey); TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first frame // Calculate the expected ciphertext for the first segment
byte[] iv = new byte[frameCipher.getBlockSize()]; byte[] iv = new byte[frameCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH]; byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext = frameCipher.doFinal(plaintext); byte[] ciphertext = frameCipher.doFinal(plaintext);
// Calculate the expected tag for the second frame // Calculate the expected tag for the second segment
byte[] tag1 = new byte[TAG_LENGTH]; byte[] tag1 = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag1, 1, tagCipher, tagKey); TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
// Calculate the expected ciphertext for the second frame // Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH]; byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L); IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv); ivSpec = new IvParameterSpec(iv);

View File

@@ -16,8 +16,8 @@ import com.google.inject.Injector;
public abstract class TransportTest extends BriarTestCase { public abstract class TransportTest extends BriarTestCase {
static final int MAX_PAYLOAD_LENGTH static final int MAX_PAYLOAD_LENGTH =
= MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - MAC_LENGTH; MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - MAC_LENGTH;
protected final Mac mac; protected final Mac mac;
protected final ErasableKey macKey; protected final ErasableKey macKey;