diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index 8b1944fa4b5d6845edd7ecf16c6153c1366f2e29..6801a40ed71e75d2a4c98bf856bd0f23305b58d2 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -9,10 +9,10 @@ import javax.crypto.Mac;
 
 public interface CryptoComponent {
 
-	ErasableKey deriveSegmentKey(byte[] secret, boolean initiator);
-
 	ErasableKey deriveTagKey(byte[] secret, boolean initiator);
 
+	ErasableKey deriveFrameKey(byte[] secret, boolean initiator);
+
 	ErasableKey deriveMacKey(byte[] secret, boolean initiator);
 
 	byte[] deriveNextSecret(byte[] secret, int index, long connection);
@@ -27,11 +27,11 @@ public interface CryptoComponent {
 
 	SecureRandom getSecureRandom();
 
-	Cipher getSegmentCipher();
+	Cipher getTagCipher();
+
+	Cipher getFrameCipher();
 
 	Signature getSignature();
 
-	Cipher getTagCipher();
-
 	Mac getMac();
 }
diff --git a/api/net/sf/briar/api/plugins/SegmentSink.java b/api/net/sf/briar/api/plugins/SegmentSink.java
deleted file mode 100644
index 83eaa2f6ae62eca33535d184112ccbb8d6cdb8b5..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/SegmentSink.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.sf.briar.api.plugins;
-
-import java.io.IOException;
-
-import net.sf.briar.api.transport.Segment;
-
-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
deleted file mode 100644
index 135d03f548a624962ff5ea3c2d674e4d4868d95a..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/SegmentSource.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.sf.briar.api.plugins;
-
-import java.io.IOException;
-
-import net.sf.briar.api.transport.Segment;
-
-public interface SegmentSource {
-
-	/**
-	 * Attempts to read a segment into the given buffer and returns true if a
-	 * 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/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPlugin.java b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPlugin.java
deleted file mode 100644
index 1dd7bf0d8a9f2079b302d440c45c9eb26c1774bd..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPlugin.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package net.sf.briar.api.plugins.duplex;
-
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.plugins.Plugin;
-
-/**
- * An interface for transport plugins that support duplex segmented
- * communication.
- */
-public interface DuplexSegmentedPlugin extends Plugin {
-
-	/**
-	 * Attempts to create and return a connection to the given contact using
-	 * the current transport and configuration properties. Returns null if a
-	 * connection could not be created.
-	 */
-	DuplexSegmentedTransportConnection createConnection(ContactId c);
-
-	/**
-	 * Starts the invitation process from the inviter's side. Returns null if
-	 * no connection can be established within the given timeout.
-	 */
-	DuplexSegmentedTransportConnection sendInvitation(int code, long timeout);
-
-	/**
-	 * Starts the invitation process from the invitee's side. Returns null if
-	 * no connection can be established within the given timeout.
-	 */
-	DuplexSegmentedTransportConnection acceptInvitation(int code, long timeout);
-}
diff --git a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginCallback.java b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginCallback.java
deleted file mode 100644
index 9fcbb1c56b70d93478c859ff177f05766cd52f55..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginCallback.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.sf.briar.api.plugins.duplex;
-
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.plugins.PluginCallback;
-
-/**
- * An interface for handling connections created by a duplex segmented
- * transport plugin.
- */
-public interface DuplexSegmentedPluginCallback extends PluginCallback {
-
-	void incomingConnectionCreated(DuplexSegmentedTransportConnection d);
-
-	void outgoingConnectionCreated(ContactId c,
-			DuplexSegmentedTransportConnection d);
-
-}
diff --git a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginFactory.java b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginFactory.java
deleted file mode 100644
index d32e372fafdb46b119710ecadea9150d5e6dbe63..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedPluginFactory.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.sf.briar.api.plugins.duplex;
-
-import java.util.concurrent.Executor;
-
-public interface DuplexSegmentedPluginFactory {
-
-	DuplexSegmentedPlugin createPlugin(Executor pluginExecutor,
-			DuplexSegmentedPluginCallback callback);
-
-}
diff --git a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java
deleted file mode 100644
index c9b16844edc621f914643b04c9fde63a8472f078..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.sf.briar.api.plugins.duplex;
-
-import net.sf.briar.api.plugins.SegmentSink;
-import net.sf.briar.api.plugins.SegmentSource;
-
-/**
- * An interface for reading and writing data over a duplex segmented transport.
- * The connection is not responsible for encrypting/decrypting or authenticating
- * the data.
- */
-public interface DuplexSegmentedTransportConnection extends SegmentSource,
-SegmentSink {
-
-	/** Returns the maximum length of a segment in bytes. */
-	int getMaximumSegmentLength();
-
-	/**
-	 * Returns true if the output stream should be flushed after each packet.
-	 */
-	boolean shouldFlush();
-
-	/**
-	 * Closes the connection and disposes of any associated resources. The
-	 * first argument indicates whether the connection is being closed because
-	 * of an exception and the second argument indicates whether the connection
-	 * was recognised, which may affect how resources are disposed of.
-	 */
-	void dispose(boolean exception, boolean recognised);
-}
diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPlugin.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPlugin.java
deleted file mode 100644
index 54deb5de058217e199bab33347818cf93acda46d..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPlugin.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package net.sf.briar.api.plugins.simplex;
-
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.plugins.Plugin;
-
-/**
- * An interface for transport plugins that support simplex segmented
- * communication.
- */
-public interface SimplexSegmentedPlugin extends Plugin {
-
-	/**
-	 * Attempts to create and return a reader for the given contact using the
-	 * current transport and configuration properties. Returns null if a reader
-	 * could not be created.
-	 */
-	SimplexSegmentedTransportReader createReader(ContactId c);
-
-	/**
-	 * Attempts to create and return a writer for the given contact using the
-	 * current transport and configuration properties. Returns null if a writer
-	 * could not be created.
-	 */
-	SimplexSegmentedTransportWriter createWriter(ContactId c);
-
-	/**
-	 * Starts the invitation process from the inviter's side. Returns null if
-	 * no connection can be established within the given timeout.
-	 */
-	SimplexSegmentedTransportWriter sendInvitation(int code, long timeout);
-
-	/**
-	 * Starts the invitation process from the invitee's side. Returns null if
-	 * no connection can be established within the given timeout.
-	 */
-	SimplexSegmentedTransportReader acceptInvitation(int code, long timeout);
-
-	/**
-	 * Continues the invitation process from the invitee's side. Returns null
-	 * if no connection can be established within the given timeout.
-	 */
-	SimplexSegmentedTransportWriter sendInvitationResponse(int code,
-			long timeout);
-
-	/**
-	 * Continues the invitation process from the inviter's side. Returns null
-	 * if no connection can be established within the given timeout.
-	 */
-	SimplexSegmentedTransportReader acceptInvitationResponse(int code,
-			long timeout);
-}
diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginCallback.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginCallback.java
deleted file mode 100644
index 654b89907bb6c1d29d214990c62fb1e8b2d0be20..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginCallback.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.sf.briar.api.plugins.simplex;
-
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.plugins.PluginCallback;
-
-/**
- * An interface for handling readers and writers created by a simplex
- * segmented transport plugin.
- */
-public interface SimplexSegmentedPluginCallback extends PluginCallback {
-
-	void readerCreated(SimplexSegmentedTransportReader r);
-
-	void writerCreated(ContactId c, SimplexSegmentedTransportWriter w);
-
-}
diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginFactory.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginFactory.java
deleted file mode 100644
index 1657c5f280bce1f7ba2f2ddafbded7b8288069e7..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedPluginFactory.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package net.sf.briar.api.plugins.simplex;
-
-import java.util.concurrent.Executor;
-
-public interface SimplexSegmentedPluginFactory {
-
-	SimplexSegmentedPlugin createPlugin(Executor pluginExecutor,
-			SimplexSegmentedPluginCallback callback);
-}
diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportReader.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportReader.java
deleted file mode 100644
index c34946d5cf14a8a512f70d44973c671e2100745b..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportReader.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.sf.briar.api.plugins.simplex;
-
-import net.sf.briar.api.plugins.SegmentSource;
-
-/**
- * An interface for reading data from a simplex segmented transport. The reader
- * is not responsible for decrypting or authenticating the data before
- * returning it.
- */
-public interface SimplexSegmentedTransportReader extends SegmentSource {
-
-	/**
-	 * Closes the reader and disposes of any associated resources. The first
-	 * argument indicates whether the reader is being closed because of an
-	 * exception and the second argument indicates whether the connection was
-	 * recognised, which may affect how resources are disposed of.
-	 */
-	void dispose(boolean exception, boolean recognised);
-}
diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java
deleted file mode 100644
index 364faf6ab34708a0713a27f4fef8cdfccd8de41d..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.sf.briar.api.plugins.simplex;
-
-import net.sf.briar.api.plugins.SegmentSink;
-
-/**
- * An interface for writing data to a simplex segmented transport. The writer is
- * not responsible for authenticating or encrypting the data before writing it.
- */
-public interface SimplexSegmentedTransportWriter extends SegmentSink {
-
-	/** Returns the capacity of the transport in bytes. */
-	long getCapacity();
-
-	/** Returns the maximum length of a segment in bytes. */
-	int getMaximumSegmentLength();
-
-	/**
-	 * Returns true if the output stream should be flushed after each packet.
-	 */
-	boolean shouldFlush();
-
-	/**
-	 * Closes the writer and disposes of any associated resources. The
-	 * argument indicates whether the writer is being closed because of an
-	 * exception, which may affect how resources are disposed of.
-	 */
-	void dispose(boolean exception);
-}
diff --git a/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java b/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java
index 94b22389bc3e29bb916bd9f48fd1759e528baf97..0d8e1dce2d5ef2f7371a863e7bba2f7a07cf3b38 100644
--- a/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java
+++ b/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java
@@ -9,7 +9,7 @@ import net.sf.briar.api.transport.ConnectionContext;
 public interface DuplexConnectionFactory {
 
 	void createIncomingConnection(ConnectionContext ctx, TransportId t,
-			DuplexTransportConnection d, byte[] tag);
+			DuplexTransportConnection d);
 
 	void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
 			DuplexTransportConnection d);
diff --git a/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java b/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java
index 7c18457c0b1a0f10c32b4f4336b101292c5cc29b..63c33a290b7bbb6560a08266155a3313732350a8 100644
--- a/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java
+++ b/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java
@@ -10,7 +10,7 @@ import net.sf.briar.api.transport.ConnectionContext;
 public interface SimplexConnectionFactory {
 
 	void createIncomingConnection(ConnectionContext ctx, TransportId t,
-			SimplexTransportReader r, byte[] tag);
+			SimplexTransportReader r);
 
 	void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
 			SimplexTransportWriter w);
diff --git a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
index 4620c5296e0981099f687b6ae9a846ae995ed9ad..c688ca9852b5be11c35830c60195e7473b98af1a 100644
--- a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
+++ b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
@@ -2,35 +2,12 @@ package net.sf.briar.api.transport;
 
 import java.io.InputStream;
 
-import net.sf.briar.api.plugins.SegmentSource;
-
 public interface ConnectionReaderFactory {
 
 	/**
-	 * Creates a connection reader for a simplex connection or the initiator's
-	 * side of a duplex connection. The secret is erased before this method
-	 * returns.
+	 * Creates a connection reader for a simplex connection or one side of a
+	 * duplex connection. The secret is erased before this method returns.
 	 */
 	ConnectionReader createConnectionReader(InputStream in, byte[] secret,
-			byte[] bufferedTag);
-
-	/**
-	 * Creates a connection reader for a simplex connection or the initiator's
-	 * side of a duplex connection. The secret is erased before this method
-	 * returns.
-	 */
-	ConnectionReader createConnectionReader(SegmentSource in, byte[] secret,
-			Segment bufferedSegment);
-
-	/**
-	 * Creates a connection reader for the responder's side of a duplex
-	 * connection. The secret is erased before this method returns.
-	 */
-	ConnectionReader createConnectionReader(InputStream in, byte[] secret);
-
-	/**
-	 * Creates a connection reader for the responder's side of a duplex
-	 * connection. The secret is erased before this method returns.
-	 */
-	ConnectionReader createConnectionReader(SegmentSource in, byte[] secret);
+			boolean initiator);
 }
diff --git a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
index be3d55747f93e0a754f686ab8872101956e6eaff..b5996f3f5f9a6d9e799349537579410a3b5488f3 100644
--- a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
+++ b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
@@ -2,8 +2,6 @@ package net.sf.briar.api.transport;
 
 import java.io.OutputStream;
 
-import net.sf.briar.api.plugins.SegmentSink;
-
 public interface ConnectionWriterFactory {
 
 	/**
@@ -12,11 +10,4 @@ public interface ConnectionWriterFactory {
 	 */
 	ConnectionWriter createConnectionWriter(OutputStream out, long capacity,
 			byte[] secret, boolean initiator);
-
-	/**
-	 * Creates a connection writer for a simplex connection or one side of a
-	 * duplex connection. The secret is erased before this method returns.
-	 */
-	ConnectionWriter createConnectionWriter(SegmentSink out, long capacity,
-			byte[] secret, boolean initiator);
 }
diff --git a/api/net/sf/briar/api/transport/Segment.java b/api/net/sf/briar/api/transport/Segment.java
deleted file mode 100644
index da91745fa4140d803c2372774a9a835360edd503..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/transport/Segment.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.sf.briar.api.transport;
-
-public interface Segment {
-
-	byte[] getBuffer();
-
-	int getLength();
-
-	long getSegmentNumber();
-
-	void setLength(int length);
-
-	void setSegmentNumber(long segmentNumber);
-}
diff --git a/api/net/sf/briar/api/transport/TransportConstants.java b/api/net/sf/briar/api/transport/TransportConstants.java
index 0e73fb1fddd349602ba08dbc58ba998c89f36848..e0ee78b2d56eeeb487b7cd769c4c7cd16c1438b0 100644
--- a/api/net/sf/briar/api/transport/TransportConstants.java
+++ b/api/net/sf/briar/api/transport/TransportConstants.java
@@ -2,21 +2,15 @@ package net.sf.briar.api.transport;
 
 public interface TransportConstants {
 
-	/** The maximum length of a segment in bytes, including the tag. */
-	static final int MAX_SEGMENT_LENGTH = 65536; // 2^16, 64 KiB
-
-	/** The length of the segment tag in bytes. */
+	/** The length of the connection tag in bytes. */
 	static final int TAG_LENGTH = 16;
 
 	/** The maximum length of a frame in bytes, including the header and MAC. */
-	static final int MAX_FRAME_LENGTH = MAX_SEGMENT_LENGTH - TAG_LENGTH;
+	static final int MAX_FRAME_LENGTH = 65536; // 2^16, 64 KiB
 
 	/** 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;
 
@@ -29,7 +23,4 @@ public interface TransportConstants {
 
 	/** The size of the connection reordering window. */
 	static final int CONNECTION_WINDOW_SIZE = 32;
-
-	/** The size of the frame reordering window. */
-	static final int FRAME_WINDOW_SIZE = 32;
 }
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index f8f046b1d1fc997c5636e2f0c630ac0c2b9f10e5..d60c53b1b63d05d04997a969235a3e923a4ca656 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -33,12 +33,12 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final String DIGEST_ALGO = "SHA-256";
 	private static final String SIGNATURE_ALGO = "ECDSA";
 	private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
-	private static final String SEGMENT_CIPHER_ALGO = "AES/CTR/NoPadding";
+	private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
 	private static final String MAC_ALGO = "HMacSHA256";
 
 	// Labels for key derivation, null-terminated
 	private static final byte[] TAG = { 'T', 'A', 'G', 0 };
