diff --git a/api/net/sf/briar/api/serial/Bytes.java b/api/net/sf/briar/api/Bytes.java
similarity index 93%
rename from api/net/sf/briar/api/serial/Bytes.java
rename to api/net/sf/briar/api/Bytes.java
index e6ad5ee52e46f3ba0afefeb9746f7cb1184952bb..77b82c1beb95763c421afc20364f6f6ce3a9f4f0 100644
--- a/api/net/sf/briar/api/serial/Bytes.java
+++ b/api/net/sf/briar/api/Bytes.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.serial;
+package net.sf.briar.api;
 
 import java.util.Arrays;
 
diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index 7684c549b1bfa6f2d01b56be43f848c961bef97c..a8f3530f459e442954af5cb4d81bb17bb85dbb95 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -10,11 +10,11 @@ import javax.crypto.SecretKey;
 
 public interface CryptoComponent {
 
-	SecretKey deriveMacKey(byte[] secret, boolean alice);
+	SecretKey deriveMacKey(byte[] secret);
 
-	SecretKey derivePacketKey(byte[] secret, boolean alice);
+	SecretKey derivePacketKey(byte[] secret);
 
-	SecretKey deriveTagKey(byte[] secret, boolean alice);
+	SecretKey deriveTagKey(byte[] secret);
 
 	KeyPair generateKeyPair();
 
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index c02a668a894c8a7de9dd41bb2ec87f01c53f677a..99cbe824d79198553fb776de824c274c44d836b4 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -68,33 +68,31 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	public SecretKey deriveMacKey(byte[] secret, boolean alice) {
-		if(alice) return deriveKey("MACA", secret);
-		else return deriveKey("MACB", secret);
+	public SecretKey deriveMacKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		if(s.getAlice()) return deriveKey("MACA", s.getIv(), s.getCiphertext());
+		else return deriveKey("MACB", s.getIv(), s.getCiphertext());
 	}
 
-	private SecretKey deriveKey(String name, byte[] secret) {
+	private SecretKey deriveKey(String name, IvParameterSpec iv,
+			byte[] ciphertext) {
 		MessageDigest digest = getMessageDigest();
 		try {
 			digest.update(name.getBytes("UTF-8"));
 		} catch(UnsupportedEncodingException e) {
 			throw new RuntimeException(e);
 		}
-		byte[] decrypted = decryptSharedSecret(secret);
+		byte[] decrypted = decryptSharedSecret(iv, ciphertext);
 		digest.update(decrypted);
-		Arrays.fill(decrypted, (byte) 0);
+		Arrays.fill(decrypted, (byte) 0); // Destroy the plaintext secret
 		return new SecretKeySpec(digest.digest(), SECRET_KEY_ALGO);
 	}
 
-	private byte[] decryptSharedSecret(byte[] secret) {
-		// The first 16 bytes of the stored secret are the IV
-		if(secret.length <= 16) throw new IllegalArgumentException();
-		IvParameterSpec iv = new IvParameterSpec(secret, 0, 16);
+	private byte[] decryptSharedSecret(IvParameterSpec iv, byte[] ciphertext) {
 		try {
-			// Decrypt and return the remainder of the stored secret
 			Cipher c = Cipher.getInstance(SECRET_STORAGE_ALGO, PROVIDER);
 			c.init(Cipher.DECRYPT_MODE, secretStorageKey, iv);
-			return c.doFinal(secret, 16, secret.length - 16);
+			return c.doFinal(ciphertext);
 		} catch(BadPaddingException e) {
 			throw new RuntimeException(e);
 		} catch(IllegalBlockSizeException e) {
@@ -112,14 +110,16 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	public SecretKey derivePacketKey(byte[] secret, boolean alice) {
-		if(alice) return deriveKey("PKTA", secret);
-		else return deriveKey("PKTB", secret);
+	public SecretKey derivePacketKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		if(s.getAlice()) return deriveKey("PKTA", s.getIv(), s.getCiphertext());
+		else return deriveKey("PKTB", s.getIv(), s.getCiphertext());
 	}
 
-	public SecretKey deriveTagKey(byte[] secret, boolean alice) {
-		if(alice) return deriveKey("TAGA", secret);
-		else return deriveKey("TAGB", secret);
+	public SecretKey deriveTagKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		if(s.getAlice()) return deriveKey("TAGA", s.getIv(), s.getCiphertext());
+		else return deriveKey("TAGB", s.getIv(), s.getCiphertext());
 	}
 
 	public KeyPair generateKeyPair() {
diff --git a/components/net/sf/briar/crypto/SharedSecret.java b/components/net/sf/briar/crypto/SharedSecret.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e9eb747e3df15ea548ed387764f4828028e6f08
--- /dev/null
+++ b/components/net/sf/briar/crypto/SharedSecret.java
@@ -0,0 +1,42 @@
+package net.sf.briar.crypto;
+
+import java.util.Arrays;
+
+import javax.crypto.spec.IvParameterSpec;
+
+class SharedSecret {
+
+	private static final int IV_BYTES = 16;
+
+	private final IvParameterSpec iv;
+	private final boolean alice;
+	private final byte[] ciphertext;
+
+	SharedSecret(byte[] secret) {
+		if(secret.length < IV_BYTES + 2) throw new IllegalArgumentException();
+		iv = new IvParameterSpec(secret, 0, IV_BYTES);
+		switch(secret[IV_BYTES]) {
+		case 0:
+			alice = false;
+			break;
+		case 1:
+			alice = true;
+			break;
+		default:
+			throw new IllegalArgumentException();
+		}
+		ciphertext = Arrays.copyOfRange(secret, IV_BYTES + 1, secret.length);
+	}
+
+	IvParameterSpec getIv() {
+		return iv;
+	}
+
+	boolean getAlice() {
+		return alice;
+	}
+
+	byte[] getCiphertext() {
+		return ciphertext;
+	}
+}
diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java
index 9638998a9767cc00d535064679863711cbc62947..92cb7a8652fc0b8d3afe3926cc77652126b4df43 100644
--- a/components/net/sf/briar/serial/ReaderImpl.java
+++ b/components/net/sf/briar/serial/ReaderImpl.java
@@ -8,7 +8,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import net.sf.briar.api.serial.Bytes;
+import net.sf.briar.api.Bytes;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.FormatException;
 import net.sf.briar.api.serial.ObjectReader;
diff --git a/components/net/sf/briar/serial/WriterImpl.java b/components/net/sf/briar/serial/WriterImpl.java
index b6d3b5b6c699d354a463d7c2bfb25e8eadbaca1a..f20b0c52e9d29603c0cb2cedc12d83aba15ff646 100644
--- a/components/net/sf/briar/serial/WriterImpl.java
+++ b/components/net/sf/briar/serial/WriterImpl.java
@@ -7,7 +7,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import net.sf.briar.api.serial.Bytes;
+import net.sf.briar.api.Bytes;
 import net.sf.briar.api.serial.Tag;
 import net.sf.briar.api.serial.Writable;
 import net.sf.briar.api.serial.Writer;
diff --git a/test/net/sf/briar/crypto/CounterModeTest.java b/test/net/sf/briar/crypto/CounterModeTest.java
index 176296af9dafd14ae995861cdf0ddd5ffce1e1ae..7ce6dbcc29515fb98e09f59a458c2bad2ba50385 100644
--- a/test/net/sf/briar/crypto/CounterModeTest.java
+++ b/test/net/sf/briar/crypto/CounterModeTest.java
@@ -11,7 +11,7 @@ import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import junit.framework.TestCase;
-import net.sf.briar.api.serial.Bytes;
+import net.sf.briar.api.Bytes;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.junit.Test;
diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java
index efe4866c0e9a8a03df29535a8e456ee513a8ca62..2187e67bf74764ac9ae6e83a8e9975899ab12e71 100644
--- a/test/net/sf/briar/serial/ReaderImplTest.java
+++ b/test/net/sf/briar/serial/ReaderImplTest.java
@@ -9,10 +9,10 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import junit.framework.TestCase;
+import net.sf.briar.api.Bytes;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.FormatException;
 import net.sf.briar.api.serial.ObjectReader;
-import net.sf.briar.api.serial.Bytes;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.util.StringUtils;