diff --git a/api/net/sf/briar/api/transport/TransportConstants.java b/api/net/sf/briar/api/transport/TransportConstants.java
index 5e65a43c1d93eb03763b6adbc11461023e1c855d..0e73fb1fddd349602ba08dbc58ba998c89f36848 100644
--- a/api/net/sf/briar/api/transport/TransportConstants.java
+++ b/api/net/sf/briar/api/transport/TransportConstants.java
@@ -14,6 +14,9 @@ public interface TransportConstants {
 	/** The length of the frame header in bytes. */
 	static final int FRAME_HEADER_LENGTH = 8;
 
+	/** The length of the ack header in bytes. */
+	static final int ACK_HEADER_LENGTH = 5;
+
 	/** The length of the MAC in bytes. */
 	static final int MAC_LENGTH = 32;
 
diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index c4fdf88a3fc204272d0d8fc9d8df61b94779ca71..70b8982021d340df8042f211b2975ee401f4fff1 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -45,19 +45,20 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 		Cipher tagCipher = crypto.getTagCipher();
 		Cipher segCipher = crypto.getSegmentCipher();
 		IncomingEncryptionLayer encryption = new IncomingEncryptionLayerImpl(in,
-				tagCipher, segCipher, tagKey, segKey, false, bufferedTag);
+				tagCipher, segCipher, tagKey, segKey, false, false,
+				bufferedTag);
 		// No error correction
 		IncomingErrorCorrectionLayer correction =
 			new NullIncomingErrorCorrectionLayer(encryption);
 		// Create the authenticator
 		Mac mac = crypto.getMac();
 		IncomingAuthenticationLayer authentication =
-			new IncomingAuthenticationLayerImpl(correction, mac, macKey);
+			new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
 		// No reordering or retransmission
 		IncomingReliabilityLayer reliability =
 			new NullIncomingReliabilityLayer(authentication);
 		// Create the reader - don't tolerate errors
-		return new ConnectionReaderImpl(reliability, false);
+		return new ConnectionReaderImpl(reliability, false, false);
 	}
 
 	public ConnectionReader createConnectionReader(SegmentSource in,
@@ -82,18 +83,18 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 		Cipher segCipher = crypto.getSegmentCipher();
 		IncomingEncryptionLayer encryption =
 			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, false, bufferedSegment);
+					tagKey, segKey, false, false, bufferedSegment);
 		// No error correction
 		IncomingErrorCorrectionLayer correction =
 			new NullIncomingErrorCorrectionLayer(encryption);
 		// Create the authenticator
 		Mac mac = crypto.getMac();
 		IncomingAuthenticationLayer authentication =
-			new IncomingAuthenticationLayerImpl(correction, mac, macKey);
+			new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
 		// No reordering or retransmission
 		IncomingReliabilityLayer reliability =
 			new NullIncomingReliabilityLayer(authentication);
 		// Create the reader - don't tolerate errors
-		return new ConnectionReaderImpl(reliability, false);
+		return new ConnectionReaderImpl(reliability, false, false);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionReaderImpl.java b/components/net/sf/briar/transport/ConnectionReaderImpl.java
index 9fef13c9f8a292b2bb8fdb098abbd635e242bd0c..484a0736e84f8236b8b739e5411b797e2977e481 100644
--- a/components/net/sf/briar/transport/ConnectionReaderImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderImpl.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
 
 import java.io.IOException;
@@ -12,13 +13,17 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
 
 	private final IncomingReliabilityLayer in;
 	private final boolean tolerateErrors;
+	private final int headerLength;
 
 	private Frame frame;
 	private int offset = 0, length = 0;
 
-	ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors) {
+	ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors,
+			boolean ackHeader) {
 		this.in = in;
 		this.tolerateErrors = tolerateErrors;
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		frame = new Frame(in.getMaxFrameLength());
 	}
 
@@ -61,7 +66,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
 					length = -1;
 					return false;
 				}
