From 3a77ba9aaf84de2ee651601a387a1127428545e4 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Fri, 20 Jan 2012 23:06:51 +0000
Subject: [PATCH] Allow the transport to specify the maximum segment length.

---
 api/net/sf/briar/api/plugins/SegmentSink.java |  5 ++++
 .../sf/briar/api/plugins/SegmentSource.java   |  5 ++++
 .../briar/transport/ConnectionReaderImpl.java |  2 +-
 .../briar/transport/ConnectionWriterImpl.java | 13 +++++-----
 components/net/sf/briar/transport/Frame.java  |  6 +++++
 .../IncomingAuthenticationLayer.java          |  3 +++
 .../IncomingAuthenticationLayerImpl.java      | 10 +++++---
 .../transport/IncomingEncryptionLayer.java    |  5 ++++
 .../IncomingEncryptionLayerImpl.java          |  6 ++++-
 .../IncomingErrorCorrectionLayer.java         |  3 +++
 .../IncomingErrorCorrectionLayerImpl.java     | 12 +++++++--
 .../transport/IncomingReliabilityLayer.java   |  3 +++
 .../IncomingSegmentedEncryptionLayer.java     | 18 ++++++++++---
 .../NullIncomingErrorCorrectionLayer.java     |  8 +++++-
 .../NullIncomingReliabilityLayer.java         |  6 +++++
 .../NullOutgoingErrorCorrectionLayer.java     |  8 +++++-
 .../NullOutgoingReliabilityLayer.java         |  6 +++++
 .../OutgoingAuthenticationLayer.java          |  3 +++
 .../OutgoingAuthenticationLayerImpl.java      |  7 +++++-
 .../transport/OutgoingEncryptionLayer.java    |  5 ++++
 .../OutgoingEncryptionLayerImpl.java          |  4 +++
 .../OutgoingErrorCorrectionLayer.java         |  3 +++
 .../OutgoingErrorCorrectionLayerImpl.java     | 12 +++++++--
 .../transport/OutgoingReliabilityLayer.java   |  3 +++
 .../OutgoingSegmentedEncryptionLayer.java     | 17 +++++++++++--
 .../net/sf/briar/transport/SegmentImpl.java   |  5 ++++
 .../IncomingErrorCorrectionLayerImplTest.java |  4 +++
 .../IncomingSegmentedEncryptionLayerTest.java |  5 ++++
 .../NullIncomingEncryptionLayer.java          |  6 +++++
 .../NullOutgoingEncryptionLayer.java          |  7 ++++++
 .../OutgoingSegmentedEncryptionLayerTest.java |  5 ++++
 .../briar/transport/XorErasureCodeTest.java   |  2 +-
 .../transport/XorErasureDecoderTest.java      |  2 +-
 .../transport/XorErasureEncoderTest.java      | 25 +++++++++----------
 34 files changed, 195 insertions(+), 39 deletions(-)

diff --git a/api/net/sf/briar/api/plugins/SegmentSink.java b/api/net/sf/briar/api/plugins/SegmentSink.java
index 55caa49340..83eaa2f6ae 100644
--- a/api/net/sf/briar/api/plugins/SegmentSink.java
+++ b/api/net/sf/briar/api/plugins/SegmentSink.java
@@ -8,4 +8,9 @@ public interface SegmentSink {
 
 	/** Writes the given segment. */
 	void writeSegment(Segment s) throws IOException;
+
+	/**
+	 * Returns the maximum length in bytes of the segments this sink accepts.
+	 */
+	int getMaxSegmentLength();
 }
diff --git a/api/net/sf/briar/api/plugins/SegmentSource.java b/api/net/sf/briar/api/plugins/SegmentSource.java
index 403659a0b1..135d03f548 100644
--- a/api/net/sf/briar/api/plugins/SegmentSource.java
+++ b/api/net/sf/briar/api/plugins/SegmentSource.java
@@ -11,4 +11,9 @@ public interface SegmentSource {
 	 * segment was read, or false if no more segments can be read.
 	 */
 	boolean readSegment(Segment s) throws IOException;
+
+	/**
+	 * Returns the maximum length in bytes of the segments this source returns.
+	 */
+	int getMaxSegmentLength();
 }
