diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index c175fa7dfed751f551a0e54ce2bd73b62fac37eb..eb7cab4963164595884a4b529d1f9224260eae45 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -43,15 +43,15 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
 	private static final String MAC_ALGO = "HMacSHA384";
 
-	// Labels for key derivation, null-terminated
-	private static final byte[] TAG = { 'T', 'A', 'G', 0 };
-	private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 };
-	private static final byte[] MAC = { 'M', 'A', 'C', 0 };
-	// Labels for secret derivation, null-terminated
-	private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T', 0 };
-	private static final byte[] NEXT = { 'N', 'E', 'X', 'T', 0 };
-	// Label for confirmation code derivation, null-terminated
-	private static final byte[] CODE = { 'C', 'O', 'D', 'E', 0 };
+	// Labels for key derivation
+	private static final byte[] TAG = { 'T', 'A', 'G' };
+	private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E' };
+	private static final byte[] MAC = { 'M', 'A', 'C' };
+	// Labels for secret derivation
+	private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T' };
+	private static final byte[] NEXT = { 'N', 'E', 'X', 'T' };
+	// Label for confirmation code derivation
+	private static final byte[] CODE = { 'C', 'O', 'D', 'E' };
 	// Context strings for key and confirmation code derivation
 	private static final byte[] INITIATOR = { 'I' };
 	private static final byte[] RESPONDER = { 'R' };
@@ -103,18 +103,14 @@ class CryptoComponentImpl implements CryptoComponent {
 		// The secret must be usable as a key
 		if(secret.length != SECRET_KEY_BYTES)
 			throw new IllegalArgumentException();
-		// The label string must be null-terminated
-		for(int i = 0; i < label.length - 1; i++)
-			if(label[i] == 0) throw new IllegalArgumentException();
-		if(label[label.length - 1] != 0) throw new IllegalArgumentException();
 		// The label and context must leave a byte free for the counter
-		if(label.length + context.length + 5 > KEY_DERIVATION_IV_BYTES)
+		if(label.length + context.length + 4 > KEY_DERIVATION_IV_BYTES)
 			throw new IllegalArgumentException();
-		// The IV starts with the null-terminated label
+		// The IV contains the length-prefixed label and context
 		byte[] ivBytes = new byte[KEY_DERIVATION_IV_BYTES];
-		System.arraycopy(label, 0, ivBytes, 0, label.length);
-		// Next comes the length-prefixed context
-		ByteUtils.writeUint32(context.length, ivBytes, label.length);
+		ByteUtils.writeUint8(label.length, ivBytes, 0);
+		System.arraycopy(label, 0, ivBytes, 2, label.length);
+		ByteUtils.writeUint8(context.length, ivBytes, label.length + 2);
 		System.arraycopy(context, 0, ivBytes, label.length + 4, context.length);
 		// Use the secret and the IV to encrypt a blank plaintext
 		IvParameterSpec iv = new IvParameterSpec(ivBytes);
@@ -138,8 +134,7 @@ class CryptoComponentImpl implements CryptoComponent {
 			MessageDigest messageDigest = getMessageDigest();
 			byte[] ourHash = messageDigest.digest(ourPublicKey);
 			byte[] theirHash = messageDigest.digest(theirPublicKey);
-			// The initiator and responder info for the concatenation KDF are
-			// the hashes of the corresponding public keys
+			// The initiator and responder info are hashes of the public keys
 			byte[] initiatorInfo, responderInfo;
 			if(initiator) {
 				initiatorInfo = ourHash;
@@ -148,8 +143,7 @@ class CryptoComponentImpl implements CryptoComponent {
 				initiatorInfo = theirHash;
 				responderInfo = ourHash;
 			}
-			// The public info for the concatenation KDF is the invitation code
-			// as a uint32
+			// The public info is the invitation code as a uint32
 			byte[] publicInfo = new byte[4];
 			ByteUtils.writeUint32(invitationCode, publicInfo, 0);
 			// The raw secret comes from the key agreement algorithm
@@ -183,23 +177,21 @@ class CryptoComponentImpl implements CryptoComponent {
 		MessageDigest messageDigest = getMessageDigest();
 		if(messageDigest.getDigestLength() < SECRET_KEY_BYTES)
 			throw new RuntimeException();
-		// The label string must be null-terminated
-		for(int i = 0; i < label.length - 1; i++)
-			if(label[i] == 0) throw new IllegalArgumentException();
-		if(label[label.length - 1] != 0) throw new IllegalArgumentException();
-		// All other fields are length-prefixed
+		// All fields are length-prefixed
 		byte[] length = new byte[4];
-		ByteUtils.writeUint32(rawSecret.length, length, 0);
+		ByteUtils.writeUint8(rawSecret.length, length, 0);
 		messageDigest.update(length);
 		messageDigest.update(rawSecret);
+		ByteUtils.writeUint8(label.length, length, 0);
+		messageDigest.update(length);
 		messageDigest.update(label);
-		ByteUtils.writeUint32(initiatorInfo.length, length, 0);
+		ByteUtils.writeUint8(initiatorInfo.length, length, 0);
 		messageDigest.update(length);
 		messageDigest.update(initiatorInfo);
-		ByteUtils.writeUint32(responderInfo.length, length, 0);
+		ByteUtils.writeUint8(responderInfo.length, length, 0);
 		messageDigest.update(length);
 		messageDigest.update(responderInfo);
-		ByteUtils.writeUint32(publicInfo.length, length, 0);
+		ByteUtils.writeUint8(publicInfo.length, length, 0);
 		messageDigest.update(length);
 		messageDigest.update(publicInfo);
 		byte[] hash = messageDigest.digest();
diff --git a/util/net/sf/briar/util/ByteUtils.java b/util/net/sf/briar/util/ByteUtils.java
index 1c3f9bdc13d1ebb419ad97531d876eb2c4017190..e19c7e3ca39ce68393b7e6a6bb24e9597ab3397a 100644
--- a/util/net/sf/briar/util/ByteUtils.java
+++ b/util/net/sf/briar/util/ByteUtils.java
@@ -12,10 +12,17 @@ public class ByteUtils {
 	 */
 	public static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
 
+	public static void writeUint8(int i, byte[] b, int offset) {
+		if(i < 0) throw new IllegalArgumentException();
+		if(i > 255) throw new IllegalArgumentException();
+		if(b.length < offset) throw new IllegalArgumentException();
+		b[offset] = (byte) i;
+	}
+
 	public static void writeUint16(int i, byte[] b, int offset) {
 		if(i < 0) throw new IllegalArgumentException();
 		if(i > MAX_16_BIT_UNSIGNED) throw new IllegalArgumentException();
-		assert b.length >= offset + 2;
+		if(b.length < offset + 2) throw new IllegalArgumentException();
 		b[offset] = (byte) (i >> 8);
 		b[offset + 1] = (byte) (i & 0xFF);
 	}
@@ -23,7 +30,7 @@ public class ByteUtils {
 	public static void writeUint32(long i, byte[] b, int offset) {
 		if(i < 0L) throw new IllegalArgumentException();
 		if(i > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException();
-		assert b.length >= offset + 4;
+		if(b.length < offset + 4) throw new IllegalArgumentException();
 		b[offset] = (byte) (i >> 24);
 		b[offset + 1] = (byte) (i >> 16 & 0xFF);
 		b[offset + 2] = (byte) (i >> 8 & 0xFF);