-				offset = FRAME_HEADER_LENGTH;
+				offset = headerLength;
 				length = HeaderEncoder.getPayloadLength(frame.getBuffer());
 				return true;
 			} catch(InvalidDataException e) {
diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
index 404a9bc231c4442a5ff68c7b7696e16b22cc1453..843150e8b6c4dcb21c1df30bb3dc445249881743 100644
--- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
@@ -46,7 +46,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 		OutgoingReliabilityLayer reliability =
 			new NullOutgoingReliabilityLayer(authentication);
 		// Create the writer
-		return new ConnectionWriterImpl(reliability);
+		return new ConnectionWriterImpl(reliability, false);
 	}
 
 	public ConnectionWriter createConnectionWriter(SegmentSink out,
@@ -61,7 +61,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 		Cipher segCipher = crypto.getSegmentCipher();
 		OutgoingEncryptionLayer encryption =
 			new SegmentedOutgoingEncryptionLayer(out, capacity, tagCipher,
-					segCipher, tagKey, segKey, false);
+					segCipher, tagKey, segKey, false, false);
 		// No error correction
 		OutgoingErrorCorrectionLayer correction =
 			new NullOutgoingErrorCorrectionLayer(encryption);
@@ -73,6 +73,6 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 		OutgoingReliabilityLayer reliability =
 			new NullOutgoingReliabilityLayer(authentication);
 		// Create the writer
-		return new ConnectionWriterImpl(reliability);
+		return new ConnectionWriterImpl(reliability, false);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index e39de66619f253f1de7aa2c9fa99cf0abaa67998..f5947152657e3a8c5d7381a67e095d65a8cddf15 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_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.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -18,16 +19,20 @@ import net.sf.briar.api.transport.ConnectionWriter;
 class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
 	private final OutgoingReliabilityLayer out;
-	private final int maxFrameLength;
+	private final int headerLength, maxFrameLength;
 	private final Frame frame;
 
-	private int offset = FRAME_HEADER_LENGTH;
-	private long frameNumber = 0L;
+	private int offset;
+	private long frameNumber;
 
-	ConnectionWriterImpl(OutgoingReliabilityLayer out) {
+	ConnectionWriterImpl(OutgoingReliabilityLayer out, boolean ackHeader) {
 		this.out = out;
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		maxFrameLength = out.getMaxFrameLength();
 		frame = new Frame(maxFrameLength);
+		offset = headerLength;
+		frameNumber = 0L;
 	}
 
 	public OutputStream getOutputStream() {
@@ -37,17 +42,16 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	public long getRemainingCapacity() {
 		long capacity = out.getRemainingCapacity();
 		// If there's any data buffered, subtract it and its overhead
-		if(offset > FRAME_HEADER_LENGTH)
-			capacity -= offset + MAC_LENGTH;
+		if(offset > headerLength) capacity -= offset + MAC_LENGTH;
 		// Subtract the overhead from the remaining capacity
 		long frames = (long) Math.ceil((double) capacity / maxFrameLength);
-		int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH;
+		int overheadPerFrame = headerLength + MAC_LENGTH;
 		return Math.max(0L, capacity - frames * overheadPerFrame);
 	}
 
 	@Override
 	public void flush() throws IOException {
-		if(offset > FRAME_HEADER_LENGTH) writeFrame();
+		if(offset > headerLength) writeFrame();
 		out.flush();
 	}
 
@@ -80,12 +84,12 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
 	private void writeFrame() throws IOException {
 		if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
-		int payload = offset - FRAME_HEADER_LENGTH;
+		int payload = offset - headerLength;
 		assert payload > 0;
 		HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0);
 		frame.setLength(offset + MAC_LENGTH);
 		out.writeFrame(frame);
-		offset = FRAME_HEADER_LENGTH;
+		offset = headerLength;
 		frameNumber++;
 	}
 }
diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java
index 6d1aae7cbdda6904942f0d43d5fc980cd63066e5..3b1910e19dfa2394d8b50b154a523f314b1fc49d 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -37,7 +37,7 @@ class Frame {
 	}
 
 	public void setLength(int length) {
-		if(length < 0 || length > buf.length)
+		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH || length > buf.length)
 			throw new IllegalArgumentException();
 		this.length = length;
 	}