diff --git a/components/net/sf/briar/transport/ConnectionReaderImpl.java b/components/net/sf/briar/transport/ConnectionReaderImpl.java
index 581c026ce5..1859eda8dc 100644
--- a/components/net/sf/briar/transport/ConnectionReaderImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderImpl.java
@@ -19,7 +19,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
 	ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors) {
 		this.in = in;
 		this.tolerateErrors = tolerateErrors;
-		frame = new Frame();
+		frame = new Frame(in.getMaxFrameLength());
 	}
 
 	public InputStream getInputStream() {
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index d1fcbd7a0d..e39de66619 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -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.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 java.io.IOException;
@@ -19,6 +18,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
 class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
 	private final OutgoingReliabilityLayer out;
+	private final int maxFrameLength;
 	private final Frame frame;
 
 	private int offset = FRAME_HEADER_LENGTH;
@@ -26,7 +26,8 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
 	ConnectionWriterImpl(OutgoingReliabilityLayer out) {
 		this.out = out;
-		frame = new Frame();
+		maxFrameLength = out.getMaxFrameLength();
+		frame = new Frame(maxFrameLength);
 	}
 
 	public OutputStream getOutputStream() {
@@ -39,7 +40,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 		if(offset > FRAME_HEADER_LENGTH)
 			capacity -= offset + MAC_LENGTH;
 		// Subtract the overhead from the remaining capacity
-		long frames = (long) Math.ceil((double) capacity / MAX_FRAME_LENGTH);
+		long frames = (long) Math.ceil((double) capacity / maxFrameLength);
 		int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH;
 		return Math.max(0L, capacity - frames * overheadPerFrame);
 	}
@@ -53,7 +54,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	@Override
 	public void write(int b) throws IOException {
 		frame.getBuffer()[offset++] = (byte) b;
-		if(offset + MAC_LENGTH == MAX_FRAME_LENGTH) writeFrame();
+		if(offset + MAC_LENGTH == maxFrameLength) writeFrame();
 	}
 
 	@Override
@@ -64,14 +65,14 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	@Override
 	public void write(byte[] b, int off, int len) throws IOException {
 		byte[] buf = frame.getBuffer();
-		int available = MAX_FRAME_LENGTH - offset - MAC_LENGTH;
+		int available = maxFrameLength - offset - MAC_LENGTH;
 		while(available <= len) {
 			System.arraycopy(b, off, buf, offset, available);
 			offset += available;
 			writeFrame();
 			off += available;
 			len -= available;
-			available = MAX_FRAME_LENGTH - offset - MAC_LENGTH;
+			available = maxFrameLength - offset - MAC_LENGTH;
 		}
 		System.arraycopy(b, off, buf, offset, len);
 		offset += len;
diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java
index 64c8eeda9a..921e5ff23e 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -1,6 +1,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.MAC_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 
 class Frame {
 
@@ -13,6 +16,9 @@ class Frame {
 	}
 
 	Frame(int length) {
+		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
+			throw new IllegalArgumentException();
+		if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
 		buf = new byte[length];
 	}
 
diff --git a/components/net/sf/briar/transport/IncomingAuthenticationLayer.java b/components/net/sf/briar/transport/IncomingAuthenticationLayer.java
index 22dd0df365..33a6f20aa3 100644
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayer.java
+++ b/components/net/sf/briar/transport/IncomingAuthenticationLayer.java
@@ -15,4 +15,7 @@ interface IncomingAuthenticationLayer {
 	 */
 	boolean readFrame(Frame f, FrameWindow window) throws IOException,
 	InvalidDataException;
+
+	/** Returns the maximum length in bytes of the frames this layer returns. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
index c2332c7458..5489a8ba9c 100644
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
@@ -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.MAC_LENGTH;
-import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
 
 import java.io.IOException;
 import java.security.InvalidKeyException;
@@ -14,13 +13,13 @@ import net.sf.briar.api.crypto.ErasableKey;
 class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 
 	private final IncomingErrorCorrectionLayer in;
+	private final int maxFrameLength;
 	private final Mac mac;
 
 	IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac,
 			ErasableKey macKey) {
 		this.in = in;
 		this.mac = mac;
-		// Initialise the MAC
 		try {
 			mac.init(macKey);
 		} catch(InvalidKeyException e) {
@@ -29,6 +28,7 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 		macKey.erase();
 		if(mac.getMacLength() != MAC_LENGTH)
 			throw new IllegalArgumentException();
+		maxFrameLength = in.getMaxFrameLength();
 	}
 
 	public boolean readFrame(Frame f, FrameWindow window) throws IOException,
@@ -39,7 +39,7 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 		int length = f.getLength();
 		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
 			throw new InvalidDataException();
-		if(length > MAX_FRAME_LENGTH) throw new InvalidDataException();
+		if(length > maxFrameLength) throw new InvalidDataException();
 		// Check that the payload and padding lengths are correct
 		byte[] buf = f.getBuffer();
 		int payload = HeaderEncoder.getPayloadLength(buf);
@@ -61,4 +61,8 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 		}
 		return true;
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayer.java b/components/net/sf/briar/transport/IncomingEncryptionLayer.java
index 3521ce46e6..08e903a28b 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayer.java
@@ -15,4 +15,9 @@ interface IncomingEncryptionLayer {
 	 * may choose whether to retry the read or close the connection.
 	 */
 	boolean readSegment(Segment s) throws IOException, InvalidDataException;
+
+	/**
+	 * Returns the maximum length in bytes of the segments this layer returns.
+	 */
+	int getMaxSegmentLength();
 }
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
index 2f1bfb44e9..b9c702b278 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
@@ -23,9 +23,9 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 	private final InputStream in;
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
+	private final boolean tagEverySegment;
 	private final int blockSize;
 	private final byte[] iv, ciphertext;
-	private final boolean tagEverySegment;
 
 	private byte[] bufferedTag;
 	private boolean firstSegment = true;
@@ -128,4 +128,8 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 			throw e;
 		}
 	}
+
+	public int getMaxSegmentLength() {
+		return MAX_SEGMENT_LENGTH - TAG_LENGTH;
+	}
 }