-	private static final byte[] SEGMENT = { 'S', 'E', 'G', 0 };
+	private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 };
 	private static final byte[] MAC = { 'M', 'A', 'C', 0 };
 	private static final byte[] NEXT = { 'N', 'E', 'X', 'T', 0 };
 	// Context strings for key derivation
@@ -71,9 +71,9 @@ class CryptoComponentImpl implements CryptoComponent {
 		else return deriveKey(secret, TAG, RESPONDER);
 	}
 
-	public ErasableKey deriveSegmentKey(byte[] secret, boolean initiator) {
-		if(initiator) return deriveKey(secret, SEGMENT, INITIATOR);
-		else return deriveKey(secret, SEGMENT, RESPONDER);
+	public ErasableKey deriveFrameKey(byte[] secret, boolean initiator) {
+		if(initiator) return deriveKey(secret, FRAME, INITIATOR);
+		else return deriveKey(secret, FRAME, RESPONDER);
 	}
 
 	public ErasableKey deriveMacKey(byte[] secret, boolean initiator) {
@@ -168,9 +168,9 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	public Cipher getSegmentCipher() {
+	public Cipher getFrameCipher() {
 		try {
-			return Cipher.getInstance(SEGMENT_CIPHER_ALGO, PROVIDER);
+			return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
 		} catch(GeneralSecurityException e) {
 			throw new RuntimeException(e);
 		}
diff --git a/components/net/sf/briar/protocol/duplex/StreamConnectionFactoryImpl.java b/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java
similarity index 93%
rename from components/net/sf/briar/protocol/duplex/StreamConnectionFactoryImpl.java
rename to components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java
index 3a2953abbf5fcbd15e1887dbc4a018b0b88b3539..ed4f08f1fb52b2f332f5e5c8852c0a8e105a48bb 100644
--- a/components/net/sf/briar/protocol/duplex/StreamConnectionFactoryImpl.java
+++ b/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java
@@ -19,7 +19,7 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
 
 import com.google.inject.Inject;
 
-class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
+class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
 
 	private final Executor dbExecutor, verificationExecutor;
 	private final DatabaseComponent db;
@@ -30,7 +30,7 @@ class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
 	private final ProtocolWriterFactory protoWriterFactory;
 
 	@Inject
-	StreamConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
+	DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
 			@VerificationExecutor Executor verificationExecutor,
 			DatabaseComponent db, ConnectionRegistry connRegistry,
 			ConnectionReaderFactory connReaderFactory,
@@ -48,11 +48,11 @@ class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
 	}
 
 	public void createIncomingConnection(ConnectionContext ctx, TransportId t,
-			DuplexTransportConnection d, byte[] tag) {
+			DuplexTransportConnection d) {
 		final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
 				verificationExecutor, db, connRegistry, connReaderFactory,
 				connWriterFactory, protoReaderFactory, protoWriterFactory,
-				ctx, t, d, tag);
+				ctx, t, d);
 		Runnable write = new Runnable() {
 			public void run() {
 				conn.write();
diff --git a/components/net/sf/briar/protocol/duplex/DuplexProtocolModule.java b/components/net/sf/briar/protocol/duplex/DuplexProtocolModule.java
index e04df3b249e029a0018e6e7022e268b52f290402..1ba18df14b28537b1e2282678a9a816640831917 100644
--- a/components/net/sf/briar/protocol/duplex/DuplexProtocolModule.java
+++ b/components/net/sf/briar/protocol/duplex/DuplexProtocolModule.java
@@ -10,6 +10,6 @@ public class DuplexProtocolModule extends AbstractModule {
 	@Override
 	protected void configure() {
 		bind(DuplexConnectionFactory.class).to(
-				StreamConnectionFactoryImpl.class).in(Singleton.class);
+				DuplexConnectionFactoryImpl.class).in(Singleton.class);
 	}
 }
diff --git a/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java b/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java
index 898cefd8aa08d67abb6ad46bb8b07eaec9ec81fa..3a888eaa7fa5f487830fe53111119449762c921a 100644
--- a/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java
+++ b/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java
@@ -20,7 +20,6 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
 class IncomingDuplexConnection extends DuplexConnection {
 
 	private final ConnectionContext ctx;
-	private final byte[] tag;
 
 	IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
 			@VerificationExecutor Executor verificationExecutor,
@@ -30,18 +29,17 @@ class IncomingDuplexConnection extends DuplexConnection {
 			ProtocolReaderFactory protoReaderFactory,
 			ProtocolWriterFactory protoWriterFactory,
 			ConnectionContext ctx, TransportId transportId,
-			DuplexTransportConnection transport, byte[] tag) {
+			DuplexTransportConnection transport) {
 		super(dbExecutor, verificationExecutor, db, connRegistry,
 				connReaderFactory, connWriterFactory, protoReaderFactory,
 				protoWriterFactory, ctx.getContactId(), transportId, transport);
 		this.ctx = ctx;
-		this.tag = tag;
 	}
 
 	@Override
 	protected ConnectionReader createConnectionReader() throws IOException {
 		return connReaderFactory.createConnectionReader(
-				transport.getInputStream(), ctx.getSecret(), tag);
+				transport.getInputStream(), ctx.getSecret(), true);
 	}
 
 	@Override
diff --git a/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java b/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java
index ffc60361d98f1ea24f192d1147ff7632c8d9d105..b784b8115eda9f7c67273e5dd6f6ad51cd2d7c4c 100644
--- a/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java
+++ b/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java
@@ -49,7 +49,7 @@ class OutgoingDuplexConnection extends DuplexConnection {
 				ctx = db.getConnectionContext(contactId, transportIndex);
 		}
 		return connReaderFactory.createConnectionReader(
-				transport.getInputStream(), ctx.getSecret());
+				transport.getInputStream(), ctx.getSecret(), false);
 	}
 
 	@Override
diff --git a/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java b/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java
index 224071e59a35b350c6d80c2b15620b125d3ceecc..f05e8c5ec78836c426b79f367349721fb8ef43ef 100644
--- a/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java
+++ b/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java
@@ -40,7 +40,6 @@ class IncomingSimplexConnection {
 	private final ConnectionContext ctx;
 	private final TransportId transportId;
 	private final SimplexTransportReader transport;
-	private final byte[] tag;
 	private final ContactId contactId;
 
 	IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
@@ -48,8 +47,7 @@ class IncomingSimplexConnection {
 			DatabaseComponent db, ConnectionRegistry connRegistry,
 			ConnectionReaderFactory connFactory,
 			ProtocolReaderFactory protoFactory, ConnectionContext ctx,
-			TransportId transportId, SimplexTransportReader transport,
-			byte[] tag) {
+			TransportId transportId, SimplexTransportReader transport) {
 		this.dbExecutor = dbExecutor;
 		this.verificationExecutor = verificationExecutor;
 		this.db = db;
@@ -59,7 +57,6 @@ class IncomingSimplexConnection {
 		this.ctx = ctx;
 		this.transportId = transportId;
 		this.transport = transport;
-		this.tag = tag;
 		contactId = ctx.getContactId();
 	}
 
@@ -67,7 +64,7 @@ class IncomingSimplexConnection {
 		connRegistry.registerConnection(contactId, transportId);
 		try {
 			ConnectionReader conn = connFactory.createConnectionReader(
-					transport.getInputStream(), ctx.getSecret(), tag);
+					transport.getInputStream(), ctx.getSecret(), true);
 			InputStream in = conn.getInputStream();
 			ProtocolReader reader = protoFactory.createProtocolReader(in);
 			// Read packets until EOF
diff --git a/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java b/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java
index 6dfa5892f0ca10aa0a0efc925fb7204497b44b2c..1c08ffe8942e1658f31c7f47065e90b4418fc7a7 100644
--- a/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java
+++ b/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java
@@ -49,10 +49,10 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
 	}
 
 	public void createIncomingConnection(ConnectionContext ctx, TransportId t,
-			SimplexTransportReader r, byte[] tag) {
+			SimplexTransportReader r) {
 		final IncomingSimplexConnection conn = new IncomingSimplexConnection(
 				dbExecutor, verificationExecutor, db, connRegistry,
-				connReaderFactory, protoReaderFactory, ctx, t, r, tag);
+				connReaderFactory, protoReaderFactory, ctx, t, r);
 		Runnable read = new Runnable() {
 			public void run() {
 				conn.read();
diff --git a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java
index 42c3b6cba3dd4c768a6a5c6d9d690134367c3006..4adbba871fad240f83426b1623a317a21f69d5f1 100644
--- a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java
+++ b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java
@@ -93,7 +93,7 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
 						tag);
 				if(ctx == null) transport.dispose(false, false);
 				else batchConnFactory.createIncomingConnection(ctx, transportId,
-						transport, tag);
+						transport);
 			} catch(DbException e) {
 				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
 				transport.dispose(true, false);
@@ -122,7 +122,7 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
 						tag);
 				if(ctx == null) transport.dispose(false, false);
 				else streamConnFactory.createIncomingConnection(ctx,
-						transportId, transport, tag);
+						transportId, transport);
 			} catch(DbException e) {
 				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
 				transport.dispose(true, false);
diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index 70b8982021d340df8042f211b2975ee401f4fff1..8163e047ffa6b31e371ba30ab63e7a6bb2d2e14a 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -7,10 +7,8 @@ import javax.crypto.Mac;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSource;
 import net.sf.briar.api.transport.ConnectionReader;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
-import net.sf.briar.api.transport.Segment;
 import net.sf.briar.util.ByteUtils;
 
 import com.google.inject.Inject;
@@ -25,76 +23,22 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 	}
 
 	public ConnectionReader createConnectionReader(InputStream in,
-			byte[] secret, byte[] bufferedTag) {
-		return createConnectionReader(in, secret, bufferedTag, true);
-	}
-
-	public ConnectionReader createConnectionReader(InputStream in,
-			byte[] secret) {
-		return createConnectionReader(in, secret, null, false);
-	}
-
-	private ConnectionReader createConnectionReader(InputStream in,
-			byte[] secret, byte[] bufferedTag, boolean initiator) {
-		// Derive the keys and erase the secret
-		ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
-		ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
-		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
-		ByteUtils.erase(secret);
-		// Create the decrypter
-		Cipher tagCipher = crypto.getTagCipher();
-		Cipher segCipher = crypto.getSegmentCipher();
-		IncomingEncryptionLayer encryption = new IncomingEncryptionLayerImpl(in,
-				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, false);
-		// No reordering or retransmission
-		IncomingReliabilityLayer reliability =
-			new NullIncomingReliabilityLayer(authentication);
-		// Create the reader - don't tolerate errors
-		return new ConnectionReaderImpl(reliability, false, false);
-	}
-
-	public ConnectionReader createConnectionReader(SegmentSource in,
-			byte[] secret, Segment bufferedSegment) {
-		return createConnectionReader(in, secret, bufferedSegment, true);
-	}
-
-	public ConnectionReader createConnectionReader(SegmentSource in,
-			byte[] secret) {
-		return createConnectionReader(in, secret, null, false);
-	}
-
-	private ConnectionReader createConnectionReader(SegmentSource in,
-			byte[] secret, Segment bufferedSegment, boolean initiator) {
+			byte[] secret, boolean initiator) {
 		// Derive the keys and erase the secret
 		ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
-		ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
+		ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
 		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
 		ByteUtils.erase(secret);
-		// Create the decrypter
+		// Encryption
 		Cipher tagCipher = crypto.getTagCipher();
-		Cipher segCipher = crypto.getSegmentCipher();
-		IncomingEncryptionLayer encryption =
-			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, false, false, bufferedSegment);
-		// No error correction
-		IncomingErrorCorrectionLayer correction =
-			new NullIncomingErrorCorrectionLayer(encryption);
-		// Create the authenticator
+		Cipher frameCipher = crypto.getFrameCipher();
+		FrameReader encryption = new IncomingEncryptionLayerImpl(in, tagCipher,
+				frameCipher, tagKey, frameKey, !initiator);
+		// Authentication
 		Mac mac = crypto.getMac();
-		IncomingAuthenticationLayer authentication =
-			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, false);
+		FrameReader authentication = new IncomingAuthenticationLayerImpl(
+				encryption, mac, macKey);
+		// Create the reader
+		return new ConnectionReaderImpl(authentication);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionReaderImpl.java b/components/net/sf/briar/transport/ConnectionReaderImpl.java
index 484a0736e84f8236b8b739e5411b797e2977e481..ad539dc63b94d9a0b41f1d61e26e2fd886b3f070 100644
--- a/components/net/sf/briar/transport/ConnectionReaderImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderImpl.java
@@ -1,30 +1,23 @@
 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;
 import java.io.InputStream;
 