diff --git a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
index 5489a8ba9c50165a9f57e338a05a17fc8423389c..9e4ed9b8919ee228c27fb01af07baf118ba1cbec 100644
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 
@@ -13,11 +14,11 @@ import net.sf.briar.api.crypto.ErasableKey;
 class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 
 	private final IncomingErrorCorrectionLayer in;
-	private final int maxFrameLength;
 	private final Mac mac;
+	private final int headerLength, maxFrameLength;
 
 	IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac,
-			ErasableKey macKey) {
+			ErasableKey macKey, boolean ackHeader) {
 		this.in = in;
 		this.mac = mac;
 		try {
@@ -28,6 +29,8 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 		macKey.erase();
 		if(mac.getMacLength() != MAC_LENGTH)
 			throw new IllegalArgumentException();
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		maxFrameLength = in.getMaxFrameLength();
 	}
 
@@ -37,22 +40,22 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
 		if(!in.readFrame(f, window)) return false;
 		// Check that the length is legal
 		int length = f.getLength();
-		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
+		if(length < headerLength + MAC_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);
 		int padding = HeaderEncoder.getPaddingLength(buf);
-		if(length != FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH)
+		if(length != headerLength + payload + padding + MAC_LENGTH)
 			throw new InvalidDataException();
 		// Check that the padding is all zeroes
-		int paddingStart = FRAME_HEADER_LENGTH + payload;
+		int paddingStart = headerLength + payload;
 		for(int i = paddingStart; i < paddingStart + padding; i++) {
 			if(buf[i] != 0) throw new InvalidDataException();
 		}
 		// Verify the MAC
-		int macStart = FRAME_HEADER_LENGTH + payload + padding;
+		int macStart = headerLength + payload + padding;
 		mac.update(buf, 0, macStart);
 		byte[] expectedMac = mac.doFinal();
 		for(int i = 0; i < expectedMac.length; i++) {
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
index b9c702b278de0cd5fc86ca60c458566f9fa9f1d8..80e5c66beaa9f218f352d1a3853f8cd362993a8a 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_FRAME_LENGTH;
@@ -24,7 +25,7 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
 	private final boolean tagEverySegment;
-	private final int blockSize;
+	private final int headerLength, blockSize;
 	private final byte[] iv, ciphertext;
 
 	private byte[] bufferedTag;
@@ -33,7 +34,7 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 
 	IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
 			Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
-			boolean tagEverySegment, byte[] bufferedTag) {
+			boolean tagEverySegment, boolean ackHeader, byte[] bufferedTag) {
 		this.in = in;
 		this.tagCipher = tagCipher;
 		this.segCipher = segCipher;
@@ -41,6 +42,8 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 		this.segKey = segKey;
 		this.tagEverySegment = tagEverySegment;
 		this.bufferedTag = bufferedTag;
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		blockSize = segCipher.getBlockSize();
 		if(blockSize < FRAME_HEADER_LENGTH)
 			throw new IllegalArgumentException();
@@ -102,7 +105,7 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 			// Parse the frame header
 			int payload = HeaderEncoder.getPayloadLength(plaintext);
 			int padding = HeaderEncoder.getPaddingLength(plaintext);
-			int length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
+			int length = headerLength + payload + padding + MAC_LENGTH;
 			if(length > MAX_FRAME_LENGTH) throw new FormatException();
 			// Read the remainder of the frame
 			while(offset < length) {
diff --git a/components/net/sf/briar/transport/SegmentImpl.java b/components/net/sf/briar/transport/SegmentImpl.java
index 3c4552cf6159a48b30ad42fdc12251a504427c0e..a62c41e8343bdd170c8d690f9d92a7c950e5e344 100644
--- a/components/net/sf/briar/transport/SegmentImpl.java
+++ b/components/net/sf/briar/transport/SegmentImpl.java
@@ -39,7 +39,7 @@ class SegmentImpl implements Segment {
 	}
 
 	public void setLength(int length) {
-		if(length < 0 || length > buf.length)
+		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH || length > buf.length)
 			throw new IllegalArgumentException();
 		this.length = length;
 	}
diff --git a/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java b/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java
index 7f378c0ebba45ed270e542e45dc00cd8e96e4862..0316dd4832c6ca50f5a6c975fb9fe474c2647b2c 100644
--- a/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_SEGMENT_LENGTH;
@@ -21,7 +22,7 @@ class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
 	private final boolean tagEverySegment;
-	private final int blockSize, maxSegmentLength;
+	private final int blockSize, headerLength, maxSegmentLength;
 	private final Segment segment;
 	private final byte[] iv;
 
@@ -31,7 +32,8 @@ class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
 
 	SegmentedIncomingEncryptionLayer(SegmentSource in, Cipher tagCipher,
 			Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
-			boolean tagEverySegment, Segment bufferedSegment) {
+			boolean tagEverySegment, boolean ackHeader,
+			Segment bufferedSegment) {
 		this.in = in;
 		this.tagCipher = tagCipher;
 		this.segCipher = segCipher;
@@ -42,8 +44,10 @@ class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
 		blockSize = segCipher.getBlockSize();
 		if(blockSize < FRAME_HEADER_LENGTH)
 			throw new IllegalArgumentException();
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		int length = in.getMaxSegmentLength();
-		if(length < TAG_LENGTH + FRAME_HEADER_LENGTH + 1 + MAC_LENGTH)
+		if(length < TAG_LENGTH + headerLength + 1 + MAC_LENGTH)
 			throw new IllegalArgumentException();
 		if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
 		maxSegmentLength = length - TAG_LENGTH;
@@ -67,7 +71,7 @@ class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
 			}
 			int offset = expectTag ? TAG_LENGTH : 0;
 			int length = segment.getLength();
-			if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
+			if(length < offset + headerLength + MAC_LENGTH)
 				throw new InvalidDataException();
 			if(length > offset + maxSegmentLength)
 				throw new InvalidDataException();
diff --git a/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java b/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java
index 4807e0d09e299b6b34c2b1d2e74608156f3c0429..8eb1c0a683ee9259d75db6f827c7ecff26b8a704 100644
--- a/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_SEGMENT_LENGTH;
@@ -21,7 +22,7 @@ class SegmentedOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
 	private final Cipher tagCipher, segCipher;
 	private final ErasableKey tagKey, segKey;
 	private final boolean tagEverySegment;
-	private final int maxSegmentLength;
+	private final int headerLength, maxSegmentLength;
 	private final Segment segment;
 	private final byte[] iv;
 
@@ -29,7 +30,7 @@ class SegmentedOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
 
 	SegmentedOutgoingEncryptionLayer(SegmentSink out, long capacity,
 			Cipher tagCipher, Cipher segCipher, ErasableKey tagKey,
-			ErasableKey segKey, boolean tagEverySegment) {
+			ErasableKey segKey, boolean tagEverySegment, boolean ackHeader) {
 		this.out = out;
 		this.capacity = capacity;
 		this.tagCipher = tagCipher;
@@ -37,10 +38,12 @@ class SegmentedOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
 		this.tagKey = tagKey;
 		this.segKey = segKey;
 		this.tagEverySegment = tagEverySegment;
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 		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();
+		if(length < TAG_LENGTH + headerLength + 1 + MAC_LENGTH)
+			throw new IllegalArgumentException();
+		if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
 		maxSegmentLength = length - MAC_LENGTH;
 		segment = new SegmentImpl(length);
 		iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
diff --git a/components/net/sf/briar/transport/XorErasureDecoder.java b/components/net/sf/briar/transport/XorErasureDecoder.java
index 685aab252e74842a5fb87cf89e463e40de05e2ad..4ae174bb8a3cda8c934716db6fcf246250577f92 100644
--- a/components/net/sf/briar/transport/XorErasureDecoder.java
+++ b/components/net/sf/briar/transport/XorErasureDecoder.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_FRAME_LENGTH;
@@ -9,10 +10,12 @@ import net.sf.briar.api.transport.Segment;
 /** An erasure decoder that uses k data segments and one parity segment. */
 class XorErasureDecoder implements ErasureDecoder {
 
-	private final int n;
+	private final int n, headerLength;
 
-	XorErasureDecoder(int n) {
+	XorErasureDecoder(int n, boolean ackHeader) {
 		this.n = n;
+		if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
+		else headerLength = FRAME_HEADER_LENGTH;
 	}
 
 	public boolean decodeFrame(Frame f, Segment[] set) throws FormatException {
@@ -61,10 +64,10 @@ class XorErasureDecoder implements ErasureDecoder {
 			int copyLength = Math.min(length, dest.length - missingOffset);
 			System.arraycopy(parity, 0, dest, missingOffset, copyLength);
 		}
-		// The frame length may not be an exact multiple of the segment length
+		// The frame length might not be an exact multiple of the segment length
 		int payload = HeaderEncoder.getPayloadLength(dest);
 		int padding = HeaderEncoder.getPaddingLength(dest);
-		int frameLength = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
+		int frameLength = headerLength + payload + padding + MAC_LENGTH;
 		if(frameLength > MAX_FRAME_LENGTH) throw new FormatException();
 		f.setLength(frameLength);
 		return true;
diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
index feaddea5ce670569c7afed45d770f8f404dcf1c0..152ae8b4071c1ad7a2a5dc67ca18670227f58c06 100644
--- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
@@ -222,9 +222,9 @@ public class ConnectionReaderImplTest extends TransportTest {
 		IncomingErrorCorrectionLayer correction =
 			new NullIncomingErrorCorrectionLayer(encryption);
 		IncomingAuthenticationLayer authentication =
-			new IncomingAuthenticationLayerImpl(correction, mac, macKey);
+			new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
 		IncomingReliabilityLayer reliability =
 			new NullIncomingReliabilityLayer(authentication);
-		return new ConnectionReaderImpl(reliability, false);
+		return new ConnectionReaderImpl(reliability, false, false);
 	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
index 225062ba19a9e5aa83b1859daf1ff3d55687aae1..a5e0d5ac6e7fb55981b6c842e5e2e07615f8cb37 100644
--- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
@@ -115,6 +115,6 @@ public class ConnectionWriterImplTest extends TransportTest {
 			new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
 		OutgoingReliabilityLayer reliability =
 			new NullOutgoingReliabilityLayer(authentication);
-		return new ConnectionWriterImpl(reliability);
+		return new ConnectionWriterImpl(reliability, false);
 	}
 }
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index 5ca19087826e3778623d9121ee9096b789350435..89f54dd26a0a7468036715eaa2547f7e0c6bf893 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -83,7 +83,8 @@ public class FrameReadWriteTest extends BriarTestCase {
 			new OutgoingAuthenticationLayerImpl(correctionOut, mac, macCopy);
 		OutgoingReliabilityLayer reliabilityOut =
 			new NullOutgoingReliabilityLayer(authenticationOut);
-		ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut);
+		ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut,
+				false);
 		OutputStream out1 = writer.getOutputStream();
 		out1.write(frame);
 		out1.flush();
@@ -97,14 +98,16 @@ public class FrameReadWriteTest extends BriarTestCase {
 		assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
 		// Read the frames back
 		IncomingEncryptionLayer encryptionIn = new IncomingEncryptionLayerImpl(
-				in, tagCipher, segCipher, tagKey, segKey, false, recoveredTag);
+				in, tagCipher, segCipher, tagKey, segKey, false, false,
+				recoveredTag);
 		IncomingErrorCorrectionLayer correctionIn =
 			new NullIncomingErrorCorrectionLayer(encryptionIn);
 		IncomingAuthenticationLayer authenticationIn =
-			new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey);
+			new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey,
+					false);
 		IncomingReliabilityLayer reliabilityIn =
 			new NullIncomingReliabilityLayer(authenticationIn);
-		ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn,
+		ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn, false,
 				false);
 		InputStream in1 = reader.getInputStream();
 		byte[] recovered = new byte[frame.length];
diff --git a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
index d18b1eeaa7ec41bb46520ac8e0afdbdbe18adef7..0b34ece3bff1842e8229ce427568de023782d141 100644
--- a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
@@ -63,7 +63,7 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 		// Use the encryption layer to decrypt the ciphertext
 		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, segCipher, tagKey, segKey, false, tag);
+				tagCipher, segCipher, tagKey, segKey, false, false, tag);
 		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