\ No newline at end of file
diff --git a/components/net/sf/briar/transport/IncomingErrorCorrectionLayer.java b/components/net/sf/briar/transport/IncomingErrorCorrectionLayer.java
index e49e0cbb9f..f909dcd606 100644
--- a/components/net/sf/briar/transport/IncomingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/IncomingErrorCorrectionLayer.java
@@ -15,4 +15,7 @@ interface IncomingErrorCorrectionLayer {
 	 */
 	boolean readFrame(Frame f, FrameWindow window) throws IOException,
 	InvalidDataException;
+
+	/** Returns the maximum length in bytes of the frames this layer returns. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java b/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
index e20ac44d79..ff1d2f8952 100644
--- a/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
@@ -1,5 +1,7 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -14,7 +16,7 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 
 	private final IncomingEncryptionLayer in;
 	private final ErasureDecoder decoder;
-	private final int n, k;
+	private final int n, k, maxSegmentLength, maxFrameLength;
 	private final Map<Long, Integer> discardCounts;
 	private final Map<Long, Segment[]> segmentSets;
 	private final ArrayList<Segment> freeSegments;
@@ -25,6 +27,8 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 		this.decoder = decoder;
 		this.n = n;
 		this.k = k;
+		maxSegmentLength = in.getMaxSegmentLength();
+		maxFrameLength = Math.min(MAX_FRAME_LENGTH, maxSegmentLength * k);
 		discardCounts = new HashMap<Long, Integer>();
 		segmentSets = new HashMap<Long, Segment[]>();
 		freeSegments = new ArrayList<Segment>();
@@ -47,7 +51,7 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 		// Grab a free segment, or allocate one if necessary
 		Segment s;
 		int free = freeSegments.size();
-		if(free == 0) s = new SegmentImpl();
+		if(free == 0) s = new SegmentImpl(maxSegmentLength);
 		else s = freeSegments.remove(free - 1);
 		// Read segments until a frame can be decoded
 		while(true) {
@@ -74,6 +78,10 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 		}
 	}
 
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
+
 	private void countDiscard(long frameNumber) throws FormatException {
 		Integer count = discardCounts.get(frameNumber);
 		if(count == null) discardCounts.put(frameNumber, 1);
diff --git a/components/net/sf/briar/transport/IncomingReliabilityLayer.java b/components/net/sf/briar/transport/IncomingReliabilityLayer.java
index 417a2525f1..162a228c2d 100644
--- a/components/net/sf/briar/transport/IncomingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/IncomingReliabilityLayer.java
@@ -13,4 +13,7 @@ interface IncomingReliabilityLayer {
 	 * may choose whether to retry the read or close the connection.
 	 */
 	boolean readFrame(Frame f) throws IOException, InvalidDataException;
+
+	/** Returns the maximum length in bytes of the frames this layer returns. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java b/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
index f2a2ad40a3..50dacb4485 100644
--- a/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
+++ b/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
@@ -20,10 +20,10 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 	private final SegmentSource in;
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
-	private final int blockSize;
-	private final byte[] iv;
 	private final boolean tagEverySegment;
+	private final int blockSize, maxSegmentLength;
 	private final Segment segment;
+	private final byte[] iv;
 
 	private Segment bufferedSegment;
 	private boolean firstSegment = true;
@@ -42,8 +42,13 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 		blockSize = segCipher.getBlockSize();
 		if(blockSize < FRAME_HEADER_LENGTH)
 			throw new IllegalArgumentException();
+		int length = in.getMaxSegmentLength();
+		if(length < TAG_LENGTH + FRAME_HEADER_LENGTH + 1 + MAC_LENGTH)
+			throw new IllegalArgumentException();
+		if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
+		maxSegmentLength = length - TAG_LENGTH;
+		segment = new SegmentImpl(length);
 		iv = IvEncoder.encodeIv(0L, blockSize);
-		segment = new SegmentImpl();
 	}
 
 	public boolean readSegment(Segment s) throws IOException,
@@ -62,9 +67,10 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 			}
 			int offset = expectTag ? TAG_LENGTH : 0;
 			int length = segment.getLength();
-			if(length > MAX_SEGMENT_LENGTH) throw new InvalidDataException();
 			if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
 				throw new InvalidDataException();
+			if(length > offset + maxSegmentLength)
+				throw new InvalidDataException();
 			byte[] ciphertext = segment.getBuffer();
 			// If a tag is expected then decrypt and validate it
 			if(expectTag) {
@@ -92,4 +98,8 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 			throw e;
 		}
 	}
+
+	public int getMaxSegmentLength() {
+		return maxSegmentLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
index 54e1f04aec..1e5077d3c1 100644
--- a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
@@ -7,11 +7,13 @@ import net.sf.briar.api.transport.Segment;
 class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
 
 	private final IncomingEncryptionLayer in;
+	private final int maxFrameLength;
 	private final Segment segment;
 
 	NullIncomingErrorCorrectionLayer(IncomingEncryptionLayer in) {
 		this.in = in;
-		segment = new SegmentImpl();
+		maxFrameLength = in.getMaxSegmentLength();
+		segment = new SegmentImpl(maxFrameLength);
 	}
 
 	public boolean readFrame(Frame f, FrameWindow window) throws IOException,
@@ -28,4 +30,8 @@ class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
 		f.setLength(length);
 		return true;
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java b/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
index 25ddb59d24..ecd871d22f 100644
--- a/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
@@ -5,10 +5,12 @@ import java.io.IOException;
 class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
 
 	private final IncomingAuthenticationLayer in;
+	private final int maxFrameLength;
 	private final FrameWindow window;
 
 	NullIncomingReliabilityLayer(IncomingAuthenticationLayer in) {
 		this.in = in;
+		maxFrameLength = in.getMaxFrameLength();
 		window = new NullFrameWindow();
 	}
 
@@ -18,4 +20,8 @@ class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
 		if(!window.remove(frameNumber)) throw new IllegalStateException();
 		return true;
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
index c0719e2ead..6847697f9a 100644
--- a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
@@ -9,13 +9,15 @@ import net.sf.briar.api.transport.Segment;
 class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
 
 	private final OutgoingEncryptionLayer out;
+	private final int maxSegmentLength;
 	private final Segment segment;
 
 	private long segmentNumber = 0L;
 
 	public NullOutgoingErrorCorrectionLayer(OutgoingEncryptionLayer out) {
 		this.out = out;
-		segment = new SegmentImpl();
+		maxSegmentLength = out.getMaxSegmentLength();
+		segment = new SegmentImpl(maxSegmentLength);
 	}
 
 	public void writeFrame(Frame f) throws IOException {
@@ -36,4 +38,8 @@ class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
 	public long getRemainingCapacity() {
 		return out.getRemainingCapacity();
 	}
+
+	public int getMaxFrameLength() {
+		return maxSegmentLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/NullOutgoingReliabilityLayer.java b/components/net/sf/briar/transport/NullOutgoingReliabilityLayer.java
index a00a2e9c4e..650f245e54 100644
--- a/components/net/sf/briar/transport/NullOutgoingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/NullOutgoingReliabilityLayer.java
@@ -5,9 +5,11 @@ import java.io.IOException;
 class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
 
 	private final OutgoingAuthenticationLayer out;
+	private final int maxFrameLength;
 
 	NullOutgoingReliabilityLayer(OutgoingAuthenticationLayer out) {
 		this.out = out;
+		maxFrameLength = out.getMaxFrameLength();
 	}
 
 	public void writeFrame(Frame f) throws IOException {
@@ -21,4 +23,8 @@ class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
 	public long getRemainingCapacity() {
 		return out.getRemainingCapacity();
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/OutgoingAuthenticationLayer.java b/components/net/sf/briar/transport/OutgoingAuthenticationLayer.java
index e1b9b72b6d..21e41a9243 100644
--- a/components/net/sf/briar/transport/OutgoingAuthenticationLayer.java
+++ b/components/net/sf/briar/transport/OutgoingAuthenticationLayer.java
@@ -12,4 +12,7 @@ interface OutgoingAuthenticationLayer {
 
 	/** Returns the maximum number of bytes that can be written. */
 	long getRemainingCapacity();
+
+	/** Returns the maximum length in bytes of the frames this layer accepts. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java b/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
index c8cee560b4..535ec4834a 100644
--- a/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
@@ -14,12 +14,12 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
 
 	private final OutgoingErrorCorrectionLayer out;
 	private final Mac mac;
+	private final int maxFrameLength;
 
 	OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac,
 			ErasableKey macKey) {
 		this.out = out;
 		this.mac = mac;
-		// Initialise the MAC
 		try {
 			mac.init(macKey);
 		} catch(InvalidKeyException badKey) {
@@ -28,6 +28,7 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
 		macKey.erase();
 		if(mac.getMacLength() != MAC_LENGTH)
 			throw new IllegalArgumentException();
+		maxFrameLength = out.getMaxFrameLength();
 	}
 
 	public void writeFrame(Frame f) throws IOException {
@@ -49,4 +50,8 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
 	public long getRemainingCapacity() {
 		return out.getRemainingCapacity();
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
index e3b11cfe2e..e0e4b44023 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
@@ -14,4 +14,9 @@ interface OutgoingEncryptionLayer {
 
 	/** Returns the maximum number of bytes that can be written. */
 	long getRemainingCapacity();
+
+	/**
+	 * Returns the maximum length in bytes of the segments this layer accepts.
+	 */
+	int getMaxSegmentLength();
 }
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
index 4169eef5af..6e9696b206 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
@@ -73,4 +73,8 @@ class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
 	public long getRemainingCapacity() {
 		return capacity;
 	}
