diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index 95c0524571cbda43d2851afd543037db2fc1cf42..a2d73e9a39f240feedd6514e5dc95ce64463701c 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -67,7 +67,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 
 	public ConnectionReader createConnectionReader(SegmentSource in,
 			byte[] secret) {
-		return createConnectionReader(in, secret, new SegmentImpl(), false);
+		return createConnectionReader(in, secret, null, false);
 	}
 
 	private ConnectionReader createConnectionReader(SegmentSource in,
diff --git a/components/net/sf/briar/transport/ErasureEncoder.java b/components/net/sf/briar/transport/ErasureEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..b09fd687cb79916f803bd47be4357d6444866025
--- /dev/null
+++ b/components/net/sf/briar/transport/ErasureEncoder.java
@@ -0,0 +1,9 @@
+package net.sf.briar.transport;
+
+import net.sf.briar.api.transport.Segment;
+
+interface ErasureEncoder {
+
+	/** Encodes the given frame as a set of segments. */
+	Segment[] encodeFrame(Frame f);
+}
diff --git a/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java b/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
index db3ab691d2713d6470bf11550a6fbf387a182a00..d74dd5b5dc045b2c50cff003eaed209af5e3ff6e 100644
--- a/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
@@ -34,7 +34,7 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 		// Free any discard counts that are no longer too high for the window
 		Iterator<Long> it1 = discardCounts.keySet().iterator();
 		while(it1.hasNext()) if(!window.isTooHigh(it1.next())) it1.remove();
-		// Allocate a segment
+		// FIXME: Unnecessary allocation
 		Segment s = new SegmentImpl();
 		// Read segments until a frame can be decoded
 		while(true) {
@@ -51,9 +51,8 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
 			if(set == null) {
 				set = new Segment[n];
 				segmentSets.put(frameNumber, set);
-			} else {
-				set[(int) (frameNumber % n)] = s;
 			}
+			set[(int) (frameNumber % n)] = s;
 			// Try to decode the frame
 			if(decoder.decodeFrame(f, set)) return true;
 		}
diff --git a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..24168939b68b6cd511108e00aa8103a81561bb85
--- /dev/null
+++ b/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
@@ -0,0 +1,31 @@
+package net.sf.briar.transport;
+
+import java.io.IOException;
+
+import net.sf.briar.api.transport.Segment;
+
+class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
+
+	private final OutgoingEncryptionLayer out;
+	private final ErasureEncoder encoder;
+	private final int n;
+
+	OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out,
+			ErasureEncoder encoder, int n) {
+		this.out = out;
+		this.encoder = encoder;
+		this.n = n;
+	}
+
+	public void writeFrame(Frame f) throws IOException {
+		for(Segment s : encoder.encodeFrame(f)) out.writeSegment(s);
+	}
+
+	public void flush() throws IOException {
+		out.flush();
+	}
+
+	public long getRemainingCapacity() {
+		return out.getRemainingCapacity() / n;
+	}
+}
diff --git a/components/net/sf/briar/transport/SegmentImpl.java b/components/net/sf/briar/transport/SegmentImpl.java
index 8cd7acc549b924109a2fd9a35567f7c4647eea1c..991585457303b9c9c32644420c4cfcad5164827c 100644
--- a/components/net/sf/briar/transport/SegmentImpl.java
+++ b/components/net/sf/briar/transport/SegmentImpl.java
@@ -1,16 +1,24 @@
 package net.sf.briar.transport;
 
 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;
-import net.sf.briar.util.ByteUtils;
 
 class SegmentImpl implements Segment {
 
-	private final byte[] buf = new byte[MAX_SEGMENT_LENGTH];
+	private final byte[] buf;
 
 	private int length = -1;
 	private long segmentNumber = -1;
 
+	SegmentImpl() {
+		this(MAX_SEGMENT_LENGTH);
+	}
+
+	SegmentImpl(int length) {
+		buf = new byte[length];
+	}
+
 	public byte[] getBuffer() {
 		return buf;
 	}
@@ -32,7 +40,7 @@ class SegmentImpl implements Segment {
 	}
 
 	public void setSegmentNumber(long segmentNumber) {
-		if(segmentNumber < 0 || segmentNumber > ByteUtils.MAX_32_BIT_UNSIGNED)
+		if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
 		this.segmentNumber = segmentNumber;
 	}
diff --git a/components/net/sf/briar/transport/XorErasureDecoder.java b/components/net/sf/briar/transport/XorErasureDecoder.java
index 61682185a1673f76f575e50707e6e2f86dd186f5..5c44778bbff196a28a87cee181da0f1668cfaa8e 100644
--- a/components/net/sf/briar/transport/XorErasureDecoder.java
+++ b/components/net/sf/briar/transport/XorErasureDecoder.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_FRAME_LENGTH;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.transport.Segment;
@@ -54,7 +56,13 @@ class XorErasureDecoder implements ErasureDecoder {
 			assert missingOffset != -1;
 			System.arraycopy(parity, 0, dest, missingOffset, length);
 		}
-		f.setLength(offset);
+		assert offset == length * (n - 1);
+		// The frame length may 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;
+		if(frameLength > MAX_FRAME_LENGTH) throw new FormatException();
+		f.setLength(frameLength);
 		return true;
 	}
 }
diff --git a/components/net/sf/briar/transport/XorErasureEncoder.java b/components/net/sf/briar/transport/XorErasureEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..706f3147f2f1ea48869cb621924c4a1a715578b8
--- /dev/null
+++ b/components/net/sf/briar/transport/XorErasureEncoder.java
@@ -0,0 +1,30 @@
+package net.sf.briar.transport;
+
+import net.sf.briar.api.transport.Segment;
+
+/** An erasure encoder than uses k data segments and one parity segment. */
+class XorErasureEncoder implements ErasureEncoder {
+
+	private final int n;
+
+	XorErasureEncoder(int n) {
+		this.n = n;
+	}
+
+	public Segment[] encodeFrame(Frame f) {
+		Segment[] set = new Segment[n];
+		int length = (int) Math.ceil((float) f.getLength() / (n - 1));
+		for(int i = 0; i < n; i++) {
+			set[i] = new SegmentImpl(length);
+			set[i].setLength(length);
+		}
+		byte[] src = f.getBuffer(), parity = set[n - 1].getBuffer();
+		int offset = 0;
+		for(int i = 0; i < n - 1; i++) {
+			System.arraycopy(src, 0, set[i].getBuffer(), offset, length);
+			for(int j = 0; j < length; j++) parity[j] ^= src[j];
+			offset += length;
+		}
+		return set;
+	}
+}