@@ -114,7 +114,7 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 		// Use the encryption layer to decrypt the ciphertext
 		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, segCipher, tagKey, segKey, true, tag);
+				tagCipher, segCipher, tagKey, segKey, true, false, tag);
 		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
diff --git a/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java b/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java
index 2a2c11faf3eff4537af6929efeb44f6c74868aa7..23bba20b49cbe278bbd0ae45a0b17e5003707597 100644
--- a/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java
@@ -25,7 +25,8 @@ public class IncomingReliabilityLayerImplTest extends BriarTestCase {
 			new TestIncomingAuthenticationLayer(frameNumbers);
 		IncomingReliabilityLayerImpl reliability =
 			new IncomingReliabilityLayerImpl(authentication);
-		ConnectionReader reader = new ConnectionReaderImpl(reliability, false);
+		ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
+				false);
 		InputStream in = reader.getInputStream();
 		for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
 			for(int j = 0; j < 100; j++) assertEquals(i, in.read());
@@ -50,7 +51,8 @@ public class IncomingReliabilityLayerImplTest extends BriarTestCase {
 			new TestIncomingAuthenticationLayer(frameNumbers);
 		IncomingReliabilityLayerImpl reliability =
 			new IncomingReliabilityLayerImpl(authentication);
-		ConnectionReader reader = new ConnectionReaderImpl(reliability, false);
+		ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
+				false);
 		InputStream in = reader.getInputStream();
 		for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
 			for(int j = 0; j < 100; j++) assertEquals(i, in.read());