+
+	public int getMaxSegmentLength() {
+		return MAX_SEGMENT_LENGTH - TAG_LENGTH;
+	}
 }
\ No newline at end of file
diff --git a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayer.java b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayer.java
index 206a594df3..9dfd9eac37 100644
--- a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayer.java
@@ -12,4 +12,7 @@ interface OutgoingErrorCorrectionLayer {
 
 	/** Returns the maximum number of bytes that can be written. */
 	long getRemainingCapacity();
+
+	/** Returns the maximum length in bytes of the frames this layer accepts. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
index 24168939b6..e7662d002e 100644
--- a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
@@ -1,5 +1,7 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+
 import java.io.IOException;
 
 import net.sf.briar.api.transport.Segment;
@@ -8,13 +10,15 @@ class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
 
 	private final OutgoingEncryptionLayer out;
 	private final ErasureEncoder encoder;
-	private final int n;
+	private final int n, maxFrameLength;
 
 	OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out,
-			ErasureEncoder encoder, int n) {
+			ErasureEncoder encoder, int n, int k) {
 		this.out = out;
 		this.encoder = encoder;
 		this.n = n;
+		maxFrameLength = Math.min(MAX_FRAME_LENGTH,
+				out.getMaxSegmentLength() * k);
 	}
 
 	public void writeFrame(Frame f) throws IOException {
@@ -28,4 +32,8 @@ class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
 	public long getRemainingCapacity() {
 		return out.getRemainingCapacity() / n;
 	}
+
+	public int getMaxFrameLength() {
+		return maxFrameLength;
+	}
 }
diff --git a/components/net/sf/briar/transport/OutgoingReliabilityLayer.java b/components/net/sf/briar/transport/OutgoingReliabilityLayer.java
index 49aacb499a..ef6797d9a8 100644
--- a/components/net/sf/briar/transport/OutgoingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/OutgoingReliabilityLayer.java
@@ -12,4 +12,7 @@ interface OutgoingReliabilityLayer {
 
 	/** Returns the maximum number of bytes that can be written. */
 	long getRemainingCapacity();
+
+	/** Returns the maximum length in bytes of the frames this layer accepts. */
+	int getMaxFrameLength();
 }
