diff --git a/api/net/sf/briar/api/plugins/SegmentSink.java b/api/net/sf/briar/api/plugins/SegmentSink.java
index 8f017c96e27647a472a54fd2033475c76b428393..55caa493403d95c52e3f7d4599328ca095c44c8a 100644
--- a/api/net/sf/briar/api/plugins/SegmentSink.java
+++ b/api/net/sf/briar/api/plugins/SegmentSink.java
@@ -2,6 +2,8 @@ package net.sf.briar.api.plugins;
 
 import java.io.IOException;
 
+import net.sf.briar.api.transport.Segment;
+
 public interface SegmentSink {
 
 	/** Writes the given segment. */
diff --git a/api/net/sf/briar/api/plugins/SegmentSource.java b/api/net/sf/briar/api/plugins/SegmentSource.java
index bb5a92a18faddad1698425a62b4176749a711dd1..403659a0b1fc831f865db2c4418a7676a7e1ab63 100644
--- a/api/net/sf/briar/api/plugins/SegmentSource.java
+++ b/api/net/sf/briar/api/plugins/SegmentSource.java
@@ -2,6 +2,8 @@ package net.sf.briar.api.plugins;
 
 import java.io.IOException;
 
+import net.sf.briar.api.transport.Segment;
+
 public interface SegmentSource {
 
 	/**
diff --git a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
index a886826af4bc076371b24004d50c93160702f391..ea079fb52f088c576a0ccaa83d5f1dbde63f17c4 100644
--- a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
+++ b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java
@@ -2,6 +2,8 @@ package net.sf.briar.api.transport;
 
 import java.io.InputStream;
 
+import net.sf.briar.api.plugins.SegmentSource;
+
 public interface ConnectionReaderFactory {
 
 	/**
@@ -12,9 +14,23 @@ public interface ConnectionReaderFactory {
 	ConnectionReader createConnectionReader(InputStream in, byte[] secret,
 			byte[] tag);
 
+	/**
+	 * 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 buffered);
+
 	/**
 	 * 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);
 }
diff --git a/api/net/sf/briar/api/plugins/Segment.java b/api/net/sf/briar/api/transport/Segment.java
similarity index 82%
rename from api/net/sf/briar/api/plugins/Segment.java
rename to api/net/sf/briar/api/transport/Segment.java
index b0eacf91045e4a4f6717036e8f2de1eb8d4c1881..da91745fa4140d803c2372774a9a835360edd503 100644
--- a/api/net/sf/briar/api/plugins/Segment.java
+++ b/api/net/sf/briar/api/transport/Segment.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.plugins;
+package net.sf.briar.api.transport;
 
 public interface Segment {
 
diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index 6e0677abaf3fd9a9384ef8c683c9fe999dbe09f2..638ff48fba74098a75997df517fc76f4105454ff 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -7,8 +7,10 @@ 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;
@@ -24,22 +26,16 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 
 	public ConnectionReader createConnectionReader(InputStream in,
 			byte[] secret, byte[] tag) {
-		// Validate the tag
-		Cipher tagCipher = crypto.getTagCipher();
-		ErasableKey tagKey = crypto.deriveTagKey(secret, true);
-		long segmentNumber = TagEncoder.decodeTag(tag, tagCipher, tagKey);
-		tagKey.erase();
-		if(segmentNumber != 0) throw new IllegalArgumentException();
-		return createConnectionReader(in, true, secret);
+		return createConnectionReader(in, secret, tag, true);
 	}
 
 	public ConnectionReader createConnectionReader(InputStream in,
 			byte[] secret) {
-		return createConnectionReader(in, false, secret);
+		return createConnectionReader(in, secret, null, false);
 	}
 
 	private ConnectionReader createConnectionReader(InputStream in,
-			boolean initiator, byte[] secret) {
+			byte[] secret, byte[] tag, boolean initiator) {
 		// Derive the keys and erase the secret
 		ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
 		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
@@ -50,7 +46,38 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 		Cipher frameCipher = crypto.getFrameCipher();
 		Mac mac = crypto.getMac();
 		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, frameCipher, tagKey, frameKey, false);
+				tagCipher, frameCipher, tagKey, frameKey, false, tag);
+		// No error correction
+		IncomingErrorCorrectionLayer correcter =
+			new NullIncomingErrorCorrectionLayer(decrypter);
+		// Create the reader
+		return new ConnectionReaderImpl(correcter, mac, macKey);
+	}
+
+	public ConnectionReader createConnectionReader(SegmentSource in,
+			byte[] secret, Segment buffered) {
+		return createConnectionReader(in, secret, buffered, true);
+	}
+
+	public ConnectionReader createConnectionReader(SegmentSource in,
+			byte[] secret) {
+		return createConnectionReader(in, secret, new SegmentImpl(), false);
+	}
+
+	private ConnectionReader createConnectionReader(SegmentSource in,
+			byte[] secret, Segment buffered, boolean initiator) {
+		// Derive the keys and erase the secret
+		ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
+		ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
+		ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
+		ByteUtils.erase(secret);
+		// Create the decrypter
+		Cipher tagCipher = crypto.getTagCipher();
+		Cipher frameCipher = crypto.getFrameCipher();
+		Mac mac = crypto.getMac();
+		IncomingEncryptionLayer decrypter =
+			new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher,
+					tagKey, frameKey, false, buffered);
 		// No error correction
 		IncomingErrorCorrectionLayer correcter =
 			new NullIncomingErrorCorrectionLayer(decrypter);
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayer.java b/components/net/sf/briar/transport/IncomingEncryptionLayer.java
index c93681130c13ec5191952c0ae19f323e7b87eb42..84c8776c1164a99a91f27404c4e3a741c6422616 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayer.java
@@ -2,7 +2,7 @@ package net.sf.briar.transport;
 
 import java.io.IOException;
 
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 interface IncomingEncryptionLayer {
 
diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
index 447656ab66aa1b5b9bcb2b13365b636252ceb058..a0c7db71a99292d02d04f2619d6ff0c2c50319cd 100644
--- a/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingEncryptionLayerImpl.java
@@ -16,7 +16,7 @@ import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 
@@ -27,18 +27,20 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 	private final byte[] iv, ciphertext;
 	private final boolean tagEverySegment;
 
+	private byte[] bufferedTag;
 	private boolean firstSegment = true;
 	private long segmentNumber = 0L;
 
 	IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
 			Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
-			boolean tagEverySegment) {
+			boolean tagEverySegment, byte[] bufferedTag) {
 		this.in = in;
 		this.tagCipher = tagCipher;
 		this.frameCipher = frameCipher;
 		this.tagKey = tagKey;
 		this.frameKey = frameKey;
 		this.tagEverySegment = tagEverySegment;
+		this.bufferedTag = bufferedTag;
 		blockSize = frameCipher.getBlockSize();
 		if(blockSize < FRAME_HEADER_LENGTH)
 			throw new IllegalArgumentException();
@@ -47,29 +49,41 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 	}
 
 	public boolean readSegment(Segment s) throws IOException {
-		boolean tag = tagEverySegment && !firstSegment;
+		boolean expectTag = tagEverySegment || firstSegment;
+		firstSegment = false;
 		try {
-			// If a tag is expected then read, decrypt and validate it
-			if(tag) {
-				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();
+			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;
 					}
-					offset += read;
+					long seg = TagEncoder.decodeTag(ciphertext, tagCipher,
+							tagKey);
+					if(seg == -1) throw new FormatException();
+					segmentNumber = seg;
+				} else {
+					System.out.println("Buffered tag");
+					long seg = TagEncoder.decodeTag(bufferedTag, tagCipher,
+							tagKey);
+					bufferedTag = null;
+					if(seg == -1) throw new FormatException();
+					segmentNumber = seg;
 				}
-				long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
-				if(seg == -1) throw new FormatException();
-				segmentNumber = seg;
 			}
 			// Read the first block of the frame/segment
 			int offset = 0;
 			while(offset < blockSize) {
 				int read = in.read(ciphertext, offset, blockSize - offset);
 				if(read == -1) {
-					if(offset == 0 && !tag && !firstSegment) return false;
+					if(offset == 0 && !expectTag) return false;
 					throw new EOFException();
 				}
 				offset += read;
@@ -108,7 +122,6 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
 			}
 			s.setLength(length);
 			s.setSegmentNumber(segmentNumber++);
-			firstSegment = false;
 			return true;
 		} catch(IOException e) {
 			frameKey.erase();
diff --git a/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java b/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
index 21caee2ce62e915b11bcc53ad437947660840b82..6c396e79ec400f9ef485eb6e8e6d7b2823bad168 100644
--- a/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
+++ b/components/net/sf/briar/transport/IncomingSegmentedEncryptionLayer.java
@@ -13,8 +13,8 @@ import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.Segment;
 import net.sf.briar.api.plugins.SegmentSource;
+import net.sf.briar.api.transport.Segment;
 
 class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 
@@ -23,15 +23,16 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 	private final ErasableKey tagKey, frameKey;
 	private final int blockSize;
 	private final byte[] iv;
-	private final Segment segment;
 	private final boolean tagEverySegment;
+	private final Segment segment;
 
+	private Segment bufferedSegment;
 	private boolean firstSegment = true;
 	private long segmentNumber = 0L;
 
 	IncomingSegmentedEncryptionLayer(SegmentSource in, Cipher tagCipher,
 			Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
-			boolean tagEverySegment) {
+			boolean tagEverySegment, Segment s) {
 		this.in = in;
 		this.tagCipher = tagCipher;
 		this.frameCipher = frameCipher;
@@ -43,20 +44,30 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 			throw new IllegalArgumentException();
 		iv = IvEncoder.encodeIv(0L, blockSize);
 		segment = new SegmentImpl();
+		bufferedSegment = s;
 	}
 
 	public boolean readSegment(Segment s) throws IOException {
-		boolean tag = tagEverySegment && !firstSegment;
+		boolean expectTag = tagEverySegment || firstSegment;
+		firstSegment = false;
 		try {
-			// Read the segment
-			if(!in.readSegment(segment)) return false;
-			int offset = tag ? TAG_LENGTH : 0, length = segment.getLength();
+			// 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 > MAX_SEGMENT_LENGTH) throw new FormatException();
 			if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
 				throw new FormatException();
 			byte[] ciphertext = segment.getBuffer();
 			// If a tag is expected then decrypt and validate it
-			if(tag) {
+			if(expectTag) {
 				long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
 				if(seg == -1) throw new FormatException();
 				segmentNumber = seg;
@@ -74,7 +85,6 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
 			}
 			s.setLength(length - offset);
 			s.setSegmentNumber(segmentNumber++);
-			firstSegment = false;
 			return true;
 		} catch(IOException e) {
 			frameKey.erase();
diff --git a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
index cd97383c7c08bebd7f153bf0219d741756a6c47f..53898dc9f953dd40d7522082ca70e5d87c0db7fa 100644
--- a/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/NullIncomingErrorCorrectionLayer.java
@@ -3,7 +3,7 @@ package net.sf.briar.transport;
 import java.io.IOException;
 import java.util.Collection;
 
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
 
diff --git a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java b/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
index 9bf37eac5253a85a8d649128e7907c2ebb55fd57..c0719e2ead3745148f84945dd45d136be67fa332 100644
--- a/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
+++ b/components/net/sf/briar/transport/NullOutgoingErrorCorrectionLayer.java
@@ -4,7 +4,7 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.IOException;
 
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
 
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
index fecc03d286118d8f86ef08b4b0fc99f7a8649d20..e3b11cfe2ee0c25e3b6ff7d4786a56583e7e1eb2 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
@@ -2,7 +2,7 @@ package net.sf.briar.transport;
 
 import java.io.IOException;
 
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 interface OutgoingEncryptionLayer {
 
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
index 5a3ff9d0927b9148fd5d153a068cb7bb3641eeaf..c0aa5942ceaa0b749c1c36e53fc668b2118e7369 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayerImpl.java
@@ -11,7 +11,7 @@ import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
 
diff --git a/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
index 2bf1864454a209019cd261376ff3c8f4d80482d3..e5c66522c939980d96cdc89582f4011a283510f1 100644
--- a/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingSegmentedEncryptionLayer.java
@@ -9,8 +9,8 @@ import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 
 import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.api.plugins.Segment;
 import net.sf.briar.api.plugins.SegmentSink;
+import net.sf.briar.api.transport.Segment;
 
 class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
 
diff --git a/components/net/sf/briar/transport/SegmentImpl.java b/components/net/sf/briar/transport/SegmentImpl.java
index da3d4eab391387085857776978dc219a494b4fd9..8cd7acc549b924109a2fd9a35567f7c4647eea1c 100644
--- a/components/net/sf/briar/transport/SegmentImpl.java
+++ b/components/net/sf/briar/transport/SegmentImpl.java
@@ -1,7 +1,7 @@
 package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 import net.sf.briar.util.ByteUtils;
 
 class SegmentImpl implements Segment {
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index 465e85e3ebcf87ef4ac97e2618af0774cacc913c..37ec138af12afc45a03e16e7d91e2414c670d321 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -94,7 +94,7 @@ public class FrameReadWriteTest extends BriarTestCase {
 		assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
 		// Read the frames back
 		IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
-				tagCipher, frameCipher, tagKey, frameKey, false);
+				tagCipher, frameCipher, tagKey, frameKey, false, recoveredTag);
 		IncomingErrorCorrectionLayer correcter1 =
 			new NullIncomingErrorCorrectionLayer(decrypter);
 		ConnectionReader reader = new ConnectionReaderImpl(correcter1, mac,
diff --git a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
index 4db4ed9297a28b9e957e464f3f4a0229db82d73e..75bec7f4b5f9f56de5ba5b166757169e31354c5c 100644
--- a/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/IncomingEncryptionLayerImplTest.java
@@ -12,7 +12,7 @@ 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.Segment;
+import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -38,6 +38,9 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 
 	@Test
 	public void testDecryptionWithFirstSegmentTagged() 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
 		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
@@ -53,15 +56,15 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
 		byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
 				plaintext1.length);
-		// Concatenate the ciphertexts
+		// Concatenate the ciphertexts, excluding the first tag
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		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, frameCipher, tagKey, frameKey, false);
-		// First frame
+				tagCipher, frameCipher, tagKey, frameKey, false, tag);
+		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext.length, s.getLength());
@@ -70,7 +73,7 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second frame
+		// Second segment
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext1.length, s.getLength());
 		assertEquals(1L, s.getSegmentNumber());
@@ -82,6 +85,9 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 
 	@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
 		byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
 		HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
@@ -89,25 +95,27 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
 		byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
-		// Calculate the ciphertext for the second segment, including its tag
+		// 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
 		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);
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
-		frameCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1,
-				TAG_LENGTH);
-		// Concatenate the ciphertexts
+		byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
+				plaintext1.length);
+		// Concatenate the ciphertexts, excluding the first 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, frameCipher, tagKey, frameKey, true);
-		// First frame
+				tagCipher, frameCipher, tagKey, frameKey, true, tag);
+		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext.length, s.getLength());
@@ -116,7 +124,7 @@ public class IncomingEncryptionLayerImplTest extends BriarTestCase {
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second frame
+		// Second segment
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext1.length, s.getLength());
 		assertEquals(1L, s.getSegmentNumber());
diff --git a/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java b/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
index fbcf8cc78c226dd2b7626e9dd7794a64830db845..9439a8482013f019a44ecd7701a8112dffae2ded 100644
--- a/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/IncomingSegmentedEncryptionLayerTest.java
@@ -12,8 +12,8 @@ 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.Segment;
 import net.sf.briar.api.plugins.SegmentSource;
+import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
@@ -38,13 +38,16 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 
 	@Test
 	public void testDecryptionWithFirstSegmentTagged() throws Exception {
-		// Calculate the ciphertext for the first segment
+		// 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, frameCipher.getBlockSize());
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
-		byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+		frameCipher.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);
@@ -53,13 +56,17 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
 		byte[] ciphertext1 = frameCipher.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
-		byte[][] frames = new byte[][] { ciphertext, ciphertext1 };
-		SegmentSource in = new ByteArraySegmentSource(frames);
 		IncomingEncryptionLayer decrypter =
 			new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher,
-					tagKey, frameKey, false);
-		// First frame
+					tagKey, frameKey, false, buffered);
+		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext.length, s.getLength());
@@ -68,7 +75,7 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second frame
+		// Second segment
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext1.length, s.getLength());
 		assertEquals(1L, s.getSegmentNumber());
@@ -80,13 +87,16 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 
 	@Test
 	public void testDecryptionWithEverySegmentTagged() throws Exception {
-		// Calculate the ciphertext for the first frame
+		// 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, frameCipher.getBlockSize());
 		IvParameterSpec ivSpec = new IvParameterSpec(iv);
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
-		byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
+		frameCipher.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);
@@ -97,13 +107,17 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 		frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
 		frameCipher.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
-		byte[][] frames = new byte[][] { ciphertext, ciphertext1 };
-		SegmentSource in = new ByteArraySegmentSource(frames);
 		IncomingEncryptionLayer decrypter =
 			new IncomingSegmentedEncryptionLayer(in, tagCipher, frameCipher,
-					tagKey, frameKey, true);
-		// First frame
+					tagKey, frameKey, true, buffered);
+		// First segment
 		Segment s = new SegmentImpl();
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext.length, s.getLength());
@@ -112,7 +126,7 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 		for(int i = 0; i < plaintext.length; i++) {
 			assertEquals(plaintext[i], decrypted[i]);
 		}
-		// Second frame
+		// Second segment
 		assertTrue(decrypter.readSegment(s));
 		assertEquals(plaintext1.length, s.getLength());
 		assertEquals(1L, s.getSegmentNumber());
@@ -124,20 +138,15 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
 
 	private static class ByteArraySegmentSource implements SegmentSource {
 
-		private final byte[][] segments;
-
-		private int segmentNumber = 0;
+		private final byte[] segment;
 
-		private ByteArraySegmentSource(byte[][] frames) {
-			this.segments = frames;
+		private ByteArraySegmentSource(byte[] segment) {
+			this.segment = segment;
 		}
 
 		public boolean readSegment(Segment s) throws IOException {
-			if(segmentNumber == segments.length) return false;
-			byte[] src = segments[segmentNumber];
-			System.arraycopy(src, 0, s.getBuffer(), 0, src.length);
-			s.setLength(src.length);
-			segmentNumber++;
+			System.arraycopy(segment, 0, s.getBuffer(), 0, segment.length);
+			s.setLength(segment.length);
 			return true;
 		}
 	}
diff --git a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
index 057bcea77bb6753f5cbacb900eaefc58bf76cedd..6be55a3a74ec8080643d7a3a93d00083fe6e2b5f 100644
--- a/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullIncomingEncryptionLayer.java
@@ -9,7 +9,7 @@ import java.io.IOException;
 import java.io.InputStream;
 
 import net.sf.briar.api.FormatException;
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 /** An encryption layer that performs no encryption. */
 class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
diff --git a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
index ed8ff5d55956654d62973fb027ba9b4ae7de3aa5..574b404c1b67067b942efd72f54e3c2478f8fc26 100644
--- a/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
+++ b/test/net/sf/briar/transport/NullOutgoingEncryptionLayer.java
@@ -3,7 +3,7 @@ package net.sf.briar.transport;
 import java.io.IOException;
 import java.io.OutputStream;
 
-import net.sf.briar.api.plugins.Segment;
+import net.sf.briar.api.transport.Segment;
 
 /** An encryption layer that performs no encryption. */
 class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
diff --git a/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
index bd1246e965560f5434b5bf48cf7284e3097712bc..c0f3a903697bd6ed8b994bf11e2fef939bde5753 100644
--- a/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
+++ b/test/net/sf/briar/transport/OutgoingEncryptionLayerImplTest.java
@@ -11,7 +11,7 @@ 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.Segment;
+import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
diff --git a/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java b/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
index 42668013602ebd52c02c1bab33d215fbfd19b261..378b515d1d09505029d1a0a90808419ff87cb6fa 100644
--- a/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
+++ b/test/net/sf/briar/transport/OutgoingSegmentedEncryptionLayerTest.java
@@ -12,8 +12,8 @@ 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.Segment;
 import net.sf.briar.api.plugins.SegmentSink;
+import net.sf.briar.api.transport.Segment;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;