diff --git a/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java b/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java
index 8f8f5bc31f5568b4733833f04d79d4fb175c936a..2e8321d6e30f651dc704922bed25e33a5084ad89 100644
--- a/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java
@@ -66,7 +66,7 @@ public class SegmentedIncomingEncryptionLayerTest extends BriarTestCase {
 		// Use the encryption layer to decrypt the ciphertext
 		IncomingEncryptionLayer decrypter =
 			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, false, buffered);
+					tagKey, segKey, false, false, buffered);
 		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
@@ -117,7 +117,7 @@ public class SegmentedIncomingEncryptionLayerTest extends BriarTestCase {
 		// Use the encryption layer to decrypt the ciphertext
 		IncomingEncryptionLayer decrypter =
 			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, true, buffered);
+					tagKey, segKey, true, false, buffered);
 		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
diff --git a/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java b/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java
index 22bb21208a6cf1e42af956f55b2d4740ea33192c..3d6e5cae31056e168f829fc5db6cf9decc256143 100644
--- a/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java
@@ -66,7 +66,7 @@ public class SegmentedOutgoingEncryptionLayerTest extends BriarTestCase {
 		ByteArraySegmentSink sink = new ByteArraySegmentSink();
 		OutgoingEncryptionLayer encrypter =
 			new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
-					tagCipher, segCipher, tagKey, segKey, false);
+					tagCipher, segCipher, tagKey, segKey, false, false);
 		Segment s = new SegmentImpl();
 		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
 		s.setLength(plaintext.length);