diff --git a/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
index 1214dec4a8..eb188ff608 100644
--- a/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
@@ -1,5 +1,8 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 
 import java.io.IOException;
@@ -18,8 +21,9 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
 	private final boolean tagEverySegment;
-	private final byte[] iv;
+	private final int maxSegmentLength;
 	private final Segment segment;
+	private final byte[] iv;
 
 	private long capacity;
 
@@ -33,8 +37,13 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
 		this.tagKey = tagKey;
 		this.segKey = segKey;
 		this.tagEverySegment = tagEverySegment;
+		int length = out.getMaxSegmentLength();
+		if(length < TAG_LENGTH + FRAME_HEADER_LENGTH + 1 + MAC_LENGTH)
+			throw new RuntimeException();
+		if(length > MAX_SEGMENT_LENGTH) throw new RuntimeException();
+		maxSegmentLength = length - MAC_LENGTH;
+		segment = new SegmentImpl(length);
 		iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
-		segment = new SegmentImpl();
 	}
 
 	public void writeSegment(Segment s) throws IOException {
@@ -72,4 +81,8 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
 	public long getRemainingCapacity() {
 		return capacity;
 	}
+
+	public int getMaxSegmentLength() {
+		return maxSegmentLength;
+	}
 }
\ No newline at end of file
diff --git a/components/net/sf/briar/transport/SegmentImpl.java b/components/net/sf/briar/transport/SegmentImpl.java
index 9915854573..3c4552cf61 100644
--- a/components/net/sf/briar/transport/SegmentImpl.java
+++ b/components/net/sf/briar/transport/SegmentImpl.java
@@ -1,5 +1,7 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 import net.sf.briar.api.transport.Segment;
@@ -16,6 +18,9 @@ class SegmentImpl implements Segment {
 	}
 
 	SegmentImpl(int length) {
+		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
+			throw new IllegalArgumentException();
+		if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
 		buf = new byte[length];
 	}
 
