diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java
index a1573ec7a9b88053f2366acb79f222c044c252c4..c6cd78c4cb8f36b0fc834cd6634b26d768351da3 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/CryptoComponent.java
@@ -10,8 +10,6 @@ public interface CryptoComponent {
 
 	SecretKey generateSecretKey();
 
-	MessageDigest getMessageDigest();
-
 	PseudoRandom getPseudoRandom(int seed1, int seed2);
 
 	SecureRandom getSecureRandom();
@@ -164,8 +162,17 @@ public interface CryptoComponent {
 	/**
 	 * Returns the hash of the given inputs. The inputs are unambiguously
 	 * combined by prefixing each input with its length.
+	 *
+	 * @param label A label specific to this hash to ensure that hashes
+	 *              calculated for distinct purposes don't collide.
+	 */
+	byte[] hash(String label, byte[]... inputs);
+
+	/**
+	 * Returns the length of hashes produced by
+	 * the {@link CryptoComponent#hash(String, byte[]...)} method.
 	 */
-	byte[] hash(byte[]... inputs);
+	int getHashLength();
 
 	/**
 	 * Returns a message authentication code with the given key over the
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/MessageDigest.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/MessageDigest.java
deleted file mode 100644
index f55e169d0ecc1ba86decdb87b46abc3a894479bd..0000000000000000000000000000000000000000
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/MessageDigest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.briarproject.bramble.api.crypto;
-
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-
-@NotNullByDefault
-public interface MessageDigest {
-
-	/**
-	 * @see {@link java.security.MessageDigest#digest()}
-	 */
-	byte[] digest();
-
-	/**
-	 * @see {@link java.security.MessageDigest#digest(byte[])}
-	 */
-	byte[] digest(byte[] input);
-
-	/**
-	 * @see {@link java.security.MessageDigest#digest(byte[], int, int)}
-	 */
-	int digest(byte[] buf, int offset, int len);
-
-	/**
-	 * @see {@link java.security.MessageDigest#getDigestLength()}
-	 */
-	int getDigestLength();
-
-	/**
-	 * @see {@link java.security.MessageDigest#reset()}
-	 */
-	void reset();
-
-	/**
-	 * @see {@link java.security.MessageDigest#update(byte)}
-	 */
-	void update(byte input);
-
-	/**
-	 * @see {@link java.security.MessageDigest#update(byte[])}
-	 */
-	void update(byte[] input);
-
-	/**
-	 * @see {@link java.security.MessageDigest#update(byte[], int, int)}
-	 */
-	void update(byte[] input, int offset, int len);
-}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java
index c035a0ce089be9d014f70459125504c23750132e..0963e0049e20ca3fb3ac8dc14950e86c0dae3898 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorId.java
@@ -3,8 +3,6 @@ package org.briarproject.bramble.api.identity;
 import org.briarproject.bramble.api.UniqueId;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 
-import java.nio.charset.Charset;
-
 import javax.annotation.concurrent.ThreadSafe;
 
 /**
@@ -18,8 +16,7 @@ public class AuthorId extends UniqueId {
 	/**
 	 * Label for hashing authors to calculate their identities.
 	 */
-	public static final byte[] LABEL =
-			"AUTHOR_ID".getBytes(Charset.forName("US-ASCII"));
+	public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
 
 	public AuthorId(byte[] id) {
 		super(id);
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java
index bea1186f13768373d04af940dc445e4f79441160..cdd6b2d3fba5dd4ad95968e6e3360b1be120a695 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/GroupId.java
@@ -3,8 +3,6 @@ package org.briarproject.bramble.api.sync;
 import org.briarproject.bramble.api.UniqueId;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 
-import java.nio.charset.Charset;
-
 import javax.annotation.concurrent.ThreadSafe;
 
 /**
@@ -17,8 +15,7 @@ public class GroupId extends UniqueId {
 	/**
 	 * Label for hashing groups to calculate their identifiers.
 	 */
-	public static final byte[] LABEL =
-			"GROUP_ID".getBytes(Charset.forName("US-ASCII"));
+	public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
 
 	public GroupId(byte[] id) {
 		super(id);
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java
index 3a1075dd9c77c1262fdf7a95c858d8d6a2095355..84389bbcc83371980d7e71a62ca5137079e011d3 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageId.java
@@ -3,8 +3,6 @@ package org.briarproject.bramble.api.sync;
 import org.briarproject.bramble.api.UniqueId;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 
-import java.nio.charset.Charset;
-
 import javax.annotation.concurrent.ThreadSafe;
 
 /**
@@ -18,8 +16,7 @@ public class MessageId extends UniqueId {
 	/**
 	 * Label for hashing messages to calculate their identifiers.
 	 */
-	public static final byte[] LABEL =
-			"MESSAGE_ID".getBytes(Charset.forName("US-ASCII"));
+	public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
 
 	public MessageId(byte[] id) {
 		super(id);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
index 879ee259541f51fd42e45be70150527b718711fe..ae25da4e189bdeb559267f07fbdac4dbbbe90140 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
@@ -3,7 +3,6 @@ package org.briarproject.bramble.crypto;
 import org.briarproject.bramble.api.crypto.CryptoComponent;
 import org.briarproject.bramble.api.crypto.KeyPair;
 import org.briarproject.bramble.api.crypto.KeyParser;
-import org.briarproject.bramble.api.crypto.MessageDigest;
 import org.briarproject.bramble.api.crypto.PrivateKey;
 import org.briarproject.bramble.api.crypto.PseudoRandom;
 import org.briarproject.bramble.api.crypto.PublicKey;
@@ -58,6 +57,7 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final int PBKDF_SALT_BYTES = 32; // 256 bits
 	private static final int PBKDF_TARGET_MILLIS = 500;
 	private static final int PBKDF_SAMPLES = 30;
+	private static final int HASH_SIZE = 256 / 8;
 
 	private static byte[] ascii(String s) {
 		return s.getBytes(Charset.forName("US-ASCII"));
@@ -73,9 +73,11 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
 	private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
 	// Hash label for BQP public key commitment derivation
-	private static final byte[] COMMIT = ascii("COMMIT");
+	private static final String COMMIT =
+			"org.briarproject.bramble.COMMIT";
 	// Hash label for shared secret derivation
-	private static final byte[] SHARED_SECRET = ascii("SHARED_SECRET");
+	private static final String SHARED_SECRET =
+			"org.briarproject.bramble.SHARED_SECRET";
 	// KDF label for BQP confirmation key derivation
 	private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
 	// KDF label for master key derivation
@@ -129,11 +131,6 @@ class CryptoComponentImpl implements CryptoComponent {
 		return new SecretKey(b);
 	}
 
-	@Override
-	public MessageDigest getMessageDigest() {
-		return new DigestWrapper(new Blake2sDigest());
-	}
-
 	@Override
 	public PseudoRandom getPseudoRandom(int seed1, int seed2) {
 		return new PseudoRandomImpl(seed1, seed2);
@@ -428,15 +425,26 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	@Override
-	public byte[] hash(byte[]... inputs) {
-		MessageDigest digest = getMessageDigest();
+	public byte[] hash(String label, byte[]... inputs) {
+		byte[] labelBytes = StringUtils.toUtf8(label);
+		Digest digest = new Blake2sDigest();
 		byte[] length = new byte[INT_32_BYTES];
+		ByteUtils.writeUint32(labelBytes.length, length, 0);
+		digest.update(length, 0, length.length);
+		digest.update(labelBytes, 0, labelBytes.length);
 		for (byte[] input : inputs) {
 			ByteUtils.writeUint32(input.length, length, 0);
-			digest.update(length);
-			digest.update(input);
+			digest.update(length, 0, length.length);
+			digest.update(input, 0, input.length);
 		}
-		return digest.digest();
+		byte[] output = new byte[digest.getDigestSize()];
+		digest.doFinal(output, 0);
+		return output;
+	}
+
+	@Override
+	public int getHashLength() {
+		return HASH_SIZE;
 	}
 
 	@Override
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/DigestWrapper.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/DigestWrapper.java
deleted file mode 100644
index 077a4012ad273a280992454899137bce15382af3..0000000000000000000000000000000000000000
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/DigestWrapper.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.briarproject.bramble.crypto;
-
-import org.briarproject.bramble.api.crypto.MessageDigest;
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.spongycastle.crypto.Digest;
-
-import javax.annotation.concurrent.NotThreadSafe;
-
-@NotThreadSafe
-@NotNullByDefault
-class DigestWrapper implements MessageDigest {
-
-	private final Digest digest;
-
-	DigestWrapper(Digest digest) {
-		this.digest = digest;
-	}
-
-	@Override
-	public byte[] digest() {
-		byte[] hash = new byte[digest.getDigestSize()];
-		digest.doFinal(hash, 0);
-		return hash;
-	}
-
-	@Override
-	public byte[] digest(byte[] input) {
-		update(input);
-		return digest();
-	}
-
-	@Override
-	public int digest(byte[] buf, int offset, int len) {
-		byte[] hash = digest();
-		len = Math.min(len, hash.length);
-		System.arraycopy(hash, 0, buf, offset, len);
-		return len;
-	}
-
-	@Override
-	public int getDigestLength() {
-		return digest.getDigestSize();
-	}
-
-	@Override
-	public void reset() {
-		digest.reset();
-	}
-
-	@Override
-	public void update(byte input) {
-		digest.update(input);
-	}
-
-	@Override
-	public void update(byte[] input) {
-		digest.update(input, 0, input.length);
-	}
-
-	@Override
-	public void update(byte[] input, int offset, int len) {
-		digest.update(input, offset, len);
-	}
-}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/DoubleDigest.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/DoubleDigest.java
index 3740472043e5b0f898136635187241f7786b184a..2bd71fa78a16fa9d5e60ef829a25d5d8ce657742 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/DoubleDigest.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/DoubleDigest.java
@@ -1,6 +1,5 @@
 package org.briarproject.bramble.crypto;
 
-import org.briarproject.bramble.api.crypto.MessageDigest;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.spongycastle.crypto.Digest;
 
@@ -17,7 +16,7 @@ import javax.annotation.concurrent.NotThreadSafe;
  */
 @NotThreadSafe
 @NotNullByDefault
-class DoubleDigest implements MessageDigest {
+class DoubleDigest implements Digest {
 
 	private final Digest delegate;
 
@@ -25,8 +24,7 @@ class DoubleDigest implements MessageDigest {
 		this.delegate = delegate;
 	}
 
-	@Override
-	public byte[] digest() {
+	private byte[] digest() {
 		byte[] digest = new byte[delegate.getDigestSize()];
 		delegate.doFinal(digest, 0); // h(m)
 		delegate.update(digest, 0, digest.length);
@@ -34,13 +32,6 @@ class DoubleDigest implements MessageDigest {
 		return digest;
 	}
 
-	@Override
-	public byte[] digest(byte[] input) {
-		delegate.update(input, 0, input.length);
-		return digest();
-	}
-
-	@Override
 	public int digest(byte[] buf, int offset, int len) {
 		byte[] digest = digest();
 		len = Math.min(len, digest.length);
@@ -49,10 +40,15 @@ class DoubleDigest implements MessageDigest {
 	}
 
 	@Override
-	public int getDigestLength() {
+	public int getDigestSize() {
 		return delegate.getDigestSize();
 	}
 
+	@Override
+	public String getAlgorithmName() {
+		return "Double " + delegate.getAlgorithmName();
+	}
+
 	@Override
 	public void reset() {
 		delegate.reset();
@@ -63,7 +59,6 @@ class DoubleDigest implements MessageDigest {
 		delegate.update(input);
 	}
 
-	@Override
 	public void update(byte[] input) {
 		delegate.update(input, 0, input.length);
 	}
@@ -72,4 +67,10 @@ class DoubleDigest implements MessageDigest {
 	public void update(byte[] input, int offset, int len) {
 		delegate.update(input, offset, len);
 	}
+
+	@Override
+	public int doFinal(byte[] out, int outOff) {
+		return digest(out, outOff, delegate.getDigestSize());
+	}
+
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/FortunaGenerator.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/FortunaGenerator.java
index 2c4ef3ef68584aece066a0d245e795523cf7357b..5c36a931fdc20ee390442b74c7eba46bd36664a9 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/FortunaGenerator.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/FortunaGenerator.java
@@ -1,6 +1,5 @@
 package org.briarproject.bramble.crypto;
 
-import org.briarproject.bramble.api.crypto.MessageDigest;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.spongycastle.crypto.BlockCipher;
 import org.spongycastle.crypto.digests.SHA256Digest;
@@ -27,7 +26,7 @@ class FortunaGenerator {
 	private final Lock lock = new ReentrantLock();
 
 	// The following are locking: lock
-	private final MessageDigest digest = new DoubleDigest(new SHA256Digest());
+	private final DoubleDigest digest = new DoubleDigest(new SHA256Digest());
 	private final BlockCipher cipher = new AESLightEngine();
 	private final byte[] key = new byte[KEY_BYTES];
 	private final byte[] counter = new byte[BLOCK_BYTES];
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/invitation/Connector.java b/bramble-core/src/main/java/org/briarproject/bramble/invitation/Connector.java
index 0fb91aa8a4be140a266cf2bf4fab6b7fc5848525..4c1a63c6732e7996ab2e19466d6d7a9a4b023741 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/invitation/Connector.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/invitation/Connector.java
@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.contact.ContactExchangeTask;
 import org.briarproject.bramble.api.crypto.CryptoComponent;
 import org.briarproject.bramble.api.crypto.KeyPair;
 import org.briarproject.bramble.api.crypto.KeyParser;
-import org.briarproject.bramble.api.crypto.MessageDigest;
 import org.briarproject.bramble.api.crypto.PseudoRandom;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.data.BdfReader;
@@ -35,6 +34,8 @@ abstract class Connector extends Thread {
 
 	private static final Logger LOG =
 			Logger.getLogger(Connector.class.getName());
+	private static final String LABEL_PUBLIC_KEY =
+			"org.briarproject.bramble.invitation.PUBLIC_KEY";
 
 	protected final CryptoComponent crypto;
 	protected final BdfReaderFactory bdfReaderFactory;
@@ -48,7 +49,6 @@ abstract class Connector extends Thread {
 
 	private final KeyPair keyPair;
 	private final KeyParser keyParser;
-	private final MessageDigest messageDigest;
 
 	Connector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
 			BdfWriterFactory bdfWriterFactory,
@@ -66,7 +66,6 @@ abstract class Connector extends Thread {
 		pluginName = plugin.getClass().getName();
 		keyPair = crypto.generateAgreementKeyPair();
 		keyParser = crypto.getAgreementKeyParser();
-		messageDigest = crypto.getMessageDigest();
 	}
 
 	@Nullable
@@ -78,13 +77,15 @@ abstract class Connector extends Thread {
 	}
 
 	void sendPublicKeyHash(BdfWriter w) throws IOException {
-		w.writeRaw(messageDigest.digest(keyPair.getPublic().getEncoded()));
+		byte[] hash =
+				crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
+		w.writeRaw(hash);
 		w.flush();
 		if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent hash");
 	}
 
 	byte[] receivePublicKeyHash(BdfReader r) throws IOException {
-		int hashLength = messageDigest.getDigestLength();
+		int hashLength = crypto.getHashLength();
 		byte[] b = r.readRaw(hashLength);
 		if (b.length < hashLength) throw new FormatException();
 		if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash");
@@ -109,7 +110,9 @@ abstract class Connector extends Thread {
 	SecretKey deriveMasterSecret(byte[] hash, byte[] key, boolean alice)
 			throws GeneralSecurityException {
 		// Check that the hash matches the key
-		if (!Arrays.equals(hash, messageDigest.digest(key))) {
+		byte[] keyHash =
+				crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
+		if (!Arrays.equals(hash, keyHash)) {
 			if (LOG.isLoggable(INFO))
 				LOG.info(pluginName + " hash does not match key");
 			throw new GeneralSecurityException();
diff --git a/briar-tests/src/org/briarproject/bramble/crypto/FortunaSecureRandomTest.java b/briar-tests/src/org/briarproject/bramble/crypto/FortunaSecureRandomTest.java
index 86e302ec0c9958d5e94d3f6a7f69f74fd65e7508..6e789aa7fedb2c79b5eb0156a1956ea527a67536 100644
--- a/briar-tests/src/org/briarproject/bramble/crypto/FortunaSecureRandomTest.java
+++ b/briar-tests/src/org/briarproject/bramble/crypto/FortunaSecureRandomTest.java
@@ -1,7 +1,6 @@
 package org.briarproject.bramble.crypto;
 
 import org.briarproject.BriarTestCase;
-import org.briarproject.bramble.api.crypto.MessageDigest;
 import org.junit.Test;
 import org.spongycastle.crypto.BlockCipher;
 import org.spongycastle.crypto.digests.SHA256Digest;
@@ -27,7 +26,7 @@ public class FortunaSecureRandomTest extends BriarTestCase {
 		byte[] counter = new byte[16], output = new byte[16];
 		byte[] newKey = new byte[32];
 		// Calculate the initial key
-		MessageDigest digest = new DoubleDigest(new SHA256Digest());
+		DoubleDigest digest = new DoubleDigest(new SHA256Digest());
 		digest.update(key);
 		digest.update(seed);
 		digest.digest(key, 0, 32);