@@ -114,7 +114,7 @@ public class SegmentedOutgoingEncryptionLayerTest extends BriarTestCase {
 		SegmentSink sink = new ByteArraySegmentSink();
 		OutgoingEncryptionLayer encrypter =
 			new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
-					tagCipher, segCipher, tagKey, segKey, true);
+					tagCipher, segCipher, tagKey, segKey, true, false);
 		Segment s = new SegmentImpl();
 		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
 		s.setLength(plaintext.length);
diff --git a/test/net/sf/briar/transport/XorErasureCodeTest.java b/test/net/sf/briar/transport/XorErasureCodeTest.java
index f8a0b017fe67902c6650637407bac81e08c7f136..0fa4158ffd0e8ac79d7f2b9c112c60c80712a20e 100644
--- a/test/net/sf/briar/transport/XorErasureCodeTest.java
+++ b/test/net/sf/briar/transport/XorErasureCodeTest.java
@@ -16,7 +16,7 @@ public class XorErasureCodeTest extends BriarTestCase {
 	@Test
 	public void testEncodingAndDecodingWithAllSegments() throws Exception {
 		XorErasureEncoder e = new XorErasureEncoder(5);
-		XorErasureDecoder d = new XorErasureDecoder(5);
+		XorErasureDecoder d = new XorErasureDecoder(5, false);
 		Frame f = new Frame(1234);
 		new Random().nextBytes(f.getBuffer());
 		int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
@@ -32,7 +32,7 @@ public class XorErasureCodeTest extends BriarTestCase {
 	@Test
 	public void testEncodingAndDecodingWithMissingSegment() throws Exception {
 		XorErasureEncoder e = new XorErasureEncoder(5);
-		XorErasureDecoder d = new XorErasureDecoder(5);
+		XorErasureDecoder d = new XorErasureDecoder(5, false);
 		Frame f = new Frame(1234);
 		new Random().nextBytes(f.getBuffer());
 		int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
diff --git a/test/net/sf/briar/transport/XorErasureDecoderTest.java b/test/net/sf/briar/transport/XorErasureDecoderTest.java
index 43b66551b63ad0987778b260e1a53a256bce631b..40346866476e5a6da8a1d081bc83190481e11b7c 100644
--- a/test/net/sf/briar/transport/XorErasureDecoderTest.java
+++ b/test/net/sf/briar/transport/XorErasureDecoderTest.java
@@ -14,7 +14,7 @@ public class XorErasureDecoderTest extends BriarTestCase {
 
 	@Test
 	public void testMaximumLength() throws Exception {
-		XorErasureDecoder d = new XorErasureDecoder(5);
+		XorErasureDecoder d = new XorErasureDecoder(5, false);
 		// A frame of the maximum length should be decoded successfully
 		Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
 		Frame f = new Frame();
@@ -37,7 +37,7 @@ public class XorErasureDecoderTest extends BriarTestCase {
 		set[1] = new SegmentImpl(251);
 		set[1].setLength(251);
 		// The frame should be decoded successfully
-		XorErasureDecoder d = new XorErasureDecoder(4);
+		XorErasureDecoder d = new XorErasureDecoder(4, false);
 		Frame f = new Frame(750);
 		assertTrue(d.decodeFrame(f, set));
 		// The minimum of the segments' lengths should have been used
@@ -46,7 +46,7 @@ public class XorErasureDecoderTest extends BriarTestCase {
 
 	@Test
 	public void testDecodingWithMissingSegment() throws Exception {
-		XorErasureDecoder d = new XorErasureDecoder(4);
+		XorErasureDecoder d = new XorErasureDecoder(4, false);
 		for(int i = 0; i < 4; i++) {
 			Segment[] set = encodeEmptyFrame(250, 4);
 			set[i] = null;
@@ -59,7 +59,7 @@ public class XorErasureDecoderTest extends BriarTestCase {
 
 	@Test
 	public void testDecodingWithTwoMissingSegments() throws Exception {
-		XorErasureDecoder d = new XorErasureDecoder(4);
+		XorErasureDecoder d = new XorErasureDecoder(4, false);
 		Segment[] set = encodeEmptyFrame(250, 4);
 		set[0] = null;
 		set[1] = null;