diff --git a/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java b/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java
index 9072b728b0..f6f507bb55 100644
--- a/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java
@@ -160,5 +160,9 @@ public class IncomingErrorCorrectionLayerImplTest extends BriarTestCase {
 			s.setLength(length);
 			return true;
 		}
+
+		public int getMaxSegmentLength() {
+			return length;
+		}
 	}
 }
diff --git a/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java b/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
index 7359cf515e..13243605ed 100644
--- a/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
@@ -2,6 +2,7 @@ package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 
 import java.io.IOException;
@@ -149,5 +150,9 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 			s.setLength(segment.length);
 			return true;
 		}
+
+		public int getMaxSegmentLength() {
+			return MAX_SEGMENT_LENGTH;
+		}
 	}
 }
diff --git a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
index def5509b83..dd244eeaff 100644
--- a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
@@ -3,6 +3,8 @@ package net.sf.briar.transport;
 import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -49,4 +51,8 @@ class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
 		s.setSegmentNumber(segmentNumber++);
 		return true;
 	}
+
+	public int getMaxSegmentLength() {
+		return MAX_SEGMENT_LENGTH - TAG_LENGTH;
+	}
 }
diff --git a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
index 574b404c1b..88921aff03 100644
--- a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
@@ -1,5 +1,8 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
+
 import java.io.IOException;
 import java.io.OutputStream;
 
@@ -34,4 +37,8 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
 	public long getRemainingCapacity() {
 		return capacity;
 	}
+
+	public int getMaxSegmentLength() {
+		return MAX_SEGMENT_LENGTH - TAG_LENGTH;
+	}
 }
diff --git a/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java b/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
index 398ca9cc5c..00d467e92c 100644
--- a/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 import static org.junit.Assert.assertArrayEquals;
 