-import net.sf.briar.api.FormatException;
 import net.sf.briar.api.transport.ConnectionReader;
 
 class ConnectionReaderImpl extends InputStream implements ConnectionReader {
 
-	private final IncomingReliabilityLayer in;
-	private final boolean tolerateErrors;
-	private final int headerLength;
+	private final FrameReader in;
+	private final Frame frame;
 
-	private Frame frame;
 	private int offset = 0, length = 0;
 
-	ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors,
-			boolean ackHeader) {
+	ConnectionReaderImpl(FrameReader in) {
 		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());
+		frame = new Frame();
+		offset = FRAME_HEADER_LENGTH;
 	}
 
 	public InputStream getInputStream() {
@@ -60,19 +53,14 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
 	private boolean readFrame() throws IOException {
 		assert length == 0;
 		while(true) {
-			try {
-				frame = in.readFrame(frame);
-				if(frame == null) {
-					length = -1;
-					return false;
-				}
-				offset = headerLength;
-				length = HeaderEncoder.getPayloadLength(frame.getBuffer());
-				return true;
-			} catch(InvalidDataException e) {
-				if(tolerateErrors) continue;
-				throw new FormatException();
+			frame.reset();
+			if(!in.readFrame(frame)) {
+				length = -1;
+				return false;
 			}
+			offset = FRAME_HEADER_LENGTH;
+			length = HeaderEncoder.getPayloadLength(frame.getBuffer());
+			return true;
 		}
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
index 68cfe03cd6ad7b998a0a999c884dd95d35f0c243..5babc2278cd3c3f1d33e9fe9e53b2b4cb97f504c 100644
--- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
+++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
@@ -103,7 +103,7 @@ DatabaseListener {
 	private Bytes calculateTag(Context ctx, byte[] secret) {
 		ErasableKey tagKey = crypto.deriveTagKey(secret, true);
 		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
+		TagEncoder.encodeTag(tag, tagCipher, tagKey);
 		tagKey.erase();
 		return new Bytes(tag);
 	}
diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
index 843150e8b6c4dcb21c1df30bb3dc445249881743..cffa5521861e6455282129057a26ac5688ec5671 100644
--- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
@@ -7,7 +7,6 @@ import javax.crypto.Mac;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSink;
 import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.util.ByteUtils;
@@ -27,52 +26,19 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 			long capacity, byte[] secret, boolean initiator) {
 		// Derive the keys and erase the secret
 		ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
-		ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
+		ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
 		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
 		ByteUtils.erase(secret);
-		// Create the encrypter
+		// Encryption
 		Cipher tagCipher = crypto.getTagCipher();
-		Cipher segCipher = crypto.getSegmentCipher();
-		OutgoingEncryptionLayer encryption = new OutgoingEncryptionLayerImpl(
-				out, capacity, tagCipher, segCipher, tagKey, segKey, false);
-		// No error correction
-		OutgoingErrorCorrectionLayer correction =
-			new NullOutgoingErrorCorrectionLayer(encryption);
+		Cipher frameCipher = crypto.getFrameCipher();
+		FrameWriter encryption = new OutgoingEncryptionLayerImpl(
+				out, capacity, tagCipher, frameCipher, tagKey, frameKey);
 		// Authentication
 		Mac mac = crypto.getMac();
-		OutgoingAuthenticationLayer authentication =
-			new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
-		// No retransmission
-		OutgoingReliabilityLayer reliability =
-			new NullOutgoingReliabilityLayer(authentication);
+		FrameWriter authentication =
+			new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
 		// Create the writer
-		return new ConnectionWriterImpl(reliability, false);
-	}
-
-	public ConnectionWriter createConnectionWriter(SegmentSink out,
-			long capacity, byte[] secret, boolean initiator) {
-		// Derive the keys and erase the secret
-		ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
-		ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
-		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
-		ByteUtils.erase(secret);
-		// Create the encrypter
-		Cipher tagCipher = crypto.getTagCipher();
-		Cipher segCipher = crypto.getSegmentCipher();
-		OutgoingEncryptionLayer encryption =
-			new SegmentedOutgoingEncryptionLayer(out, capacity, tagCipher,
-					segCipher, tagKey, segKey, false, false);
-		// No error correction
-		OutgoingErrorCorrectionLayer correction =
-			new NullOutgoingErrorCorrectionLayer(encryption);
-		// Authentication
-		Mac mac = crypto.getMac();
-		OutgoingAuthenticationLayer authentication =
-			new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
-		// No retransmission
-		OutgoingReliabilityLayer reliability =
-			new NullOutgoingReliabilityLayer(authentication);
-		// Create the writer
-		return new ConnectionWriterImpl(reliability, false);
+		return new ConnectionWriterImpl(authentication);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index f5947152657e3a8c5d7381a67e095d65a8cddf15..bc95444f19bfd06e3a8067ef7a1cffac95ebbf82 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -1,8 +1,8 @@
 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;
 import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.IOException;
@@ -18,20 +18,16 @@ import net.sf.briar.api.transport.ConnectionWriter;
  */
 class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
-	private final OutgoingReliabilityLayer out;
-	private final int headerLength, maxFrameLength;
+	private final FrameWriter out;
 	private final Frame frame;
 
 	private int offset;
 	private long frameNumber;
 
-	ConnectionWriterImpl(OutgoingReliabilityLayer out, boolean ackHeader) {
+	ConnectionWriterImpl(FrameWriter out) {
 		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;
+		frame = new Frame();
+		offset = FRAME_HEADER_LENGTH;
 		frameNumber = 0L;
 	}
 
@@ -42,23 +38,23 @@ 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 > headerLength) capacity -= offset + MAC_LENGTH;
+		if(offset > FRAME_HEADER_LENGTH) capacity -= offset + MAC_LENGTH;
 		// Subtract the overhead from the remaining capacity
-		long frames = (long) Math.ceil((double) capacity / maxFrameLength);
-		int overheadPerFrame = headerLength + MAC_LENGTH;
+		long frames = (long) Math.ceil((double) capacity / MAX_FRAME_LENGTH);
+		int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH;
 		return Math.max(0L, capacity - frames * overheadPerFrame);
 	}
 
 	@Override
 	public void flush() throws IOException {
-		if(offset > headerLength) writeFrame();
+		if(offset > FRAME_HEADER_LENGTH) writeFrame();
 		out.flush();
 	}
 
 	@Override
 	public void write(int b) throws IOException {
 		frame.getBuffer()[offset++] = (byte) b;
-		if(offset + MAC_LENGTH == maxFrameLength) writeFrame();
+		if(offset + MAC_LENGTH == MAX_FRAME_LENGTH) writeFrame();
 	}
 
 	@Override
@@ -69,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 = maxFrameLength - offset - MAC_LENGTH;
+		int available = MAX_FRAME_LENGTH - offset - MAC_LENGTH;
 		while(available <= len) {
 			System.arraycopy(b, off, buf, offset, available);
 			offset += available;
 			writeFrame();
 			off += available;
 			len -= available;
-			available = maxFrameLength - offset - MAC_LENGTH;
+			available = MAX_FRAME_LENGTH - offset - MAC_LENGTH;
 		}
 		System.arraycopy(b, off, buf, offset, len);
 		offset += len;
@@ -84,12 +80,13 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 
 	private void writeFrame() throws IOException {
 		if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
-		int payload = offset - headerLength;
+		int payload = offset - FRAME_HEADER_LENGTH;
 		assert payload > 0;
 		HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0);
 		frame.setLength(offset + MAC_LENGTH);
 		out.writeFrame(frame);
-		offset = headerLength;
+		frame.reset();
+		offset = FRAME_HEADER_LENGTH;
 		frameNumber++;
 	}
 }
diff --git a/components/net/sf/briar/transport/ErasureDecoder.java b/components/net/sf/briar/transport/ErasureDecoder.java
deleted file mode 100644
index 679c26ab20f19ffa35a17bafdd510488c8485b11..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/ErasureDecoder.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.sf.briar.transport;
-
-import net.sf.briar.api.FormatException;
-import net.sf.briar.api.transport.Segment;
-
-interface ErasureDecoder {
-
-	/**
-	 * Decodes the given set of segments into the given frame, or returns false
-	 * if the segments cannot be decoded. The segment set may contain nulls.
-	 */
-	public boolean decodeFrame(Frame f, Segment[] set) throws FormatException;
-}
diff --git a/components/net/sf/briar/transport/ErasureEncoder.java b/components/net/sf/briar/transport/ErasureEncoder.java
deleted file mode 100644
index b09fd687cb79916f803bd47be4357d6444866025..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/ErasureEncoder.java
+++ /dev/null
@@ -1,9 +0,0 @@
-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/Frame.java b/components/net/sf/briar/transport/Frame.java
index 3b1910e19dfa2394d8b50b154a523f314b1fc49d..93ef395c36e7ce6151bb76c2e8f229e51f9d9ff7 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -3,7 +3,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.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
 
 class Frame {
 
@@ -12,25 +11,13 @@ class Frame {
 	private int length = -1;
 
 	Frame() {
-		this(MAX_FRAME_LENGTH);
-	}
-
-	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];
+		buf = new byte[MAX_FRAME_LENGTH];
 	}
 
 	public byte[] getBuffer() {
 		return buf;
 	}
 
-	public long getFrameNumber() {
-		if(length == -1) throw new IllegalStateException();
-		return HeaderEncoder.getFrameNumber(buf);
-	}
-
 	public int getLength() {
 		if(length == -1) throw new IllegalStateException();
 		return length;
@@ -41,4 +28,8 @@ class Frame {
 			throw new IllegalArgumentException();
 		this.length = length;
 	}
+
+	public void reset() {
+		length = -1;
+	}
 }
diff --git a/components/net/sf/briar/transport/FrameReader.java b/components/net/sf/briar/transport/FrameReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad4a8b625180c9e0c8ef0d7e1301f2d5e694eab5
--- /dev/null
+++ b/components/net/sf/briar/transport/FrameReader.java
@@ -0,0 +1,12 @@
+package net.sf.briar.transport;
+
+import java.io.IOException;
+
+interface FrameReader {
+
+	/**
+	 * Reads a frame into the given buffer. Returns false if no more frames can
+	 * be read from the connection.
+	 */
+	boolean readFrame(Frame f) throws IOException;
+}
diff --git a/components/net/sf/briar/transport/FrameWindow.java b/components/net/sf/briar/transport/FrameWindow.java
deleted file mode 100644
index 85e162b1f1cc90c909dfe91ead44e7b933606953..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/FrameWindow.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.sf.briar.transport;
-
-interface FrameWindow {
-
-	/** Returns true if the given number is too high to fit in the window. */
-	boolean isTooHigh(long frameNumber);
-
-	/** Returns true if the given number is in the window. */
-	boolean contains(long frameNumber);
-
-	/**
-	 * Removes the given number from the window and advances the window.
-	 * Returns false if the given number is not in the window.
-	 */
-	boolean remove(long frameNumber);
-}
diff --git a/components/net/sf/briar/transport/FrameWindowImpl.java b/components/net/sf/briar/transport/FrameWindowImpl.java
deleted file mode 100644
index c3ff67a75a789c42d15fa8528f0f682d7c391c5a..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/FrameWindowImpl.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
-
-import java.util.Collection;
-import java.util.HashSet;
-
-/** A frame window that allows a limited amount of reordering. */
-class FrameWindowImpl implements FrameWindow {
-
-	private final Collection<Long> window;
-
-	private long base;
-
-	FrameWindowImpl() {
-		window = new HashSet<Long>();
-		fill(0, FRAME_WINDOW_SIZE);
-		base = 0;
-	}
-
-	public boolean isTooHigh(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		return frameNumber >= base + FRAME_WINDOW_SIZE;
-	}
-
-	public boolean contains(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		return window.contains(frameNumber);
-	}
-
-	public boolean remove(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		if(!window.remove(frameNumber)) return false;
-		if(frameNumber == base) {
-			// Find the new base
-			if(window.isEmpty()) {
-				base += FRAME_WINDOW_SIZE;
-				fill(base, base + FRAME_WINDOW_SIZE);
-			} else {
-				for(long l = base; l < base + FRAME_WINDOW_SIZE; l++) {
-					if(window.contains(l)) {
-						fill(base + FRAME_WINDOW_SIZE, l + FRAME_WINDOW_SIZE);
-						base = l;
-						break;
-					}
-				}
-			}
-		}
-		return true;
-	}
-
-	private void fill(long from, long to) {
-		for(long l = from; l < to; l++) {
-			if(l <= MAX_32_BIT_UNSIGNED) window.add(l);
-			else return;
-		}
-	}
-}
diff --git a/components/net/sf/briar/transport/OutgoingReliabilityLayer.java b/components/net/sf/briar/transport/FrameWriter.java
similarity index 67%
rename from components/net/sf/briar/transport/OutgoingReliabilityLayer.java
rename to components/net/sf/briar/transport/FrameWriter.java
index ef6797d9a893fdfb6576bc53491130928593eb4f..d8f24517f431a5be34456cae2d39381dcf9e1100 100644
--- a/components/net/sf/briar/transport/OutgoingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/FrameWriter.java
@@ -2,7 +2,7 @@ package net.sf.briar.transport;
 
 import java.io.IOException;
 
-interface OutgoingReliabilityLayer {
+interface FrameWriter {
 
 	/** Writes the given frame. */
 	void writeFrame(Frame f) throws IOException;
@@ -12,7 +12,4 @@ 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/IncomingAuthenticationLayer.java b/components/net/sf/briar/transport/IncomingAuthenticationLayer.java
deleted file mode 100644
index 33a6f20aa3e9590bc47c6390cb6d202aed00683b..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-interface IncomingAuthenticationLayer {
-
-	/**
-	 * 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.
-	 * @throws IOException if an unrecoverable error occurs and the connection
-	 * must be closed.
-	 * @throws InvalidDataException if a recoverable error occurs. The caller
-	 * may choose whether to retry the read or close the connection.
-	 */
-	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 9e4ed9b8919ee228c27fb01af07baf118ba1cbec..81cacbae19a5d29811c0979b6a695e69910ec1cf 100644
--- a/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingAuthenticationLayerImpl.java
@@ -1,24 +1,24 @@
 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;
 
 import java.io.IOException;
 import java.security.InvalidKeyException;
 
 import javax.crypto.Mac;
 
+import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.ErasableKey;
 
-class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
+class IncomingAuthenticationLayerImpl implements FrameReader {
 
-	private final IncomingErrorCorrectionLayer in;
+	private final FrameReader in;
 	private final Mac mac;
-	private final int headerLength, maxFrameLength;
 
-	IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac,
-			ErasableKey macKey, boolean ackHeader) {
+	IncomingAuthenticationLayerImpl(FrameReader in, Mac mac,
+			ErasableKey macKey) {
 		this.in = in;
 		this.mac = mac;
 		try {
@@ -29,43 +29,34 @@ 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();
 	}
 
-	public boolean readFrame(Frame f, FrameWindow window) throws IOException,
-	InvalidDataException {
+	public boolean readFrame(Frame f) throws IOException {
 		// Read a frame
-		if(!in.readFrame(f, window)) return false;
+		if(!in.readFrame(f)) return false;
 		// Check that the length is legal
 		int length = f.getLength();
-		if(length < headerLength + MAC_LENGTH)
-			throw new InvalidDataException();
-		if(length > maxFrameLength) throw new InvalidDataException();
+		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
+			throw new FormatException();
+		if(length > MAX_FRAME_LENGTH) throw new FormatException();
 		// 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 != headerLength + payload + padding + MAC_LENGTH)
-			throw new InvalidDataException();
+		if(length != FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH)
+			throw new FormatException();
 		// Check that the padding is all zeroes
-		int paddingStart = headerLength + payload;
+		int paddingStart = FRAME_HEADER_LENGTH + payload;
 		for(int i = paddingStart; i < paddingStart + padding; i++) {
-			if(buf[i] != 0) throw new InvalidDataException();
+			if(buf[i] != 0) throw new FormatException();
 		}
 		// Verify the MAC
-		int macStart = headerLength + payload + padding;
+		int macStart = FRAME_HEADER_LENGTH + payload + padding;
 		mac.update(buf, 0, macStart);
 		byte[] expectedMac = mac.doFinal();
 		for(int i = 0; i < expectedMac.length; i++) {
-			if(expectedMac[i] != buf[macStart + i])
-				throw new InvalidDataException();
+			if(expectedMac[i] != buf[macStart + i]) throw new FormatException();
 		}
 		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
deleted file mode 100644
index 08e903a28b6ed1810eb952c8041f80e1fe5c1f85..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingEncryptionLayer.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-import net.sf.briar.api.transport.Segment;
-
-interface IncomingEncryptionLayer {
-
-	/**
-	 * Reads a segment, excluding its tag, into the given buffer. Returns false
-	 * if no more segments can be read from the connection.
-	 * @throws IOException if an unrecoverable error occurs and the connection
-	 * must be closed.
-	 * @throws InvalidDataException if a recoverable error occurs. The caller
-	 * 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 80e5c66beaa9f218f352d1a3853f8cd362993a8a..ef8e1f61e0956ffa9b871c92e9966ab15bfb1e0d 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
@@ -1,10 +1,8 @@
 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;
-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;
@@ -17,86 +15,70 @@ import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.transport.Segment;
 
-class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
+class IncomingEncryptionLayerImpl implements FrameReader {
 
 	private final InputStream in;
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-	private final boolean tagEverySegment;
-	private final int headerLength, blockSize;
+	private final Cipher tagCipher, frameCipher;
+	private final ErasableKey tagKey, frameKey;
+	private final int blockSize;
 	private final byte[] iv, ciphertext;
 
-	private byte[] bufferedTag;
-	private boolean firstSegment = true;
-	private long segmentNumber = 0L;
+	private boolean readTag;
+	private long frameNumber;
 
 	IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
-			Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
-			boolean tagEverySegment, boolean ackHeader, byte[] bufferedTag) {
+			Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
+			boolean readTag) {
 		this.in = in;
 		this.tagCipher = tagCipher;
-		this.segCipher = segCipher;
+		this.frameCipher = frameCipher;
 		this.tagKey = tagKey;
-		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();
+		this.frameKey = frameKey;
+		this.readTag = readTag;
+		blockSize = frameCipher.getBlockSize();
 		if(blockSize < FRAME_HEADER_LENGTH)
 			throw new IllegalArgumentException();
 		iv = IvEncoder.encodeIv(0L, blockSize);
-		ciphertext = new byte[MAX_SEGMENT_LENGTH];
+		ciphertext = new byte[MAX_FRAME_LENGTH];
+		frameNumber = 0L;
 	}
 
-	public boolean readSegment(Segment s) throws IOException {
-		boolean expectTag = tagEverySegment || firstSegment;
-		firstSegment = false;
+	public boolean readFrame(Frame f) throws IOException {
 		try {
-			if(expectTag) {
-				// Read the tag if we don't have one buffered
-				if(bufferedTag == null) {
-					int offset = 0;
-					while(offset < TAG_LENGTH) {
-						int read = in.read(ciphertext, offset,
-								TAG_LENGTH - offset);
-						if(read == -1) {
-							if(offset == 0) return false;
-							throw new EOFException();
-						}
-						offset += read;
+			// Read the tag if it hasn't already been read
+			if(readTag) {
+				int offset = 0;
+				while(offset < TAG_LENGTH) {
+					int read = in.read(ciphertext, offset,
+							TAG_LENGTH - offset);
+					if(read == -1) {
+						if(offset == 0) return false;
+						throw new EOFException();
 					}
-					long seg = TagEncoder.decodeTag(ciphertext, tagCipher,
-							tagKey);
-					if(seg == -1) throw new FormatException();
-					segmentNumber = seg;
-				} else {
-					long seg = TagEncoder.decodeTag(bufferedTag, tagCipher,
-							tagKey);
-					bufferedTag = null;
-					if(seg == -1) throw new FormatException();
-					segmentNumber = seg;
+					offset += read;
 				}
+				if(!TagEncoder.decodeTag(ciphertext, tagCipher, tagKey))
+					throw new FormatException();
 			}
 			// Read the first block of the frame
 			int offset = 0;
 			while(offset < blockSize) {
 				int read = in.read(ciphertext, offset, blockSize - offset);
 				if(read == -1) {
-					if(offset == 0 && !expectTag) return false;
+					if(offset == 0 && !readTag) return false;
 					throw new EOFException();
 				}
 				offset += read;
 			}
+			readTag = false;
 			// Decrypt the first block of the frame
-			byte[] plaintext = s.getBuffer();
+			byte[] plaintext = f.getBuffer();
 			try {
-				IvEncoder.updateIv(iv, segmentNumber);
+				IvEncoder.updateIv(iv, frameNumber);
 				IvParameterSpec ivSpec = new IvParameterSpec(iv);
-				segCipher.init(Cipher.DECRYPT_MODE, segKey, ivSpec);
-				int decrypted = segCipher.update(ciphertext, 0, blockSize,
+				frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
+				int decrypted = frameCipher.update(ciphertext, 0, blockSize,
 						plaintext);
 				if(decrypted != blockSize) throw new RuntimeException();
 			} catch(GeneralSecurityException badCipher) {
@@ -105,7 +87,7 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 			// Parse the frame header
 			int payload = HeaderEncoder.getPayloadLength(plaintext);
 			int padding = HeaderEncoder.getPaddingLength(plaintext);
-			int length = headerLength + payload + padding + MAC_LENGTH;
+			int length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
 			if(length > MAX_FRAME_LENGTH) throw new FormatException();
 			// Read the remainder of the frame
 			while(offset < length) {
@@ -115,24 +97,20 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 			}
 			// Decrypt the remainder of the frame
 			try {
-				int decrypted = segCipher.doFinal(ciphertext, blockSize,
+				int decrypted = frameCipher.doFinal(ciphertext, blockSize,
 						length - blockSize, plaintext, blockSize);
 				if(decrypted != length - blockSize)
 					throw new RuntimeException();
 			} catch(GeneralSecurityException badCipher) {
 				throw new RuntimeException(badCipher);
 			}
-			s.setLength(length);
-			s.setSegmentNumber(segmentNumber++);
+			f.setLength(length);
+			frameNumber++;
 			return true;
 		} catch(IOException e) {
-			segKey.erase();
+			frameKey.erase();
 			tagKey.erase();
 			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
deleted file mode 100644
index f909dcd606b0a46957643456e4da05a52e39138e..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingErrorCorrectionLayer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-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.
-	 * @throws IOException if an unrecoverable error occurs and the connection
-	 * must be closed.
-	 * @throws InvalidDataException if a recoverable error occurs. The caller
-	 * may choose whether to retry the read or close the connection.
-	 */
-	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
deleted file mode 100644
index ff1d2f89524966fdff4012288997b372b992a454..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingErrorCorrectionLayerImpl.java
+++ /dev/null
@@ -1,101 +0,0 @@
-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;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import net.sf.briar.api.FormatException;
-import net.sf.briar.api.transport.Segment;
-
-class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
-
-	private final IncomingEncryptionLayer in;
-	private final ErasureDecoder decoder;
-	private final int n, k, maxSegmentLength, maxFrameLength;
-	private final Map<Long, Integer> discardCounts;
-	private final Map<Long, Segment[]> segmentSets;
-	private final ArrayList<Segment> freeSegments;
-
-	IncomingErrorCorrectionLayerImpl(IncomingEncryptionLayer in,
-			ErasureDecoder decoder, int n, int k) {
-		this.in = in;
-		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>();
-	}
-
-	public boolean readFrame(Frame f, FrameWindow window) throws IOException,
-	InvalidDataException {
-		// Free any segment sets that have been removed from the window
-		Iterator<Entry<Long, Segment[]>> it = segmentSets.entrySet().iterator();
-		while(it.hasNext()) {
-			Entry<Long, Segment[]> e = it.next();
-			if(!window.contains(e.getKey())) {
-				it.remove();
-				for(Segment s : e.getValue()) if(s != null) freeSegments.add(s);
-			}
-		}
-		// 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();
-		// Grab a free segment, or allocate one if necessary
-		Segment s;
-		int free = freeSegments.size();
-		if(free == 0) s = new SegmentImpl(maxSegmentLength);
-		else s = freeSegments.remove(free - 1);
-		// Read segments until a frame can be decoded
-		while(true) {
-			// Read segments until a segment in the window is returned
-			long frameNumber;
-			while(true) {
-				if(!in.readSegment(s)) {
-					freeSegments.add(s);
-					return false;
-				}
-				frameNumber = s.getSegmentNumber() / n;
-				if(window.contains(frameNumber)) break;
-				if(window.isTooHigh(frameNumber)) countDiscard(frameNumber);
-			}
-			// Add the segment to its set, creating a set if necessary
-			Segment[] set = segmentSets.get(frameNumber);
-			if(set == null) {
-				set = new Segment[n];
-				segmentSets.put(frameNumber, set);
-			}
-			set[(int) (frameNumber % n)] = s;
-			// Try to decode the frame
-			if(decoder.decodeFrame(f, set)) return true;
-		}
-	}
-
-	public int getMaxFrameLength() {
-		return maxFrameLength;
-	}
-
-	private void countDiscard(long frameNumber) throws FormatException {
-		Integer count = discardCounts.get(frameNumber);
-		if(count == null) discardCounts.put(frameNumber, 1);
-		else if(count == n - k) throw new FormatException();
-		else discardCounts.put(frameNumber, count + 1);
-	}
-
-	// Only for testing
-	Map<Long, Segment[]> getSegmentSets() {
-		return segmentSets;
-	}
-
-	// Only for testing
-	Map<Long, Integer> getDiscardCounts() {
-		return discardCounts;
-	}
-}
diff --git a/components/net/sf/briar/transport/IncomingReliabilityLayer.java b/components/net/sf/briar/transport/IncomingReliabilityLayer.java
deleted file mode 100644
index 01525c0e4bed2714e727769d8513860fb36dc382..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingReliabilityLayer.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-interface IncomingReliabilityLayer {
-
-	/**
-	 * Reads and returns a frame, possibly using the given buffer. Returns null
-	 * if no more frames can be read from the connection.
-	 * @throws IOException if an unrecoverable error occurs and the connection
-	 * must be closed.
-	 * @throws InvalidDataException if a recoverable error occurs. The caller
-	 * may choose whether to retry the read or close the connection.
-	 */
-	Frame 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/IncomingReliabilityLayerImpl.java b/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java
deleted file mode 100644
index 5ed04342e19e9b27d3918cfc79c2da76cdcce1a9..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-/** A reliability layer that reorders out-of-order frames. */
-class IncomingReliabilityLayerImpl implements IncomingReliabilityLayer {
-
-	private final IncomingAuthenticationLayer in;
-	private final int maxFrameLength;
-	private final FrameWindow window;
-	private final SortedMap<Long, Frame> frames;
-	private final ArrayList<Frame> freeFrames;
-
-	private long nextFrameNumber = 0L;
-
-	IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in) {
-		this.in = in;
-		maxFrameLength = in.getMaxFrameLength();
-		window = new FrameWindowImpl();
-		frames = new TreeMap<Long, Frame>();
-		freeFrames = new ArrayList<Frame>();
-	}
-
-	public Frame readFrame(Frame f) throws IOException,
-	InvalidDataException {
-		freeFrames.add(f);
-		// Read frames until there's an in-order frame to return
-		while(frames.isEmpty() || frames.firstKey() > nextFrameNumber) {
-			// Grab a free frame, or allocate one if necessary
-			int free = freeFrames.size();
-			if(free == 0) f = new Frame(maxFrameLength);
-			else f = freeFrames.remove(free - 1);
-			// Read a frame
-			if(!in.readFrame(f, window)) return null;
-			// If the frame is in order, return it
-			long frameNumber = f.getFrameNumber();
-			if(frameNumber == nextFrameNumber) {
-				if(!window.remove(nextFrameNumber))
-					throw new IllegalStateException();
-				nextFrameNumber++;
-				return f;
-			}
-			// Insert the frame into the map
-			frames.put(frameNumber, f);
-		}
-		if(!window.remove(nextFrameNumber)) throw new IllegalStateException();
-		nextFrameNumber++;
-		return frames.remove(frames.firstKey());
-	}
-
-	public int getMaxFrameLength() {
-		return maxFrameLength;
-	}
-
-	// Only for testing
-	public int getFreeFramesCount() {
-		return freeFrames.size();
-	}
-}
diff --git a/components/net/sf/briar/transport/InvalidDataException.java b/components/net/sf/briar/transport/InvalidDataException.java
deleted file mode 100644
index 34580d4340feeb2239993105f8c02e8b859b02a2..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/InvalidDataException.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.sf.briar.transport;
-
-/** An exception that indicates a recoverable formatting error. */
-class InvalidDataException extends Exception {
-
-	private static final long serialVersionUID = 4455775710413826953L;
-}
diff --git a/components/net/sf/briar/transport/NullFrameWindow.java b/components/net/sf/briar/transport/NullFrameWindow.java
deleted file mode 100644
index 9f5fb19f7145b800fb871b31b5460472fd5c8621..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/NullFrameWindow.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
-
-/** A frame window that does not allow any reordering. */
-class NullFrameWindow implements FrameWindow {
-
-	private long base = 0L;
-
-	public boolean isTooHigh(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		return frameNumber != base;
-	}
-
-	public boolean contains(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		return frameNumber == base;
-	}
-
-	public boolean remove(long frameNumber) {
-		if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		if(frameNumber != base) return false;
-		base++;
-		return true;
-	}
-}
diff --git a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
deleted file mode 100644
index 1e5077d3c139d7377bd93ec8e7d620b699064f0d..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-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;
-		maxFrameLength = in.getMaxSegmentLength();
-		segment = new SegmentImpl(maxFrameLength);
-	}
-
-	public boolean readFrame(Frame f, FrameWindow window) throws IOException,
-	InvalidDataException {
-		while(true) {
-			if(!in.readSegment(segment)) return false;
-			byte[] buf = segment.getBuffer();
-			long frameNumber = HeaderEncoder.getFrameNumber(buf);
-			if(window.contains(frameNumber)) break;
-		}
-		int length = segment.getLength();
-		// FIXME: Unnecessary copy
-		System.arraycopy(segment.getBuffer(), 0, f.getBuffer(), 0, length);
-		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
deleted file mode 100644
index 194df4f4bf1517fb41870ab0afde64cde7a25450..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.sf.briar.transport;
-
-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();
-	}
-
-	public Frame readFrame(Frame f) throws IOException, InvalidDataException {
-		if(!in.readFrame(f, window)) return null;
-		if(!window.remove(f.getFrameNumber()))
-			throw new IllegalStateException();
-		return f;
-	}
-
-	public int getMaxFrameLength() {
-		return maxFrameLength;
-	}
-}
diff --git a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
deleted file mode 100644
index 6847697f9a59e0231dbd495a0652b8ed335d57d3..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
-
-import java.io.IOException;
-
-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;
-		maxSegmentLength = out.getMaxSegmentLength();
-		segment = new SegmentImpl(maxSegmentLength);
-	}
-
-	public void writeFrame(Frame f) throws IOException {
-		if(segmentNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalStateException();
-		int length = f.getLength();
-		// FIXME: Unnecessary copy
-		System.arraycopy(f.getBuffer(), 0, segment.getBuffer(), 0, length);
-		segment.setLength(length);
-		segment.setSegmentNumber(segmentNumber++);
-		out.writeSegment(segment);
-	}
-
-	public void flush() throws IOException {
-		out.flush();
-	}
-
-	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
deleted file mode 100644
index 650f245e54c0e9b84ae8119d62ce8329138a598d..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/NullOutgoingReliabilityLayer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package net.sf.briar.transport;
-
-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 {
-		out.writeFrame(f);
-	}
-
-	public void flush() throws IOException {
-		out.flush();
-	}
-
-	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
deleted file mode 100644
index 21e41a9243aed1bbc5cd58444c61b2d634d83664..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/OutgoingAuthenticationLayer.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-interface OutgoingAuthenticationLayer {
-
-	/** Writes the given frame. */
-	void writeFrame(Frame f) throws IOException;
-
-	/** Flushes the stack. */
-	void flush() throws IOException;
-
-	/** 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 535ec4834a2a0af64d12da6616527b657f8d5ea7..2e3798dc36b54560f73872578b9eef8852e07536 100644
--- a/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingAuthenticationLayerImpl.java
@@ -10,13 +10,12 @@ import javax.crypto.ShortBufferException;
 
 import net.sf.briar.api.crypto.ErasableKey;
 
-class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
+class OutgoingAuthenticationLayerImpl implements FrameWriter {
 
-	private final OutgoingErrorCorrectionLayer out;
+	private final FrameWriter out;
 	private final Mac mac;
-	private final int maxFrameLength;
 
-	OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac,
+	OutgoingAuthenticationLayerImpl(FrameWriter out, Mac mac,
 			ErasableKey macKey) {
 		this.out = out;
 		this.mac = mac;
@@ -28,7 +27,6 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
 		macKey.erase();
 		if(mac.getMacLength() != MAC_LENGTH)
 			throw new IllegalArgumentException();
-		maxFrameLength = out.getMaxFrameLength();
 	}
 
 	public void writeFrame(Frame f) throws IOException {
@@ -50,8 +48,4 @@ 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
deleted file mode 100644
index e0e4b44023ae29e67cc6d3776dcb6a2397f0a5e6..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-import net.sf.briar.api.transport.Segment;
-
-interface OutgoingEncryptionLayer {
-
-	/** Writes the given segment. */
-	void writeSegment(Segment s) throws IOException;
-
-	/** Flushes the stack. */
-	void flush() throws IOException;
-
-	/** 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 6e9696b2060aef6f545b2bee480e61a29a9eef4f..f79b6831d6c4f9642287f2c803973e2212a4eb98 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
@@ -1,6 +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.MAX_FRAME_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 
 import java.io.IOException;
@@ -11,46 +11,43 @@ import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.transport.Segment;
 
-class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
+class OutgoingEncryptionLayerImpl implements FrameWriter {
 
 	private final OutputStream out;
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-	private final boolean tagEverySegment;
+	private final Cipher tagCipher, frameCipher;
+	private final ErasableKey tagKey, frameKey;
 	private final byte[] iv, ciphertext;
 
-	private long capacity;
+	private long capacity, frameNumber;
 
 	OutgoingEncryptionLayerImpl(OutputStream out, long capacity,
-			Cipher tagCipher, Cipher segCipher, ErasableKey tagKey,
-			ErasableKey segKey, boolean tagEverySegment) {
+			Cipher tagCipher, Cipher frameCipher, ErasableKey tagKey,
+			ErasableKey frameKey) {
 		this.out = out;
 		this.capacity = capacity;
 		this.tagCipher = tagCipher;
-		this.segCipher = segCipher;
+		this.frameCipher = frameCipher;
 		this.tagKey = tagKey;
-		this.segKey = segKey;
-		this.tagEverySegment = tagEverySegment;
-		iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
-		ciphertext = new byte[MAX_SEGMENT_LENGTH];
+		this.frameKey = frameKey;
+		iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
+		ciphertext = new byte[TAG_LENGTH + MAX_FRAME_LENGTH];
+		frameNumber = 0L;
 	}
 
-	public void writeSegment(Segment s) throws IOException {
-		byte[] plaintext = s.getBuffer();
-		int length = s.getLength();
-		long segmentNumber = s.getSegmentNumber();
+	public void writeFrame(Frame f) throws IOException {
+		byte[] plaintext = f.getBuffer();
+		int length = f.getLength();
 		int offset = 0;
-		if(tagEverySegment || segmentNumber == 0) {
-			TagEncoder.encodeTag(ciphertext, segmentNumber, tagCipher, tagKey);
+		if(frameNumber == 0) {
+			TagEncoder.encodeTag(ciphertext, tagCipher, tagKey);
 			offset = TAG_LENGTH;
 		}
-		IvEncoder.updateIv(iv, segmentNumber);
+		IvEncoder.updateIv(iv, frameNumber);
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
 		try {
-			segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-			int encrypted = segCipher.doFinal(plaintext, 0, length,
+			frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+			int encrypted = frameCipher.doFinal(plaintext, 0, length,
 					ciphertext, offset);
 			if(encrypted != length) throw new RuntimeException();
 		} catch(GeneralSecurityException badCipher) {
@@ -59,11 +56,12 @@ class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
 		try {
 			out.write(ciphertext, 0, offset + length);
 		} catch(IOException e) {
-			segKey.erase();
+			frameKey.erase();
 			tagKey.erase();
 			throw e;
 		}
 		capacity -= offset + length;
+		frameNumber++;
 	}
 
 	public void flush() throws IOException {
@@ -73,8 +71,4 @@ 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
deleted file mode 100644
index 9dfd9eac37647014bb66dc871a41eedd18f5dffb..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayer.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-
-interface OutgoingErrorCorrectionLayer {
-
-	/** Writes the given frame. */
-	void writeFrame(Frame f) throws IOException;
-
-	/** Flushes the stack. */
-	void flush() throws IOException;
-
-	/** 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
deleted file mode 100644
index e7662d002e123b8b9cecefacdf5c43e754f12c11..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/OutgoingErrorCorrectionLayerImpl.java
+++ /dev/null
@@ -1,39 +0,0 @@
-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;
-
-class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
-
-	private final OutgoingEncryptionLayer out;
-	private final ErasureEncoder encoder;
-	private final int n, maxFrameLength;
-
-	OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out,
-			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 {
-		for(Segment s : encoder.encodeFrame(f)) out.writeSegment(s);
-	}
-
-	public void flush() throws IOException {
-		out.flush();
-	}
-
-	public long getRemainingCapacity() {
-		return out.getRemainingCapacity() / n;
-	}
-
-	public int getMaxFrameLength() {
-		return maxFrameLength;
-	}
-}
diff --git a/components/net/sf/briar/transport/SegmentImpl.java b/components/net/sf/briar/transport/SegmentImpl.java
deleted file mode 100644
index a62c41e8343bdd170c8d690f9d92a7c950e5e344..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/SegmentImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-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;
-
-class SegmentImpl implements Segment {
-
-	private final byte[] buf;
-
-	private int length = -1;
-	private long segmentNumber = -1;
-
-	SegmentImpl() {
-		this(MAX_SEGMENT_LENGTH);
-	}
-
-	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];
-	}
-
-	public byte[] getBuffer() {
-		return buf;
-	}
-
-	public int getLength() {
-		if(length == -1) throw new IllegalStateException();
-		return length;
-	}
-
-	public long getSegmentNumber() {
-		if(segmentNumber == -1) throw new IllegalStateException();
-		return segmentNumber;
-	}
-
-	public void setLength(int length) {
-		if(length < FRAME_HEADER_LENGTH + MAC_LENGTH || length > buf.length)
-			throw new IllegalArgumentException();
-		this.length = length;
-	}
-
-	public void setSegmentNumber(long segmentNumber) {
-		if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		this.segmentNumber = segmentNumber;
-	}
-}
diff --git a/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java b/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java
deleted file mode 100644
index 0316dd4832c6ca50f5a6c975fb9fe474c2647b2c..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/SegmentedIncomingEncryptionLayer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
-import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-
-import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSource;
-import net.sf.briar.api.transport.Segment;
-
-class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
-
-	private final SegmentSource in;
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-	private final boolean tagEverySegment;
-	private final int blockSize, headerLength, maxSegmentLength;
-	private final Segment segment;
-	private final byte[] iv;
-
-	private Segment bufferedSegment;
-	private boolean firstSegment = true;
-	private long segmentNumber = 0L;
-
-	SegmentedIncomingEncryptionLayer(SegmentSource in, Cipher tagCipher,
-			Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
-			boolean tagEverySegment, boolean ackHeader,
-			Segment bufferedSegment) {
-		this.in = in;
-		this.tagCipher = tagCipher;
-		this.segCipher = segCipher;
-		this.tagKey = tagKey;
-		this.segKey = segKey;
-		this.tagEverySegment = tagEverySegment;
-		this.bufferedSegment = bufferedSegment;
-		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 + headerLength + 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);
-	}
-
-	public boolean readSegment(Segment s) throws IOException,
-	InvalidDataException {
-		boolean expectTag = tagEverySegment || firstSegment;
-		firstSegment = false;
-		try {
-			// Read the segment, unless we have one buffered
-			Segment segment;
-			if(bufferedSegment == null) {
-				segment = this.segment;
-				if(!in.readSegment(segment)) return false;
-			} else {
-				segment = bufferedSegment;
-				bufferedSegment = null;
-			}
-			int offset = expectTag ? TAG_LENGTH : 0;
-			int length = segment.getLength();
-			if(length < offset + headerLength + 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) {
-				long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
-				if(seg == -1) throw new InvalidDataException();
-				segmentNumber = seg;
-			}
-			// Decrypt the segment
-			try {
-				IvEncoder.updateIv(iv, segmentNumber);
-				IvParameterSpec ivSpec = new IvParameterSpec(iv);
-				segCipher.init(Cipher.DECRYPT_MODE, segKey, ivSpec);
-				int decrypted = segCipher.doFinal(ciphertext, offset,
-						length - offset, s.getBuffer());
-				if(decrypted != length - offset) throw new RuntimeException();
-			} catch(GeneralSecurityException badCipher) {
-				throw new RuntimeException(badCipher);
-			}
-			s.setLength(length - offset);
-			s.setSegmentNumber(segmentNumber++);
-			return true;
-		} catch(IOException e) {
-			segKey.erase();
-			tagKey.erase();
-			throw e;
-		}
-	}
-
-	public int getMaxSegmentLength() {
-		return maxSegmentLength;
-	}
-}
diff --git a/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java b/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java
deleted file mode 100644
index 8eb1c0a683ee9259d75db6f827c7ecff26b8a704..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/SegmentedOutgoingEncryptionLayer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-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;
-import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-
-import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSink;
-import net.sf.briar.api.transport.Segment;
-
-class SegmentedOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
-
-	private final SegmentSink out;
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-	private final boolean tagEverySegment;
-	private final int headerLength, maxSegmentLength;
-	private final Segment segment;
-	private final byte[] iv;
-
-	private long capacity;
-
-	SegmentedOutgoingEncryptionLayer(SegmentSink out, long capacity,
-			Cipher tagCipher, Cipher segCipher, ErasableKey tagKey,
-			ErasableKey segKey, boolean tagEverySegment, boolean ackHeader) {
-		this.out = out;
-		this.capacity = capacity;
-		this.tagCipher = tagCipher;
-		this.segCipher = segCipher;
-		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 + 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());
-	}
-
-	public void writeSegment(Segment s) throws IOException {
-		byte[] plaintext = s.getBuffer(), ciphertext = segment.getBuffer();
-		int length = s.getLength();
-		long segmentNumber = s.getSegmentNumber();
-		int offset = 0;
-		if(tagEverySegment || segmentNumber == 0) {
-			TagEncoder.encodeTag(ciphertext, segmentNumber, tagCipher, tagKey);
-			offset = TAG_LENGTH;
-		}
-		IvEncoder.updateIv(iv, segmentNumber);
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		try {
-			segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-			int encrypted = segCipher.doFinal(plaintext, 0, length,
-					ciphertext, offset);
-			if(encrypted != length) throw new RuntimeException();
-		} catch(GeneralSecurityException badCipher) {
-			throw new RuntimeException(badCipher);
-		}
-		segment.setLength(offset + length);
-		try {
-			out.writeSegment(segment);
-		} catch(IOException e) {
-			segKey.erase();
-			tagKey.erase();
-			throw e;
-		}
-		capacity -= offset + length;
-	}
-
-	public void flush() throws IOException {}
-
-	public long getRemainingCapacity() {
-		return capacity;
-	}
-
-	public int getMaxSegmentLength() {
-		return maxSegmentLength;
-	}
-}
\ No newline at end of file
diff --git a/components/net/sf/briar/transport/TagEncoder.java b/components/net/sf/briar/transport/TagEncoder.java
index e7a223f2a3cee537515e093c8e7dc5400b56d9f7..dca0c60debee9c91edb0fdfa4ce0c5d3a2d1e988 100644
--- a/components/net/sf/briar/transport/TagEncoder.java
+++ b/components/net/sf/briar/transport/TagEncoder.java
@@ -1,26 +1,19 @@
 package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.security.GeneralSecurityException;
 
 import javax.crypto.Cipher;
 
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.util.ByteUtils;
 
 class TagEncoder {
 
-	static void encodeTag(byte[] tag, long segmentNumber, Cipher tagCipher,
-			ErasableKey tagKey) {
+	static void encodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
 		if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
-		if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		// Clear the tag
+		// Blank plaintext
 		for(int i = 0; i < TAG_LENGTH; i++) tag[i] = 0;
-		// Encode the segment number as a uint32 at the end of the tag
-		ByteUtils.writeUint32(segmentNumber, tag, TAG_LENGTH - 4);
 		try {
 			tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
 			int encrypted = tagCipher.doFinal(tag, 0, TAG_LENGTH, tag);
@@ -31,18 +24,18 @@ class TagEncoder {
 		}
 	}
 
-	static long decodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
+	static boolean decodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
 		if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
 		try {
 			tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
 			byte[] plaintext = tagCipher.doFinal(tag, 0, TAG_LENGTH);
 			if(plaintext.length != TAG_LENGTH)
 				throw new IllegalArgumentException();
-			// All but the last four bytes of the plaintext should be blank
-			for(int i = 0; i < TAG_LENGTH - 4; i++) {
-				if(plaintext[i] != 0) return -1;
+			//The plaintext should be blank
+			for(int i = 0; i < TAG_LENGTH; i++) {
+				if(plaintext[i] != 0) return false;
 			}
-			return ByteUtils.readUint32(plaintext, TAG_LENGTH - 4);
+			return true;
 		} catch(GeneralSecurityException e) {
 			// Unsuitable cipher or key
 			throw new IllegalArgumentException(e);
diff --git a/components/net/sf/briar/transport/XorErasureDecoder.java b/components/net/sf/briar/transport/XorErasureDecoder.java
deleted file mode 100644
index 4ae174bb8a3cda8c934716db6fcf246250577f92..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/XorErasureDecoder.java
+++ /dev/null
@@ -1,75 +0,0 @@
-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;
-import net.sf.briar.api.FormatException;
-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, headerLength;
-
-	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 {
-		// We need at least n - 1 pieces
-		int pieces = 0;
-		for(int i = 0; i < n; i++) if(set[i] != null) pieces++;
-		if(pieces < n - 1) return false;
-		// All the pieces must have the same length - take the minimum
-		int length = MAX_FRAME_LENGTH;
-		for(int i = 0; i < n; i++) {
-			if(set[i] != null) {
-				int len = set[i].getLength();
-				if(len < length) length = len;
-			}
-		}
-		if(length * (n - 1) > MAX_FRAME_LENGTH) throw new FormatException();
-		// Decode the frame
-		byte[] dest = f.getBuffer();
-		int offset = 0;
-		if(pieces == n || set[n - 1] == null) {
-			// We don't need no stinkin' parity segment
-			for(int i = 0; i < n - 1; i++) {
-				byte[] src = set[i].getBuffer();
-				int copyLength = Math.min(length, dest.length - offset);
-				System.arraycopy(src, 0, dest, offset, copyLength);
-				offset += length;
-			}
-		} else {
-			// Reconstruct the missing segment
-			byte[] parity = new byte[length];
-			int missingOffset = -1;
-			for(int i = 0; i < n - 1; i++) {
-				if(set[i] == null) {
-					missingOffset = offset;
-				} else {
-					byte[] src = set[i].getBuffer();
-					for(int j = 0; j < length; j++) parity[j] ^= src[j];
-					int copyLength = Math.min(length, dest.length - offset);
-					System.arraycopy(src, 0, dest, offset, copyLength);
-				}
-				offset += length;
-			}
-			byte[] src = set[n - 1].getBuffer();
-			for(int i = 0; i < length; i++) parity[i] ^= src[i];
-			assert missingOffset != -1;
-			int copyLength = Math.min(length, dest.length - missingOffset);
-			System.arraycopy(parity, 0, dest, missingOffset, copyLength);
-		}
-		// 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 = headerLength + 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
deleted file mode 100644
index 220f679e38b92968cb902a5a709944430a05402b..0000000000000000000000000000000000000000
--- a/components/net/sf/briar/transport/XorErasureEncoder.java
+++ /dev/null
@@ -1,31 +0,0 @@
-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++) {
-			int copyLength = Math.min(length, src.length - offset);
-			System.arraycopy(src, offset, set[i].getBuffer(), 0, copyLength);
-			for(int j = 0; j < copyLength; j++) parity[j] ^= src[offset + j];
-			offset += length;
-		}
-		return set;
-	}
-}
diff --git a/test/build.xml b/test/build.xml
index 77541769ffe5425e41d192557f174ee53c8f9809..c28327848121a228f899cc8ce9d502f77410e3e6 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -56,16 +56,8 @@
 			<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
 			<test name='net.sf.briar.transport.ConnectionWriterTest'/>
 			<test name='net.sf.briar.transport.FrameReadWriteTest'/>
-			<test name='net.sf.briar.transport.FrameWindowImplTest'/>
 			<test name='net.sf.briar.transport.IncomingEncryptionLayerImplTest'/>
-			<test name='net.sf.briar.transport.IncomingErrorCorrectionLayerImplTest'/>
-			<test name='net.sf.briar.transport.IncomingReliabilityLayerImplTest'/>
 			<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
-			<test name='net.sf.briar.transport.SegmentedIncomingEncryptionLayerTest'/>
-			<test name='net.sf.briar.transport.SegmentedOutgoingEncryptionLayerTest'/>
-			<test name='net.sf.briar.transport.XorErasureCodeTest'/>
-			<test name='net.sf.briar.transport.XorErasureDecoderTest'/>
-			<test name='net.sf.briar.transport.XorErasureEncoderTest'/>
 			<test name='net.sf.briar.util.ByteUtilsTest'/>
 			<test name='net.sf.briar.util.FileUtilsTest'/>
 			<test name='net.sf.briar.util.StringUtilsTest'/>
diff --git a/test/net/sf/briar/ProtocolIntegrationTest.java b/test/net/sf/briar/ProtocolIntegrationTest.java
index db5f968cef7d1439b74b704942876f2dc069c6d2..1d313a3de50add697ec01b585d55af96f564cdac 100644
--- a/test/net/sf/briar/ProtocolIntegrationTest.java
+++ b/test/net/sf/briar/ProtocolIntegrationTest.java
@@ -189,7 +189,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 		byte[] tag = new byte[TAG_LENGTH];
 		assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
 		ConnectionReader conn = connectionReaderFactory.createConnectionReader(
-				in, secret.clone(), tag);
+				in, secret.clone(), true);
 		InputStream in1 = conn.getInputStream();
 		ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);
 
diff --git a/test/net/sf/briar/crypto/KeyDerivationTest.java b/test/net/sf/briar/crypto/KeyDerivationTest.java
index 3823291e73070b5466a67eed5b011ae36d129e78..b777cd7921491bafd8a483db0a65d9b8bec5b803 100644
--- a/test/net/sf/briar/crypto/KeyDerivationTest.java
+++ b/test/net/sf/briar/crypto/KeyDerivationTest.java
@@ -27,8 +27,8 @@ public class KeyDerivationTest extends BriarTestCase {
 	@Test
 	public void testSixKeysAreDistinct() {
 		List<ErasableKey> keys = new ArrayList<ErasableKey>();
-		keys.add(crypto.deriveSegmentKey(secret, true));
-		keys.add(crypto.deriveSegmentKey(secret, false));
+		keys.add(crypto.deriveFrameKey(secret, true));
+		keys.add(crypto.deriveFrameKey(secret, false));
 		keys.add(crypto.deriveTagKey(secret, true));
 		keys.add(crypto.deriveTagKey(secret, false));
 		keys.add(crypto.deriveMacKey(secret, true));
diff --git a/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java b/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java
index 7c551d09b5b97cca8249239ed0b56c78189c34a4..59bc557661594046317524462ba06f8c46730ec1 100644
--- a/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java
+++ b/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java
@@ -174,7 +174,7 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase {
 		IncomingSimplexConnection batchIn = new IncomingSimplexConnection(
 				new ImmediateExecutor(), new ImmediateExecutor(), db,
 				connRegistry, connFactory, protoFactory, ctx, transportId,
-				transport, tag);
+				transport);
 		// No messages should have been added yet
 		assertFalse(listener.messagesAdded);
 		// Read whatever needs to be read
diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
index 152ae8b4071c1ad7a2a5dc67ca18670227f58c06..92d431cfa8548ca926fb4820db30ae089bea3ebf 100644
--- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
@@ -15,7 +15,6 @@ import net.sf.briar.api.transport.ConnectionReader;
 import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.junit.Test;
 
-// FIXME: This test covers too many classes
 public class ConnectionReaderImplTest extends TransportTest {
 
 	public ConnectionReaderImplTest() throws Exception {
@@ -217,14 +216,9 @@ public class ConnectionReaderImplTest extends TransportTest {
 	}
 
 	private ConnectionReader createConnectionReader(InputStream in) {
-		IncomingEncryptionLayer encryption =
-			new NullIncomingEncryptionLayer(in);
-		IncomingErrorCorrectionLayer correction =
-			new NullIncomingErrorCorrectionLayer(encryption);
-		IncomingAuthenticationLayer authentication =
-			new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
-		IncomingReliabilityLayer reliability =
-			new NullIncomingReliabilityLayer(authentication);
-		return new ConnectionReaderImpl(reliability, false, false);
+		FrameReader encryption = new NullIncomingEncryptionLayer(in);
+		FrameReader authentication = new IncomingAuthenticationLayerImpl(
+				encryption, mac, macKey);
+		return new ConnectionReaderImpl(authentication);
 	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
index a18f12b5be892295cb88813d2867c5d69e589337..e4cd00d3cab2249b2ef3aec8c25e094c352969c6 100644
--- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
@@ -618,7 +618,7 @@ public class ConnectionRecogniserImplTest extends BriarTestCase {
 		ErasableKey tagKey = crypto.deriveTagKey(secret, true);
 		Cipher tagCipher = crypto.getTagCipher();
 		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
+		TagEncoder.encodeTag(tag, tagCipher, tagKey);
 		return tag;
 	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
index a5e0d5ac6e7fb55981b6c842e5e2e07615f8cb37..335b922204e6f3206b604cc962cd4eafdce66e35 100644
--- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
@@ -12,7 +12,6 @@ import net.sf.briar.api.transport.ConnectionWriter;
 
 import org.junit.Test;
 
-// FIXME: This test covers too many classes
 public class ConnectionWriterImplTest extends TransportTest {
 
 	public ConnectionWriterImplTest() throws Exception {
@@ -107,14 +106,9 @@ public class ConnectionWriterImplTest extends TransportTest {
 	}
 
 	private ConnectionWriter createConnectionWriter(OutputStream out) {
-		OutgoingEncryptionLayer encryption =
-			new NullOutgoingEncryptionLayer(out);
-		OutgoingErrorCorrectionLayer correction =
-			new NullOutgoingErrorCorrectionLayer(encryption);
-		OutgoingAuthenticationLayer authentication =
-			new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
-		OutgoingReliabilityLayer reliability =
-			new NullOutgoingReliabilityLayer(authentication);
-		return new ConnectionWriterImpl(reliability, false);
+		FrameWriter encryption = new NullOutgoingEncryptionLayer(out);
+		FrameWriter authentication =
+			new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
+		return new ConnectionWriterImpl(authentication);
 	}
 }
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index 89f54dd26a0a7468036715eaa2547f7e0c6bf893..62e275873fd2c2ead287fa239ef712161dc72a1a 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -27,25 +27,25 @@ import com.google.inject.Injector;
 public class FrameReadWriteTest extends BriarTestCase {
 
 	private final CryptoComponent crypto;
-	private final Cipher tagCipher, segCipher;
+	private final Cipher tagCipher, frameCipher;
 	private final Mac mac;
 	private final Random random;
 	private final byte[] outSecret;
-	private final ErasableKey tagKey, segKey, macKey;
+	private final ErasableKey tagKey, frameKey, macKey;
 
 	public FrameReadWriteTest() {
 		super();
 		Injector i = Guice.createInjector(new CryptoModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		tagCipher = crypto.getTagCipher();
-		segCipher = crypto.getSegmentCipher();
+		frameCipher = crypto.getFrameCipher();
 		mac = crypto.getMac();
 		random = new Random();
 		// Since we're sending frames to ourselves, we only need outgoing keys
 		outSecret = new byte[32];
 		random.nextBytes(outSecret);
 		tagKey = crypto.deriveTagKey(outSecret, true);
-		segKey = crypto.deriveSegmentKey(outSecret, true);
+		frameKey = crypto.deriveFrameKey(outSecret, true);
 		macKey = crypto.deriveMacKey(outSecret, true);
 	}
 
@@ -62,7 +62,7 @@ public class FrameReadWriteTest extends BriarTestCase {
 	private void testWriteAndRead(boolean initiator) throws Exception {
 		// Encode the tag
 		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
+		TagEncoder.encodeTag(tag, tagCipher, tagKey);
 		// Generate two random frames
 		byte[] frame = new byte[12345];
 		random.nextBytes(frame);
@@ -70,21 +70,15 @@ public class FrameReadWriteTest extends BriarTestCase {
 		random.nextBytes(frame1);
 		// Copy the keys - the copies will be erased
 		ErasableKey tagCopy = tagKey.copy();
-		ErasableKey segCopy = segKey.copy();
+		ErasableKey frameCopy = frameKey.copy();
 		ErasableKey macCopy = macKey.copy();
 		// Write the frames
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		OutgoingEncryptionLayer encryptionOut = new OutgoingEncryptionLayerImpl(
-				out, Long.MAX_VALUE, tagCipher, segCipher, tagCopy, segCopy,
-				false);
-		OutgoingErrorCorrectionLayer correctionOut =
-			new NullOutgoingErrorCorrectionLayer(encryptionOut);
-		OutgoingAuthenticationLayer authenticationOut =
-			new OutgoingAuthenticationLayerImpl(correctionOut, mac, macCopy);
-		OutgoingReliabilityLayer reliabilityOut =
-			new NullOutgoingReliabilityLayer(authenticationOut);
-		ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut,
-				false);
+		FrameWriter encryptionOut = new OutgoingEncryptionLayerImpl(out,
+				Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy);
+		FrameWriter authenticationOut = new OutgoingAuthenticationLayerImpl(
+				encryptionOut, mac, macCopy);
+		ConnectionWriter writer = new ConnectionWriterImpl(authenticationOut);
 		OutputStream out1 = writer.getOutputStream();
 		out1.write(frame);
 		out1.flush();
@@ -95,20 +89,13 @@ public class FrameReadWriteTest extends BriarTestCase {
 		byte[] recoveredTag = new byte[TAG_LENGTH];
 		assertEquals(TAG_LENGTH, in.read(recoveredTag));
 		assertArrayEquals(tag, recoveredTag);
-		assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
+		assertTrue(TagEncoder.decodeTag(tag, tagCipher, tagKey));
 		// Read the frames back
-		IncomingEncryptionLayer encryptionIn = new IncomingEncryptionLayerImpl(
-				in, tagCipher, segCipher, tagKey, segKey, false, false,
-				recoveredTag);
-		IncomingErrorCorrectionLayer correctionIn =
-			new NullIncomingErrorCorrectionLayer(encryptionIn);
-		IncomingAuthenticationLayer authenticationIn =
-			new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey,
-					false);
-		IncomingReliabilityLayer reliabilityIn =
-			new NullIncomingReliabilityLayer(authenticationIn);
-		ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn, false,
-				false);
+		FrameReader encryptionIn = new IncomingEncryptionLayerImpl(in,
+				tagCipher, frameCipher, tagKey, frameKey, false);
+		FrameReader authenticationIn = new IncomingAuthenticationLayerImpl(
+				encryptionIn, mac, macKey);
+		ConnectionReader reader = new ConnectionReaderImpl(authenticationIn);
 		InputStream in1 = reader.getInputStream();
 		byte[] recovered = new byte[frame.length];
 		int offset = 0;
diff --git a/test/net/sf/briar/transport/FrameWindowImplTest.java b/test/net/sf/briar/transport/FrameWindowImplTest.java
deleted file mode 100644
index fc8913befca114f27139d541c02111513e5a8eb5..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/FrameWindowImplTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package net.sf.briar.transport;
-
-import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
-import net.sf.briar.BriarTestCase;
-
-import org.junit.Test;
-
-public class FrameWindowImplTest extends BriarTestCase {
-
-	@Test
-	public void testWindowSliding() {
-		FrameWindow w = new FrameWindowImpl();
-		for(int i = 0; i < 100; i++) {
-			assertTrue(w.contains(i));
-			assertTrue(w.remove(i));
-			assertFalse(w.contains(i));
-		}
-		for(int i = 100; i < 100 + FRAME_WINDOW_SIZE; i++) {
-			assertTrue(w.contains(i));
-			assertFalse(w.isTooHigh(i));
-		}
-		assertFalse(w.contains(100 + FRAME_WINDOW_SIZE));
-		assertTrue(w.isTooHigh(100 + FRAME_WINDOW_SIZE));
-	}
-
-	@Test
-	public void testWindowJumping() {
-		FrameWindow w = new FrameWindowImpl();
-		// Base of the window is 0
-		for(int i = 0; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.contains(i));
-		assertFalse(w.contains(FRAME_WINDOW_SIZE));
-		assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
-		assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
-		// Remove all numbers except 0 and 5
-		for(int i = 1; i < 5; i++) assertTrue(w.remove(i));
-		for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.remove(i));
-		// Base of the window should still be 0
-		assertTrue(w.contains(0));
-		for(int i = 1; i < 5; i++) assertFalse(w.contains(i));
-		assertTrue(w.contains(5));
-		for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
-		assertFalse(w.contains(FRAME_WINDOW_SIZE));
-		assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
-		assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
-		// Remove 0
-		assertTrue(w.remove(0));
-		// Base of the window should now be 5
-		for(int i = 0; i < 5; i++) assertFalse(w.contains(i));
-		assertTrue(w.contains(5));
-		for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
-		for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
-			assertTrue(w.contains(i));
-		}
-		assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
-		assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
-		assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
-		// Remove all numbers except 5
-		for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
-			assertTrue(w.remove(i));
-		}
-		// Base of the window should still be 5
-		assertTrue(w.contains(5));
-		for(int i = 6; i < FRAME_WINDOW_SIZE + 5; i++) {
-			assertFalse(w.contains(i));
-		}
-		assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
-		assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
-		assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
-		// Remove 5
-		assertTrue(w.remove(5));
-		// Base of the window should now be FRAME_WINDOW_SIZE + 5
-		for(int i = 0; i < FRAME_WINDOW_SIZE + 5; i++) {
-			assertFalse(w.contains(i));
-		}
-		for(int i = FRAME_WINDOW_SIZE + 5; i < FRAME_WINDOW_SIZE * 2 + 5; i++) {
-			assertTrue(w.contains(i));
-		}
-		assertFalse(w.contains(FRAME_WINDOW_SIZE * 2 + 5));
-		assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 4));
-		assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 5));
-	}
-}
\ No newline at end of file
diff --git a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
index 0b34ece3bff1842e8229ce427568de023782d141..58c80823aba6b436d462d340697a56daa57d87d7 100644
--- a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
@@ -12,7 +12,6 @@ import javax.crypto.spec.IvParameterSpec;
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -23,112 +22,106 @@ import com.google.inject.Injector;
 
 public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
+	private final Cipher tagCipher, frameCipher;
+	private final ErasableKey tagKey, frameKey;
 
 	public IncomingEncryptionLayerImplTest() {
 		super();
 		Injector i = Guice.createInjector(new CryptoModule());
 		CryptoComponent crypto = i.getInstance(CryptoComponent.class);
 		tagCipher = crypto.getTagCipher();
-		segCipher = crypto.getSegmentCipher();
+		frameCipher = crypto.getFrameCipher();
 		tagKey = crypto.generateTestKey();
-		segKey = crypto.generateTestKey();
+		frameKey = crypto.generateTestKey();
 	}
 
 	@Test
-	public void testDecryptionWithFirstSegmentTagged() throws Exception {
-		// Calculate the tag for the first segment
+	public void testDecryptionWithTag() throws Exception {
+		// Calculate the tag
 		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the ciphertext for the first segment
+		TagEncoder.encodeTag(tag, tagCipher, tagKey);
+		// Calculate the ciphertext for the first frame
 		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
-		byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
+		byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
-		// Calculate the ciphertext for the second segment
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+		// Calculate the ciphertext for the second frame
 		byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
 		IvEncoder.updateIv(iv, 1L);
 		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
 				plaintext1.length);
-		// Concatenate the ciphertexts, excluding the first tag
+		// Concatenate the ciphertexts, including the tag
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		out.write(tag);
 		out.write(ciphertext);
 		out.write(ciphertext1);
 		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 		// Use the encryption layer to decrypt the ciphertext
-		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, segCipher, tagKey, segKey, false, false, tag);
-		// First segment
-		Segment s = new SegmentImpl();
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext.length, s.getLength());
-		assertEquals(0L, s.getSegmentNumber());
-		byte[] decrypted = s.getBuffer();
+		FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
+				frameCipher, tagKey, frameKey, true);
+		// First frame
+		Frame f = new Frame();
+		assertTrue(decrypter.readFrame(f));
+		assertEquals(plaintext.length, f.getLength());
+		byte[] decrypted = f.getBuffer();
+		assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second segment
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext1.length, s.getLength());
-		assertEquals(1L, s.getSegmentNumber());
-		decrypted = s.getBuffer();
+		// Second frame
+		assertTrue(decrypter.readFrame(f));
+		assertEquals(plaintext1.length, f.getLength());
+		decrypted = f.getBuffer();
+		assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
 		for(int i = 0; i < plaintext1.length; i++) {
 			assertEquals(plaintext1[i], decrypted[i]);
 		}
 	}
 
 	@Test
-	public void testDecryptionWithEverySegmentTagged() throws Exception {
-		// Calculate the tag for the first segment
-		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the ciphertext for the first segment
+	public void testDecryptionWithoutTag() throws Exception {
+		// Calculate the ciphertext for the first frame
 		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
-		byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
+		byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
-		// Calculate the tag for the second segment
-		byte[] tag1 = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
-		// Calculate the ciphertext for the second segment
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+		// Calculate the ciphertext for the second frame
 		byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
 		IvEncoder.updateIv(iv, 1L);
 		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
 				plaintext1.length);
-		// Concatenate the ciphertexts, excluding the first tag
+		// Concatenate the ciphertexts, excluding the tag
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		out.write(ciphertext);
-		out.write(tag1);
 		out.write(ciphertext1);
 		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 		// Use the encryption layer to decrypt the ciphertext
-		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, segCipher, tagKey, segKey, true, false, tag);
-		// First segment
-		Segment s = new SegmentImpl();
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext.length, s.getLength());
-		assertEquals(0L, s.getSegmentNumber());
-		byte[] decrypted = s.getBuffer();
+		FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
+				frameCipher, tagKey, frameKey, false);
+		// First frame
+		Frame f = new Frame();
+		assertTrue(decrypter.readFrame(f));
+		assertEquals(plaintext.length, f.getLength());
+		byte[] decrypted = f.getBuffer();
+		assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second segment
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext1.length, s.getLength());
-		assertEquals(1L, s.getSegmentNumber());
-		decrypted = s.getBuffer();
+		// Second frame
+		assertTrue(decrypter.readFrame(f));
+		assertEquals(plaintext1.length, f.getLength());
+		assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
+		decrypted = f.getBuffer();
 		for(int i = 0; i < plaintext1.length; i++) {
 			assertEquals(plaintext1[i], decrypted[i]);
 		}
diff --git a/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java b/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java
deleted file mode 100644
index f6f507bb55aad999f154cbce0374c6142464a9a2..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/IncomingErrorCorrectionLayerImplTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package net.sf.briar.transport;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.Map;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.FormatException;
-import net.sf.briar.api.transport.Segment;
-
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.junit.Test;
-
-public class IncomingErrorCorrectionLayerImplTest extends BriarTestCase {
-
-	@Test
-	public void testDiscardedSegmentsAreCounted() throws Exception {
-		LinkedList<Long> segmentNumbers = new LinkedList<Long>();
-		segmentNumbers.add(123L); // 123 / 3 = frame number 41
-		segmentNumbers.add(456L); // 456 / 3 = frame number 152
-		segmentNumbers.add(0L); // 0 / 3 = frame number 0
-		IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
-				segmentNumbers, 1234);
-		Mockery context = new Mockery();
-		final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
-		final FrameWindow window = context.mock(FrameWindow.class);
-		context.checking(new Expectations() {{
-			// First segment
-			one(window).contains(41L);
-			will(returnValue(false));
-			one(window).isTooHigh(41L);
-			will(returnValue(true));
-			// Second segment
-			one(window).contains(152L);
-			will(returnValue(false));
-			one(window).isTooHigh(152L);
-			will(returnValue(true));
-			// Third segment
-			one(window).contains(0L);
-			will(returnValue(true));
-			one(decoder).decodeFrame(with(any(Frame.class)),
-					with(any(Segment[].class)));
-			will(returnValue(false));
-		}});
-
-		IncomingErrorCorrectionLayerImpl err =
-			new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
-		Frame f = new Frame();
-		assertFalse(err.readFrame(f, window));
-		Map<Long, Integer> discardCounts = err.getDiscardCounts();
-		assertEquals(2, discardCounts.size());
-		assertEquals(Integer.valueOf(1), discardCounts.get(41L));
-		assertEquals(Integer.valueOf(1), discardCounts.get(152L));
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testTooManyDiscardedSegmentsCauseException() throws Exception {
-		LinkedList<Long> segmentNumbers = new LinkedList<Long>();
-		segmentNumbers.add(123L); // 123 / 3 = frame number 41
-		segmentNumbers.add(124L); // 124 / 3 = frame number 41
-		IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
-				segmentNumbers, 1234);
-		Mockery context = new Mockery();
-		final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
-		final FrameWindow window = context.mock(FrameWindow.class);
-		context.checking(new Expectations() {{
-			// First segment
-			one(window).contains(41L);
-			will(returnValue(false));
-			one(window).isTooHigh(41L);
-			will(returnValue(true));
-			// Second segment
-			one(window).contains(41L);
-			will(returnValue(false));
-			one(window).isTooHigh(41L);
-			will(returnValue(true));
-		}});
-		IncomingErrorCorrectionLayerImpl err =
-			new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
-		Frame f = new Frame();
-		try {
-			err.readFrame(f, window);
-			fail();
-		} catch(FormatException expected) {}
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testSetsAndDiscardedSegmentsAreFreed() throws Exception {
-		LinkedList<Long> segmentNumbers = new LinkedList<Long>();
-		segmentNumbers.add(96L); // 96 / 3 = frame number 32
-		segmentNumbers.add(0L); // 0 / 3 = frame number 0
-		segmentNumbers.add(1L); // 1 / 3 = frame number 0
-		IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
-				segmentNumbers, 1234);
-		Mockery context = new Mockery();
-		final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
-		final FrameWindow window = context.mock(FrameWindow.class);
-		context.checking(new Expectations() {{
-			// First segment
-			one(window).contains(32L);
-			will(returnValue(false));
-			one(window).isTooHigh(32L);
-			will(returnValue(true));
-			// Second segment
-			one(window).contains(0L);
-			will(returnValue(true));
-			one(decoder).decodeFrame(with(any(Frame.class)),
-					with(any(Segment[].class)));
-			will(returnValue(false));
-			// Third segment
-			one(window).contains(0L);
-			will(returnValue(true));
-			one(decoder).decodeFrame(with(any(Frame.class)),
-					with(any(Segment[].class)));
-			will(returnValue(true));
-			// Second call, new window
-			one(window).contains(0L);
-			will(returnValue(false));
-			one(window).isTooHigh(32L);
-			will(returnValue(false));
-		}});
-		IncomingErrorCorrectionLayerImpl err =
-			new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
-		Frame f = new Frame();
-		// The first call discards one segment and decodes two
-		assertTrue(err.readFrame(f, window));
-		// The second call reaches EOF
-		assertFalse(err.readFrame(f, window));
-		// The segment set and discard count should have been freed
-		Map<Long, Segment[]> segmentSets = err.getSegmentSets();
-		assertTrue(segmentSets.isEmpty());
-		Map<Long, Integer> discardCounts = err.getDiscardCounts();
-		assertTrue(discardCounts.isEmpty());
-
-		context.assertIsSatisfied();
-	}
-
-	private static class TestIncomingEncryptionLayer
-	implements IncomingEncryptionLayer {
-
-		private final LinkedList<Long> segmentNumbers;
-		private final int length;
-
-		private TestIncomingEncryptionLayer(LinkedList<Long> segmentNumbers,
-				int length) {
-			this.segmentNumbers = segmentNumbers;
-			this.length = length;
-		}
-
-		public boolean readSegment(Segment s) throws IOException,
-		InvalidDataException {
-			Long segmentNumber = segmentNumbers.poll();
-			if(segmentNumber == null) return false;
-			s.setSegmentNumber(segmentNumber);
-			s.setLength(length);
-			return true;
-		}
-
-		public int getMaxSegmentLength() {
-			return length;
-		}
-	}
-}
diff --git a/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java b/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java
deleted file mode 100644
index 23bba20b49cbe278bbd0ae45a0b17e5003707597..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/IncomingReliabilityLayerImplTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-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_WINDOW_SIZE;
-import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.transport.ConnectionReader;
-
-import org.junit.Test;
-
-public class IncomingReliabilityLayerImplTest extends BriarTestCase {
-
-	@Test
-	public void testNoReordering() throws Exception {
-		List<Integer> frameNumbers = new ArrayList<Integer>();
-		// Receive FRAME_WINDOW_SIZE * 2 frames in the correct order
-		for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) frameNumbers.add(i);
-		IncomingAuthenticationLayer authentication =
-			new TestIncomingAuthenticationLayer(frameNumbers);
-		IncomingReliabilityLayerImpl reliability =
-			new IncomingReliabilityLayerImpl(authentication);
-		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());
-		}
-		assertEquals(-1, in.read());
-		// No free frames should be cached
-		assertEquals(0, reliability.getFreeFramesCount());
-	}
-
-	@Test
-	public void testReordering() throws Exception {
-		List<Integer> frameNumbers = new ArrayList<Integer>();
-		// Receive the first FRAME_WINDOW_SIZE frames in a random order
-		for(int i = 0; i < FRAME_WINDOW_SIZE; i++) frameNumbers.add(i);
-		Collections.shuffle(frameNumbers);
-		// Receive the next FRAME_WINDOW_SIZE frames in the correct order
-		for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE * 2; i++) {
-			frameNumbers.add(i);
-		}
-		// The reliability layer should reorder the frames
-		IncomingAuthenticationLayer authentication =
-			new TestIncomingAuthenticationLayer(frameNumbers);
-		IncomingReliabilityLayerImpl reliability =
-			new IncomingReliabilityLayerImpl(authentication);
-		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());
-		}
-		assertEquals(-1, in.read());
-		// Fewer than FRAME_WINDOW_SIZE free frames should be cached
-		assertTrue(reliability.getFreeFramesCount() < 32);
-	}
-
-	private static class TestIncomingAuthenticationLayer
-	implements IncomingAuthenticationLayer {
-
-		private final List<Integer> frameNumbers;
-
-		private int index;
-
-		private TestIncomingAuthenticationLayer(List<Integer> frameNumbers) {
-			this.frameNumbers = frameNumbers;
-			index = 0;
-		}
-
-		public boolean readFrame(Frame f, FrameWindow window) {
-			if(index >= frameNumbers.size()) return false;
-			int frameNumber = frameNumbers.get(index);
-			assertTrue(window.contains(frameNumber));
-			index++;
-			byte[] buf = f.getBuffer();
-			HeaderEncoder.encodeHeader(buf, frameNumber, 100, 0);
-			for(int i = 0; i < 100; i++) {
-				buf[FRAME_HEADER_LENGTH + i] = (byte) frameNumber;
-			}
-			f.setLength(FRAME_HEADER_LENGTH + 100 + MAC_LENGTH);
-			return true;
-		}
-
-		public int getMaxFrameLength() {
-			return FRAME_HEADER_LENGTH + 100 + MAC_LENGTH;
-		}
-	}
-}
diff --git a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
index dd244eeaffc0a43b89cfdcac11d0ab8fa294b5e6..dcf0386702c2eced15b8afb961f28bc259890f9e 100644
--- a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
@@ -3,29 +3,24 @@ 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;
 import java.io.InputStream;
 
 import net.sf.briar.api.FormatException;
-import net.sf.briar.api.transport.Segment;
 
 /** An encryption layer that performs no encryption. */
-class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
+class NullIncomingEncryptionLayer implements FrameReader {
 
 	private final InputStream in;
 
-	private long segmentNumber = 0L;
-
 	NullIncomingEncryptionLayer(InputStream in) {
 		this.in = in;
 	}
 
-	public boolean readSegment(Segment s) throws IOException {
-		byte[] buf = s.getBuffer();
+	public boolean readFrame(Frame f) throws IOException {
+		byte[] buf = f.getBuffer();
 		// Read the frame header
 		int offset = 0, length = FRAME_HEADER_LENGTH;
 		while(offset < length) {
@@ -47,12 +42,7 @@ class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
 			if(read == -1) throw new EOFException();
 			offset += read;
 		}
-		s.setLength(length);
-		s.setSegmentNumber(segmentNumber++);
+		f.setLength(length);
 		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 88921aff0321f7ca4ac996c0fc56972be0beb21e..598562877bcdf93c99c4dcc0df74805d663a0523 100644
--- a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
@@ -1,15 +1,10 @@
 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;
 
-import net.sf.briar.api.transport.Segment;
-
 /** An encryption layer that performs no encryption. */
-class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
+class NullOutgoingEncryptionLayer implements FrameWriter {
 
 	private final OutputStream out;
 
@@ -25,9 +20,9 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
 		this.capacity = capacity;
 	}
 
-	public void writeSegment(Segment s) throws IOException {
-		out.write(s.getBuffer(), 0, s.getLength());
-		capacity -= s.getLength();
+	public void writeFrame(Frame f) throws IOException {
+		out.write(f.getBuffer(), 0, f.getLength());
+		capacity -= f.getLength();
 	}
 
 	public void flush() throws IOException {
@@ -37,8 +32,4 @@ 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/OutgoingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
index a535725385ddb5be3898503f3c1daf4f2c55986f..aca3f7192be20c26a4d5867e29542abaf7156abf 100644
--- a/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
@@ -11,7 +11,6 @@ import javax.crypto.spec.IvParameterSpec;
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
@@ -23,36 +22,36 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
 
 	private static final int MAC_LENGTH = 32;
 
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
+	private final Cipher tagCipher, frameCipher;
+	private final ErasableKey tagKey, frameKey;
 
 	public OutgoingEncryptionLayerImplTest() {
 		super();
 		Injector i = Guice.createInjector(new CryptoModule());
 		CryptoComponent crypto = i.getInstance(CryptoComponent.class);
 		tagCipher = crypto.getTagCipher();
-		segCipher = crypto.getSegmentCipher();
+		frameCipher = crypto.getFrameCipher();
 		tagKey = crypto.generateTestKey();
-		segKey = crypto.generateTestKey();
+		frameKey = crypto.generateTestKey();
 	}
 
 	@Test
-	public void testEncryptionWithFirstSegmentTagged() throws Exception {
+	public void testEncryptionWithTag() throws Exception {
 		// Calculate the expected tag
 		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the first segment
-		byte[] iv = new byte[segCipher.getBlockSize()];
+		TagEncoder.encodeTag(tag, tagCipher, tagKey);
+		// Calculate the expected ciphertext for the first frame
+		byte[] iv = new byte[frameCipher.getBlockSize()];
 		byte[] plaintext = new byte[123 + MAC_LENGTH];
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext);
-		// Calculate the expected ciphertext for the second segment
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext = frameCipher.doFinal(plaintext);
+		// Calculate the expected ciphertext for the second frame
 		byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
 		IvEncoder.updateIv(iv, 1L);
 		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1);
+		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
+		byte[] ciphertext1 = frameCipher.doFinal(plaintext1);
 		// Concatenate the ciphertexts
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		out.write(tag);
@@ -61,65 +60,15 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
 		byte[] expected = out.toByteArray();
 		// Use the encryption layer to encrypt the plaintext
 		out.reset();
-		OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
-				Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey,
-				false);
-		Segment s = new SegmentImpl();
-		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
-		s.setLength(plaintext.length);
-		s.setSegmentNumber(0L);
-		encrypter.writeSegment(s);
-		System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
-		s.setLength(plaintext1.length);
-		s.setSegmentNumber(1L);
-		encrypter.writeSegment(s);
-		byte[] actual = out.toByteArray();
-		// Check that the actual ciphertext matches the expected ciphertext
-		assertArrayEquals(expected, actual);
-		assertEquals(Long.MAX_VALUE - actual.length,
-				encrypter.getRemainingCapacity());
-	}
-
-	@Test
-	public void testEncryptionWithEverySegmentTagged() throws Exception {
-		// Calculate the expected tag for the first segment
-		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the first segment
-		byte[] iv = new byte[segCipher.getBlockSize()];
-		byte[] plaintext = new byte[123 + MAC_LENGTH];
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext);
-		// Calculate the expected tag for the second segment
-		byte[] tag1 = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the second segment
-		byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
-		IvEncoder.updateIv(iv, 1L);
-		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1);
-		// Concatenate the ciphertexts
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		out.write(tag);
-		out.write(ciphertext);
-		out.write(tag1);
-		out.write(ciphertext1);
-		byte[] expected = out.toByteArray();
-		// Use the encryption layer to encrypt the plaintext
-		out.reset();
-		OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
-				Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey, true);
-		Segment s = new SegmentImpl();
-		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
-		s.setLength(plaintext.length);
-		s.setSegmentNumber(0L);
-		encrypter.writeSegment(s);
-		System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
-		s.setLength(plaintext1.length);
-		s.setSegmentNumber(1L);
-		encrypter.writeSegment(s);
+		FrameWriter encrypter = new OutgoingEncryptionLayerImpl(out,
+				Long.MAX_VALUE, tagCipher, frameCipher, tagKey, frameKey);
+		Frame f = new Frame();
+		System.arraycopy(plaintext, 0, f.getBuffer(), 0, plaintext.length);
+		f.setLength(plaintext.length);
+		encrypter.writeFrame(f);
+		System.arraycopy(plaintext1, 0, f.getBuffer(), 0, plaintext1.length);
+		f.setLength(plaintext1.length);
+		encrypter.writeFrame(f);
 		byte[] actual = out.toByteArray();
 		// Check that the actual ciphertext matches the expected ciphertext
 		assertArrayEquals(expected, actual);
diff --git a/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java b/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java
deleted file mode 100644
index 2e8321d6e30f651dc704922bed25e33a5084ad89..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/SegmentedIncomingEncryptionLayerTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-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;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSource;
-import net.sf.briar.api.transport.Segment;
-import net.sf.briar.crypto.CryptoModule;
-
-import org.junit.Test;
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-
-public class SegmentedIncomingEncryptionLayerTest extends BriarTestCase {
-
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-
-	public SegmentedIncomingEncryptionLayerTest() {
-		super();
-		Injector i = Guice.createInjector(new CryptoModule());
-		CryptoComponent crypto = i.getInstance(CryptoComponent.class);
-		tagCipher = crypto.getTagCipher();
-		segCipher = crypto.getSegmentCipher();
-		tagKey = crypto.generateTestKey();
-		segKey = crypto.generateTestKey();
-	}
-
-	@Test
-	public void testDecryptionWithFirstSegmentTagged() throws Exception {
-		// Calculate the ciphertext for the first segment, including its tag
-		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
-		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
-		byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
-		TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
-		byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
-				TAG_LENGTH);
-		// Calculate the ciphertext for the second segment
-		byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
-		HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
-		IvEncoder.updateIv(iv, 1L);
-		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
-				plaintext1.length);
-		// Buffer the first segment and create a source for the second
-		Segment buffered = new SegmentImpl();
-		System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
-				ciphertext.length);
-		buffered.setLength(ciphertext.length);
-		SegmentSource in = new ByteArraySegmentSource(ciphertext1);
-		// Use the encryption layer to decrypt the ciphertext
-		IncomingEncryptionLayer decrypter =
-			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, false, false, buffered);
-		// First segment
-		Segment s = new SegmentImpl();
-		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++) {
-			assertEquals(plaintext[i], decrypted[i]);
-		}
-		// Second segment
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext1.length, s.getLength());
-		assertEquals(1L, s.getSegmentNumber());
-		decrypted = s.getBuffer();
-		for(int i = 0; i < plaintext1.length; i++) {
-			assertEquals(plaintext1[i], decrypted[i]);
-		}
-	}
-
-	@Test
-	public void testDecryptionWithEverySegmentTagged() throws Exception {
-		// Calculate the ciphertext for the first segment, including its tag
-		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
-		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
-		byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
-		TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
-		byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
-				TAG_LENGTH);
-		// Calculate the ciphertext for the second frame, including its tag
-		byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
-		HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
-		byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length];
-		TagEncoder.encodeTag(ciphertext1, 1L, tagCipher, tagKey);
-		IvEncoder.updateIv(iv, 1L);
-		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		segCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1,
-				TAG_LENGTH);
-		// Buffer the first segment and create a source for the second
-		Segment buffered = new SegmentImpl();
-		System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
-				ciphertext.length);
-		buffered.setLength(ciphertext.length);
-		SegmentSource in = new ByteArraySegmentSource(ciphertext1);
-		// Use the encryption layer to decrypt the ciphertext
-		IncomingEncryptionLayer decrypter =
-			new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
-					tagKey, segKey, true, false, buffered);
-		// First segment
-		Segment s = new SegmentImpl();
-		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++) {
-			assertEquals(plaintext[i], decrypted[i]);
-		}
-		// Second segment
-		assertTrue(decrypter.readSegment(s));
-		assertEquals(plaintext1.length, s.getLength());
-		assertEquals(1L, s.getSegmentNumber());
-		decrypted = s.getBuffer();
-		for(int i = 0; i < plaintext1.length; i++) {
-			assertEquals(plaintext1[i], decrypted[i]);
-		}
-	}
-
-	private static class ByteArraySegmentSource implements SegmentSource {
-
-		private final byte[] segment;
-
-		private ByteArraySegmentSource(byte[] segment) {
-			this.segment = segment;
-		}
-
-		public boolean readSegment(Segment s) throws IOException {
-			System.arraycopy(segment, 0, s.getBuffer(), 0, segment.length);
-			s.setLength(segment.length);
-			return true;
-		}
-
-		public int getMaxSegmentLength() {
-			return MAX_SEGMENT_LENGTH;
-		}
-	}
-}
diff --git a/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java b/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java
deleted file mode 100644
index 3d6e5cae31056e168f829fc5db6cf9decc256143..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/SegmentedOutgoingEncryptionLayerTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-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;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.SegmentSink;
-import net.sf.briar.api.transport.Segment;
-import net.sf.briar.crypto.CryptoModule;
-
-import org.junit.Test;
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-
-public class SegmentedOutgoingEncryptionLayerTest extends BriarTestCase {
-
-	private static final int MAC_LENGTH = 32;
-
-	private final Cipher tagCipher, segCipher;
-	private final ErasableKey tagKey, segKey;
-
-	public SegmentedOutgoingEncryptionLayerTest() {
-		super();
-		Injector i = Guice.createInjector(new CryptoModule());
-		CryptoComponent crypto = i.getInstance(CryptoComponent.class);
-		tagCipher = crypto.getTagCipher();
-		segCipher = crypto.getSegmentCipher();
-		tagKey = crypto.generateTestKey();
-		segKey = crypto.generateTestKey();
-	}
-
-	@Test
-	public void testEncryptionWithFirstSegmentTagged() throws Exception {
-		// Calculate the expected tag
-		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the first segment
-		byte[] iv = new byte[segCipher.getBlockSize()];
-		byte[] plaintext = new byte[123 + MAC_LENGTH];
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext);
-		// Calculate the expected ciphertext for the second segment
-		byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
-		IvEncoder.updateIv(iv, 1L);
-		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1);
-		// Concatenate the ciphertexts
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		out.write(tag);
-		out.write(ciphertext);
-		out.write(ciphertext1);
-		byte[] expected = out.toByteArray();
-		// Use the encryption layer to encrypt the plaintext
-		ByteArraySegmentSink sink = new ByteArraySegmentSink();
-		OutgoingEncryptionLayer encrypter =
-			new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
-					tagCipher, segCipher, tagKey, segKey, false, false);
-		Segment s = new SegmentImpl();
-		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
-		s.setLength(plaintext.length);
-		s.setSegmentNumber(0L);
-		encrypter.writeSegment(s);
-		System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
-		s.setLength(plaintext1.length);
-		s.setSegmentNumber(1L);
-		encrypter.writeSegment(s);
-		byte[] actual = out.toByteArray();
-		// Check that the actual ciphertext matches the expected ciphertext
-		assertArrayEquals(expected, actual);
-		assertEquals(Long.MAX_VALUE - actual.length,
-				encrypter.getRemainingCapacity());
-	}
-
-	@Test
-	public void testEncryptionWithEverySegmentTagged() throws Exception {
-		// Calculate the expected tag for the first segment
-		byte[] tag = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the first segment
-		byte[] iv = new byte[segCipher.getBlockSize()];
-		byte[] plaintext = new byte[123 + MAC_LENGTH];
-		IvParameterSpec ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext = segCipher.doFinal(plaintext);
-		// Calculate the expected tag for the second segment
-		byte[] tag1 = new byte[TAG_LENGTH];
-		TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
-		// Calculate the expected ciphertext for the second segment
-		byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
-		IvEncoder.updateIv(iv, 1L);
-		ivSpec = new IvParameterSpec(iv);
-		segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
-		byte[] ciphertext1 = segCipher.doFinal(plaintext1);
-		// Concatenate the ciphertexts
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		out.write(tag);
-		out.write(ciphertext);
-		out.write(tag1);
-		out.write(ciphertext1);
-		byte[] expected = out.toByteArray();
-		// Use the encryption layer to encrypt the plaintext
-		SegmentSink sink = new ByteArraySegmentSink();
-		OutgoingEncryptionLayer encrypter =
-			new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
-					tagCipher, segCipher, tagKey, segKey, true, false);
-		Segment s = new SegmentImpl();
-		System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
-		s.setLength(plaintext.length);
-		s.setSegmentNumber(0L);
-		encrypter.writeSegment(s);
-		System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
-		s.setLength(plaintext1.length);
-		s.setSegmentNumber(1L);
-		encrypter.writeSegment(s);
-		byte[] actual = out.toByteArray();
-		// Check that the actual ciphertext matches the expected ciphertext
-		assertArrayEquals(expected, actual);
-		assertEquals(Long.MAX_VALUE - actual.length,
-				encrypter.getRemainingCapacity());
-	}
-
-	private static class ByteArraySegmentSink extends ByteArrayOutputStream
-	implements SegmentSink {
-
-		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
deleted file mode 100644
index 0fa4158ffd0e8ac79d7f2b9c112c60c80712a20e..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/XorErasureCodeTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-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 org.junit.Test;
-
-public class XorErasureCodeTest extends BriarTestCase {
-
-	@Test
-	public void testEncodingAndDecodingWithAllSegments() throws Exception {
-		XorErasureEncoder e = new XorErasureEncoder(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;
-		HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
-		f.setLength(1234);
-		Segment[] set = e.encodeFrame(f);
-		assertEquals(5, set.length);
-		Frame f1 = new Frame(1234);
-		assertTrue(d.decodeFrame(f1, set));
-		assertArrayEquals(f.getBuffer(), f1.getBuffer());
-	}
-
-	@Test
-	public void testEncodingAndDecodingWithMissingSegment() throws Exception {
-		XorErasureEncoder e = new XorErasureEncoder(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;
-		HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
-		f.setLength(1234);
-		for(int i = 0; i < 5; i++) {
-			Segment[] set = e.encodeFrame(f);
-			assertEquals(5, set.length);
-			set[i] = null;
-			Frame f1 = new Frame(1234);
-			assertTrue(d.decodeFrame(f1, set));
-			assertArrayEquals(f.getBuffer(), f1.getBuffer());
-		}
-	}
-}
diff --git a/test/net/sf/briar/transport/XorErasureDecoderTest.java b/test/net/sf/briar/transport/XorErasureDecoderTest.java
deleted file mode 100644
index 40346866476e5a6da8a1d081bc83190481e11b7c..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/XorErasureDecoderTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-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 org.junit.Test;
-
-public class XorErasureDecoderTest extends BriarTestCase {
-
-	@Test
-	public void testMaximumLength() throws Exception {
-		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();
-		assertTrue(d.decodeFrame(f, set));
-		checkFrame(f, MAX_FRAME_LENGTH);
-		// A frame larger than the maximum length should not be decoded
-		set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
-		f = new Frame();
-		try {
-			d.decodeFrame(f, set);
-		} catch(FormatException expected) {}
-	}
-
-	@Test
-	public void testMinimumLengthIsUsed() throws Exception {
-		Segment[] set = encodeEmptyFrame(250, 4);
-		// Replace one of the pieces with a longer piece
-		byte[] b = set[1].getBuffer();
-		assertArrayEquals(new byte[250], b);
-		set[1] = new SegmentImpl(251);
-		set[1].setLength(251);
-		// The frame should be decoded successfully
-		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
-		assertEquals(750, f.getLength());
-	}
-
-	@Test
-	public void testDecodingWithMissingSegment() throws Exception {
-		XorErasureDecoder d = new XorErasureDecoder(4, false);
-		for(int i = 0; i < 4; i++) {
-			Segment[] set = encodeEmptyFrame(250, 4);
-			set[i] = null;
-			// The frame should be decoded successfully
-			Frame f = new Frame(750);
-			assertTrue(d.decodeFrame(f, set));
-			checkFrame(f, 750);
-		}
-	}
-
-	@Test
-	public void testDecodingWithTwoMissingSegments() throws Exception {
-		XorErasureDecoder d = new XorErasureDecoder(4, false);
-		Segment[] set = encodeEmptyFrame(250, 4);
-		set[0] = null;
-		set[1] = null;
-		Frame f = new Frame(750);
-		assertFalse(d.decodeFrame(f, set));
-	}
-
-	private Segment[] encodeEmptyFrame(int length, int n) {
-		Segment[] set = new Segment[n];
-		for(int i = 0; i < n; i++) {
-			set[i] = new SegmentImpl(length);
-			set[i].setLength(length);
-		}
-		int payload = length * (n - 1) - FRAME_HEADER_LENGTH - MAC_LENGTH;
-		HeaderEncoder.encodeHeader(set[0].getBuffer(), 0L, payload, 0);
-		HeaderEncoder.encodeHeader(set[n - 1].getBuffer(), 0L, payload, 0);
-		return set;
-	}
-
-	private void checkFrame(Frame f, int length) {
-		byte[] b = f.getBuffer();
-		assertEquals(0L, HeaderEncoder.getFrameNumber(b));
-		int payload = length - FRAME_HEADER_LENGTH - MAC_LENGTH;
-		assertEquals(payload, HeaderEncoder.getPayloadLength(b));
-		assertEquals(0, HeaderEncoder.getPaddingLength(b));
-		// Check the body
-		assertEquals(length, f.getLength());
-		for(int i = FRAME_HEADER_LENGTH; i < length; i++) {
-			assertEquals("" + i, 0, b[i]);
-		}
-	}
-}
diff --git a/test/net/sf/briar/transport/XorErasureEncoderTest.java b/test/net/sf/briar/transport/XorErasureEncoderTest.java
deleted file mode 100644
index af67c0b644d28c8536b95eac1054f900532cdcdd..0000000000000000000000000000000000000000
--- a/test/net/sf/briar/transport/XorErasureEncoderTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package net.sf.briar.transport;
-
-import static org.junit.Assert.assertArrayEquals;
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.transport.Segment;
-
-import org.junit.Test;
-
-public class XorErasureEncoderTest extends BriarTestCase {
-
-	@Test
-	public void testEncoding() {
-		// Create a frame
-		Frame f = new Frame();
-		f.setLength(200);
-		byte[] b = f.getBuffer();
-		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 67 bytes each
-		assertEquals(4, set.length);
-		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 < 67; i++) assertEquals((byte) i, b1[i]);
-		byte[] b2 = set[1].getBuffer();
-		for(int i = 0; i < 67; i++) assertEquals((byte) (i + 67), b2[i]);
-		byte[] b3 = set[2].getBuffer();
-		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[67];
-		for(int i = 0; i < 67; i++) {
-			expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]);
-		}
-		assertArrayEquals(expected, b4);
-	}
-}