diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 0f1aca360ad2212863352cad4a5b1f9a0749edf7..db329f1878d371135864eaa47fba846649847ac3 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -36,14 +36,17 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
 	private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
 
+	// Labels for key derivation, null-terminated
+	private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 };
+	private static final byte[] IV = { 'I', 'V', 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
-	private static final byte[] FRAME_I = { 'F', 'R', 'A', 'M', 'E', '_', 'I' };
-	private static final byte[] FRAME_R = { 'F', 'R', 'A', 'M', 'E', '_', 'R' };
-	private static final byte[] IV_I = { 'I', 'V', '_', 'I' };
-	private static final byte[] IV_R = { 'I', 'V', '_', 'R' };
-	private static final byte[] MAC_I = { 'M', 'A', 'C', '_', 'I' };
-	private static final byte[] MAC_R = { 'M', 'A', 'C', '_', 'R' };
-	private static final byte[] NEXT = { 'N', 'E', 'X', 'T' };
+	private static final byte[] INITIATOR = { 'I' };
+	private static final byte[] RESPONDER = { 'R' };
+	// Blank plaintext for key derivation
+	private static final byte[] KEY_DERIVATION_INPUT =
+		new byte[SECRET_KEY_BYTES];
 
 	private final KeyParser keyParser;
 	private final KeyPairGenerator keyPairGenerator;
@@ -62,47 +65,46 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	public ErasableKey deriveFrameKey(byte[] secret, boolean initiator) {
-		if(initiator) return deriveKey(secret, FRAME_I);
-		else return deriveKey(secret, FRAME_R);
+		if(initiator) return deriveKey(secret, FRAME, INITIATOR);
+		else return deriveKey(secret, FRAME, RESPONDER);
 	}
 
 	public ErasableKey deriveIvKey(byte[] secret, boolean initiator) {
-		if(initiator) return deriveKey(secret, IV_I);
-		else return deriveKey(secret, IV_R);
+		if(initiator) return deriveKey(secret, IV, INITIATOR);
+		else return deriveKey(secret, IV, RESPONDER);
 	}
 
 	public ErasableKey deriveMacKey(byte[] secret, boolean initiator) {
-		if(initiator) return deriveKey(secret, MAC_I);
-		else return deriveKey(secret, MAC_R);
+		if(initiator) return deriveKey(secret, MAC, INITIATOR);
+		else return deriveKey(secret, MAC, RESPONDER);
 	}
 
-	private ErasableKey deriveKey(byte[] secret, byte[] context) {
-		byte[] key = counterModeKdf(secret, context);
+	private ErasableKey deriveKey(byte[] secret, byte[] label, byte[] context) {
+		byte[] key = counterModeKdf(secret, label, context);
 		return new ErasableKeyImpl(key, SECRET_KEY_ALGO);
 	}
 
 	// Key derivation function based on a block cipher in CTR mode - see
 	// NIST SP 800-108, section 5.1
-	private byte[] counterModeKdf(byte[] secret, byte[] context) {
+	private byte[] counterModeKdf(byte[] secret, byte[] label, byte[] context) {
 		// The secret must be usable as a key
 		if(secret.length != SECRET_KEY_BYTES)
 			throw new IllegalArgumentException();
 		ErasableKey key = new ErasableKeyImpl(secret, SECRET_KEY_ALGO);
-		// The context must leave two bytes free for the length
-		if(context.length + 2 > SECRET_KEY_BYTES)
+		// The label and context must leave a byte free for the counter
+		if(label.length + context.length + 1 > KEY_DERIVATION_IV_BYTES)
 			throw new IllegalArgumentException();
-		byte[] input = new byte[SECRET_KEY_BYTES];
-		// The input starts with the length of the context as a big-endian int16
-		ByteUtils.writeUint16(context.length, input, 0);
-		// The remaining bytes of the input are the context
-		System.arraycopy(context, 0, input, 2, context.length);
-		// Initialise the counter to zero
-		byte[] zero = new byte[KEY_DERIVATION_IV_BYTES];
-		IvParameterSpec iv = new IvParameterSpec(zero);
+		// The IV starts with the null-terminated label
+		byte[] ivBytes = new byte[KEY_DERIVATION_IV_BYTES];
+		System.arraycopy(label, 0, ivBytes, 0, label.length);
+		// Next comes the context, leaving the last byte free for the counter
+		System.arraycopy(context, 0, ivBytes, label.length, context.length);
+		assert ivBytes[ivBytes.length - 1] == 0;
+		IvParameterSpec iv = new IvParameterSpec(ivBytes);
 		try {
 			Cipher cipher = Cipher.getInstance(KEY_DERIVATION_ALGO, PROVIDER);
 			cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-			byte[] output = cipher.doFinal(input);
+			byte[] output = cipher.doFinal(KEY_DERIVATION_INPUT);
 			assert output.length == SECRET_KEY_BYTES;
 			return output;
 		} catch(GeneralSecurityException e) {
@@ -115,11 +117,10 @@ class CryptoComponentImpl implements CryptoComponent {
 			throw new IllegalArgumentException();
 		if(connection < 0 || connection > ByteUtils.MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
-		byte[] context = new byte[NEXT.length + 6];
-		System.arraycopy(NEXT, 0, context, 0, NEXT.length);
-		ByteUtils.writeUint16(index, context, NEXT.length);
-		ByteUtils.writeUint32(connection, context, NEXT.length + 2);
-		return counterModeKdf(secret, context);
+		byte[] context = new byte[6];
+		ByteUtils.writeUint16(index, context, 0);
+		ByteUtils.writeUint32(connection, context, 2);
+		return counterModeKdf(secret, NEXT, context);
 	}
 
 	public KeyPair generateKeyPair() {