@@ -136,5 +137,9 @@ public class OutgoingSegmentedEncryptionLayerTest extends BriarTestCase {
 		public void writeSegment(Segment s) throws IOException {
 			write(s.getBuffer(), 0, s.getLength());
 		}
+
+		public int getMaxSegmentLength() {
+			return MAX_SEGMENT_LENGTH;
+		}
 	}
 }
diff --git a/test/net/sf/briar/transport/XorErasureCodeTest.java b/test/net/sf/briar/transport/XorErasureCodeTest.java
index b361cc2d83..f8a0b017fe 100644
--- a/test/net/sf/briar/transport/XorErasureCodeTest.java
+++ b/test/net/sf/briar/transport/XorErasureCodeTest.java
@@ -2,13 +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.MAC_LENGTH;
+import static org.junit.Assert.assertArrayEquals;
 
 import java.util.Random;
 
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.transport.Segment;
 
-import static org.junit.Assert.assertArrayEquals;
 import org.junit.Test;
 
 public class XorErasureCodeTest extends BriarTestCase {
diff --git a/test/net/sf/briar/transport/XorErasureDecoderTest.java b/test/net/sf/briar/transport/XorErasureDecoderTest.java
index 944610fcbe..43b66551b6 100644
--- a/test/net/sf/briar/transport/XorErasureDecoderTest.java
+++ b/test/net/sf/briar/transport/XorErasureDecoderTest.java
@@ -3,11 +3,11 @@ package net.sf.briar.transport;
 import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+import static org.junit.Assert.assertArrayEquals;
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.transport.Segment;
 
-import static org.junit.Assert.assertArrayEquals;
 import org.junit.Test;
 
 public class XorErasureDecoderTest extends BriarTestCase {
diff --git a/test/net/sf/briar/transport/XorErasureEncoderTest.java b/test/net/sf/briar/transport/XorErasureEncoderTest.java
index e1ca00b3ae..af67c0b644 100644
--- a/test/net/sf/briar/transport/XorErasureEncoderTest.java
+++ b/test/net/sf/briar/transport/XorErasureEncoderTest.java
@@ -10,30 +10,29 @@ public class XorErasureEncoderTest extends BriarTestCase {
 
 	@Test
 	public void testEncoding() {
-		// Create a 100-byte frame
+		// Create a frame
 		Frame f = new Frame();
-		f.setLength(100);
+		f.setLength(200);
 		byte[] b = f.getBuffer();
-		for(int i = 0; i < 100; i++) b[i] = (byte) i;
+		for(int i = 0; i < 200; i++) b[i] = (byte) i;
 		// Encode the frame
 		XorErasureEncoder e = new XorErasureEncoder(4);
 		Segment[] set = e.encodeFrame(f);
-		// There should be four pieces of 34 bytes each
+		// There should be four pieces of 67 bytes each
 		assertEquals(4, set.length);
-		for(int i = 0; i < 4; i++) assertEquals(34, set[i].getLength());
-		// The first three pieces should contain the data, plus two zero bytes
+		for(int i = 0; i < 4; i++) assertEquals(67, set[i].getLength());
+		// The first three pieces should contain the data plus on padding byte
 		byte[] b1 = set[0].getBuffer();
-		for(int i = 0; i < 34; i++) assertEquals(i, b1[i]);
+		for(int i = 0; i < 67; i++) assertEquals((byte) i, b1[i]);
 		byte[] b2 = set[1].getBuffer();
-		for(int i = 0; i < 34; i++) assertEquals(i + 34, b2[i]);
+		for(int i = 0; i < 67; i++) assertEquals((byte) (i + 67), b2[i]);
 		byte[] b3 = set[2].getBuffer();
-		for(int i = 0; i < 32; i++) assertEquals(i + 68, b3[i]);
-		assertEquals(0, b3[32]);
-		assertEquals(0, b3[33]);
+		for(int i = 0; i < 66; i++) assertEquals((byte) (i + 134), b3[i]);
+		assertEquals(0, b3[66]);
 		// The fourth piece should be the XOR of the other three
 		byte[] b4 = set[3].getBuffer();
-		byte[] expected = new byte[34];
-		for(int i = 0; i < 34; i++) {
+		byte[] expected = new byte[67];
+		for(int i = 0; i < 67; i++) {
 			expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]);
 		}
 		assertArrayEquals(expected, b4);
-- 
GitLab