diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/Blake2sDigest.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/Blake2sDigest.java
deleted file mode 100644
index a8e132a9b15a88c743f032228be0aa36bc6ec1e3..0000000000000000000000000000000000000000
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/Blake2sDigest.java
+++ /dev/null
@@ -1,547 +0,0 @@
-package org.briarproject.bramble.crypto;
-
-/*
-  The BLAKE2 cryptographic hash function was designed by Jean-
-  Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
-  Winnerlein.
-
-  Reference Implementation and Description can be found at: https://blake2.net/
-  RFC: https://tools.ietf.org/html/rfc7693
-
-  This implementation does not support the Tree Hashing Mode.
-
-  For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based
-  message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2.
-
-         Algorithm     | Target | Collision | Hash | Hash ASN.1 |
-            Identifier |  Arch  |  Security |  nn  | OID Suffix |
-        ---------------+--------+-----------+------+------------+
-         id-blake2s128 | 32-bit |   2**64   |  16  |   x.2.4    |
-         id-blake2s160 | 32-bit |   2**80   |  20  |   x.2.5    |
-         id-blake2s224 | 32-bit |   2**112  |  28  |   x.2.7    |
-         id-blake2s256 | 32-bit |   2**128  |  32  |   x.2.8    |
-        ---------------+--------+-----------+------+------------+
-
-  Based on the BouncyCastle implementation of BLAKE2b. License:
-
-  Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc.
-  (http://www.bouncycastle.org)
-
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-
-  The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-  SOFTWARE.
- */
-
-import org.spongycastle.crypto.ExtendedDigest;
-import org.spongycastle.util.Arrays;
-
-/**
- * Implementation of the cryptographic hash function BLAKE2s.
- * <p/>
- * BLAKE2s offers a built-in keying mechanism to be used directly
- * for authentication ("Prefix-MAC") rather than a HMAC construction.
- * <p/>
- * BLAKE2s offers a built-in support for a salt for randomized hashing
- * and a personal string for defining a unique hash function for each application.
- * <p/>
- * BLAKE2s is optimized for 32-bit platforms and produces digests of any size
- * between 1 and 32 bytes.
- */
-public class Blake2sDigest implements ExtendedDigest {
-	/** BLAKE2s Initialization Vector **/
-	private static final int blake2s_IV[] =
-			// Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
-			// The same as SHA-256 IV.
-			{
-					0x6a09e667, 0xbb67ae85, 0x3c6ef372,
-					0xa54ff53a, 0x510e527f, 0x9b05688c,
-					0x1f83d9ab, 0x5be0cd19
-			};
-
-	/** Message word permutations **/
-	private static final byte[][] blake2s_sigma =
-			{
-					{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
-					{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
-					{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
-					{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
-					{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
-					{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
-					{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
-					{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
-					{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
-					{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }
-			};
-
-	private static final int ROUNDS = 10; // to use for Catenas H'
-	private static final int BLOCK_LENGTH_BYTES = 64;// bytes
-
-	// General parameters:
-	private int digestLength = 32; // 1- 32 bytes
-	private int keyLength = 0; // 0 - 32 bytes for keyed hashing for MAC
-	private byte[] salt = null;
-	private byte[] personalization = null;
-	private byte[] key = null;
-
-	// Tree hashing parameters:
-	// Because this class does not implement the Tree Hashing Mode,
-	// these parameters can be treated as constants (see init() function)
-	/*
-	 * private int fanout = 1; // 0-255
-	 * private int depth = 1; // 1 - 255
-	 * private int leafLength= 0;
-	 * private long nodeOffset = 0L;
-	 * private int nodeDepth = 0;
-	 * private int innerHashLength = 0;
-	 */
-
-	/**
-	 * Whenever this buffer overflows, it will be processed in the compress()
-	 * function. For performance issues, long messages will not use this buffer.
-	 */
-	private byte[] buffer = null;
-	/** Position of last inserted byte **/
-	private int bufferPos = 0;// a value from 0 up to BLOCK_LENGTH_BYTES
-
-	/** Internal state, in the BLAKE2 paper it is called v **/
-	private int[] internalState = new int[16];
-	/** State vector, in the BLAKE2 paper it is called h **/
-	private int[] chainValue = null;
-
-	// counter (counts bytes): Length up to 2^64 are supported
-	/** holds least significant bits of counter **/
-	private int t0 = 0;
-	/** holds most significant bits of counter **/
-	private int t1 = 0;
-	/** finalization flag, for last block: ~0 **/
-	private int f0 = 0;
-
-	// For Tree Hashing Mode, not used here:
-	// private long f1 = 0L; // finalization flag, for last node: ~0L
-
-	/**
-	 * BLAKE2s-256 for hashing.
-	 */
-	public Blake2sDigest() {
-		this(256);
-	}
-
-	public Blake2sDigest(Blake2sDigest digest) {
-		this.bufferPos = digest.bufferPos;
-		this.buffer = Arrays.clone(digest.buffer);
-		this.keyLength = digest.keyLength;
-		this.key = Arrays.clone(digest.key);
-		this.digestLength = digest.digestLength;
-		this.chainValue = Arrays.clone(digest.chainValue);
-		this.personalization = Arrays.clone(digest.personalization);
-	}
-
-	/**
-	 * BLAKE2s for hashing.
-	 *
-	 * @param digestBits the desired digest length in bits. Must be one of
-	 *                     [128, 160, 224, 256].
-	 */
-	public Blake2sDigest(int digestBits) {
-		if (digestBits != 128 && digestBits != 160 &&
-				digestBits != 224 && digestBits != 256) {
-			throw new IllegalArgumentException(
-					"BLAKE2s digest restricted to one of [128, 160, 224, 256]");
-		}
-		buffer = new byte[BLOCK_LENGTH_BYTES];
-		keyLength = 0;
-		digestLength = digestBits / 8;
-		init();
-	}
-
-	/**
-	 * BLAKE2s for authentication ("Prefix-MAC mode").
-	 * <p/>
-	 * After calling the doFinal() method, the key will remain to be used for
-	 * further computations of this instance. The key can be overwritten using
-	 * the clearKey() method.
-	 *
-	 * @param key a key up to 32 bytes or null
-	 */
-	public Blake2sDigest(byte[] key) {
-		buffer = new byte[BLOCK_LENGTH_BYTES];
-		if (key != null) {
-			if (key.length > 32) {
-				throw new IllegalArgumentException(
-						"Keys > 32 are not supported");
-			}
-			this.key = new byte[key.length];
-			System.arraycopy(key, 0, this.key, 0, key.length);
-
-			keyLength = key.length;
-			System.arraycopy(key, 0, buffer, 0, key.length);
-			bufferPos = BLOCK_LENGTH_BYTES; // zero padding
-		}
-		digestLength = 32;
-		init();
-	}
-
-	/**
-	 * BLAKE2s with key, required digest length, salt and personalization.
-	 * <p/>
-	 * After calling the doFinal() method, the key, the salt and the personal
-	 * string will remain and might be used for further computations with this
-	 * instance. The key can be overwritten using the clearKey() method, the
-	 * salt (pepper) can be overwritten using the clearSalt() method.
-	 *
-	 * @param key a key up to 32 bytes or null
-	 * @param digestBytes from 1 up to 32 bytes
-	 * @param salt 8 bytes or null
-	 * @param personalization 8 bytes or null
-	 */
-	public Blake2sDigest(byte[] key, int digestBytes, byte[] salt,
-			byte[] personalization) {
-		buffer = new byte[BLOCK_LENGTH_BYTES];
-		if (digestBytes < 1 || digestBytes > 32) {
-			throw new IllegalArgumentException(
-					"Invalid digest length (required: 1 - 32)");
-		}
-		digestLength = digestBytes;
-		if (salt != null) {
-			if (salt.length != 8) {
-				throw new IllegalArgumentException(
-						"Salt length must be exactly 8 bytes");
-			}
-			this.salt = new byte[8];
-			System.arraycopy(salt, 0, this.salt, 0, salt.length);
-		}
-		if (personalization != null) {
-			if (personalization.length != 8) {
-				throw new IllegalArgumentException(
-						"Personalization length must be exactly 8 bytes");
-			}
-			this.personalization = new byte[8];
-			System.arraycopy(personalization, 0, this.personalization, 0,
-					personalization.length);
-		}
-		if (key != null) {
-			if (key.length > 32) {
-				throw new IllegalArgumentException(
-						"Keys > 32 bytes are not supported");
-			}
-			this.key = new byte[key.length];
-			System.arraycopy(key, 0, this.key, 0, key.length);
-
-			keyLength = key.length;
-			System.arraycopy(key, 0, buffer, 0, key.length);
-			bufferPos = BLOCK_LENGTH_BYTES; // zero padding
-		}
-		init();
-	}
-
-	// initialize chainValue
-	private void init() {
-		if (chainValue == null) {
-			chainValue = new int[8];
-
-			chainValue[0] = blake2s_IV[0]
-					^ (digestLength | (keyLength << 8) | 0x1010000);
-			// 0x1010000 = ((fanout << 16) | (depth << 24));
-			// with fanout = 1; depth = 0;
-			chainValue[1] = blake2s_IV[1];// ^ leafLength; with leafLength = 0;
-			chainValue[2] = blake2s_IV[2];// ^ nodeOffset; with nodeOffset = 0;
-			chainValue[3] = blake2s_IV[3];// ^ ( (nodeOffset << 32) |
-			// (nodeDepth << 16) | (innerHashLength << 24) );
-			// with nodeDepth = 0; innerHashLength = 0;
-
-			chainValue[4] = blake2s_IV[4];
-			chainValue[5] = blake2s_IV[5];
-			if (salt != null) {
-				chainValue[4] ^= (bytes2int(salt, 0));
-				chainValue[5] ^= (bytes2int(salt, 4));
-			}
-
-			chainValue[6] = blake2s_IV[6];
-			chainValue[7] = blake2s_IV[7];
-			if (personalization != null) {
-				chainValue[6] ^= (bytes2int(personalization, 0));
-				chainValue[7] ^= (bytes2int(personalization, 4));
-			}
-		}
-	}
-
-	private void initializeInternalState() {
-		// initialize v:
-		System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
-		System.arraycopy(blake2s_IV, 0, internalState, chainValue.length, 4);
-		internalState[12] = t0 ^ blake2s_IV[4];
-		internalState[13] = t1 ^ blake2s_IV[5];
-		internalState[14] = f0 ^ blake2s_IV[6];
-		internalState[15] = blake2s_IV[7];// ^ f1 with f1 = 0
-	}
-
-	/**
-	 * Update the message digest with a single byte.
-	 *
-	 * @param b the input byte to be entered.
-	 */
-	public void update(byte b) {
-		int remainingLength; // left bytes of buffer
-
-		// process the buffer if full else add to buffer:
-		remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
-		if (remainingLength == 0) { // full buffer
-			t0 += BLOCK_LENGTH_BYTES;
-			if (t0 == 0) { // if message > 2^32
-				t1++;
-			}
-			compress(buffer, 0);
-			Arrays.fill(buffer, (byte)0);// clear buffer
-			buffer[0] = b;
-			bufferPos = 1;
-		} else {
-			buffer[bufferPos] = b;
-			bufferPos++;
-		}
-	}
-
-	/**
-	 * Update the message digest with a block of bytes.
-	 *
-	 * @param message the byte array containing the data.
-	 * @param offset the offset into the byte array where the data starts.
-	 * @param len the length of the data.
-	 */
-	public void update(byte[] message, int offset, int len) {
-		if (message == null || len == 0)
-			return;
-
-		int remainingLength = 0; // left bytes of buffer
-
-		if (bufferPos != 0) { // commenced, incomplete buffer
-
-			// complete the buffer:
-			remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
-			if (remainingLength < len) { // full buffer + at least 1 byte
-				System.arraycopy(message, offset, buffer, bufferPos,
-						remainingLength);
-				t0 += BLOCK_LENGTH_BYTES;
-				if (t0 == 0) { // if message > 2^32
-					t1++;
-				}
-				compress(buffer, 0);
-				bufferPos = 0;
-				Arrays.fill(buffer, (byte) 0);// clear buffer
-			} else {
-				System.arraycopy(message, offset, buffer, bufferPos, len);
-				bufferPos += len;
-				return;
-			}
-		}
-
-		// process blocks except last block (also if last block is full)
-		int messagePos;
-		int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
-		for (messagePos = offset + remainingLength;
-				messagePos < blockWiseLastPos;
-				messagePos += BLOCK_LENGTH_BYTES) { // block wise 64 bytes
-			// without buffer:
-			t0 += BLOCK_LENGTH_BYTES;
-			if (t0 == 0) {
-				t1++;
-			}
-			compress(message, messagePos);
-		}
-
-		// fill the buffer with left bytes, this might be a full block
-		System.arraycopy(message, messagePos, buffer, 0, offset + len
-				- messagePos);
-		bufferPos += offset + len - messagePos;
-	}
-
-	/**
-	 * Close the digest, producing the final digest value. The doFinal() call
-	 * leaves the digest reset. Key, salt and personal string remain.
-	 *
-	 * @param out the array the digest is to be copied into.
-	 * @param outOffset the offset into the out array the digest is to start at.
-	 */
-	public int doFinal(byte[] out, int outOffset) {
-		f0 = 0xFFFFFFFF;
-		t0 += bufferPos;
-		// bufferPos may be < 64, so (t0 == 0) does not work
-		// for 2^32 < message length > 2^32 - 63
-		if ((t0 < 0) && (bufferPos > -t0)) {
-			t1++;
-		}
-		compress(buffer, 0);
-		Arrays.fill(buffer, (byte) 0);// Holds eventually the key if input is null
-		Arrays.fill(internalState, 0);
-
-		for (int i = 0; i < chainValue.length && (i * 4 < digestLength); i++) {
-			byte[] bytes = int2bytes(chainValue[i]);
-
-			if (i * 4 < digestLength - 4) {
-				System.arraycopy(bytes, 0, out, outOffset + i * 4, 4);
-			} else {
-				System.arraycopy(bytes, 0, out, outOffset + i * 4,
-						digestLength - (i * 4));
-			}
-		}
-
-		Arrays.fill(chainValue, 0);
-
-		reset();
-
-		return digestLength;
-	}
-
-	/**
-	 * Reset the digest back to its initial state. The key, the salt and the
-	 * personal string will remain for further computations.
-	 */
-	public void reset() {
-		bufferPos = 0;
-		f0 = 0;
-		t0 = 0;
-		t1 = 0;
-		chainValue = null;
-		if (key != null) {
-			Arrays.fill(buffer, (byte) 0);
-			System.arraycopy(key, 0, buffer, 0, key.length);
-			bufferPos = BLOCK_LENGTH_BYTES; // zero padding
-		}
-		init();
-	}
-
-	private void compress(byte[] message, int messagePos) {
-		initializeInternalState();
-
-		int[] m = new int[16];
-		for (int j = 0; j < 16; j++) {
-			m[j] = bytes2int(message, messagePos + j * 4);
-		}
-
-		for (int round = 0; round < ROUNDS; round++) {
-
-			// G apply to columns of internalState:m[blake2s_sigma[round][2 *
-			// blockPos]] /+1
-			G(m[blake2s_sigma[round][0]], m[blake2s_sigma[round][1]], 0, 4, 8,
-					12);
-			G(m[blake2s_sigma[round][2]], m[blake2s_sigma[round][3]], 1, 5, 9,
-					13);
-			G(m[blake2s_sigma[round][4]], m[blake2s_sigma[round][5]], 2, 6, 10,
-					14);
-			G(m[blake2s_sigma[round][6]], m[blake2s_sigma[round][7]], 3, 7, 11,
-					15);
-			// G apply to diagonals of internalState:
-			G(m[blake2s_sigma[round][8]], m[blake2s_sigma[round][9]], 0, 5, 10,
-					15);
-			G(m[blake2s_sigma[round][10]], m[blake2s_sigma[round][11]], 1, 6,
-					11, 12);
-			G(m[blake2s_sigma[round][12]], m[blake2s_sigma[round][13]], 2, 7,
-					8, 13);
-			G(m[blake2s_sigma[round][14]], m[blake2s_sigma[round][15]], 3, 4,
-					9, 14);
-		}
-
-		// update chain values:
-		for (int offset = 0; offset < chainValue.length; offset++) {
-			chainValue[offset] = chainValue[offset] ^ internalState[offset]
-					^ internalState[offset + 8];
-		}
-	}
-
-	private void G(int m1, int m2, int posA, int posB, int posC, int posD) {
-		internalState[posA] = internalState[posA] + internalState[posB] + m1;
-		internalState[posD] = rotr32(internalState[posD] ^ internalState[posA],
-				16);
-		internalState[posC] = internalState[posC] + internalState[posD];
-		internalState[posB] = rotr32(internalState[posB] ^ internalState[posC],
-				12);
-		internalState[posA] = internalState[posA] + internalState[posB] + m2;
-		internalState[posD] = rotr32(internalState[posD] ^ internalState[posA],
-				8);
-		internalState[posC] = internalState[posC] + internalState[posD];
-		internalState[posB] = rotr32(internalState[posB] ^ internalState[posC],
-				7);
-	}
-
-	private int rotr32(int x, int rot) {
-		return x >>> rot | (x << (32 - rot));
-	}
-
-	// convert one int value in byte array
-	// little-endian byte order!
-	private byte[] int2bytes(int intValue) {
-		return new byte[] {
-				(byte) intValue, (byte) (intValue >> 8),
-				(byte) (intValue >> 16), (byte) (intValue >> 24)
-		};
-	}
-
-	// little-endian byte order!
-	private int bytes2int(byte[] byteArray, int offset) {
-		return (((int) byteArray[offset] & 0xFF)
-				| (((int) byteArray[offset + 1] & 0xFF) << 8)
-				| (((int) byteArray[offset + 2] & 0xFF) << 16)
-				| (((int) byteArray[offset + 3] & 0xFF) << 24));
-	}
-
-	/**
-	 * Return the algorithm name.
-	 *
-	 * @return the algorithm name
-	 */
-	public String getAlgorithmName() {
-		return "BLAKE2s";
-	}
-
-	/**
-	 * Return the size in bytes of the digest produced by this message digest.
-	 *
-	 * @return the size in bytes of the digest produced by this message digest.
-	 */
-	public int getDigestSize() {
-		return digestLength;
-	}
-
-	/**
-	 * Return the size in bytes of the internal buffer the digest applies its
-	 * compression function to.
-	 *
-	 * @return byte length of the digest's internal buffer.
-	 */
-	public int getByteLength() {
-		return BLOCK_LENGTH_BYTES;
-	}
-
-	/**
-	 * Overwrite the key if it is no longer used (zeroization).
-	 */
-	public void clearKey() {
-		if (key != null) {
-			Arrays.fill(key, (byte) 0);
-			Arrays.fill(buffer,  (byte) 0);
-		}
-	}
-
-	/**
-	 * Overwrite the salt (pepper) if it is secret and no longer used
-	 * (zeroization).
-	 */
-	public void clearSalt() {
-		if (salt != null) {
-			Arrays.fill(salt, (byte) 0);
-		}
-	}
-}
\ No newline at end of file
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 7eed7b5b7fd4e542e5f3766bc3ce2fdab81c48d6..435b453ca1b04ec2a089b5a212093f31502f76c9 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
@@ -18,6 +18,7 @@ import org.spongycastle.crypto.CipherParameters;
 import org.spongycastle.crypto.CryptoException;
 import org.spongycastle.crypto.Digest;
 import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 import org.spongycastle.crypto.digests.SHA256Digest;
 import org.spongycastle.crypto.generators.ECKeyPairGenerator;
 import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
@@ -297,7 +298,7 @@ class CryptoComponentImpl implements CryptoComponent {
 	@Override
 	public byte[] hash(String label, byte[]... inputs) {
 		byte[] labelBytes = StringUtils.toUtf8(label);
-		Digest digest = new Blake2sDigest();
+		Digest digest = new Blake2bDigest(256);
 		byte[] length = new byte[INT_32_BYTES];
 		ByteUtils.writeUint32(labelBytes.length, length, 0);
 		digest.update(length, 0, length.length);
@@ -315,7 +316,7 @@ class CryptoComponentImpl implements CryptoComponent {
 	@Override
 	public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
 		byte[] labelBytes = StringUtils.toUtf8(label);
-		Digest mac = new Blake2sDigest(macKey.getBytes());
+		Digest mac = new Blake2bDigest(macKey.getBytes(), 32, null, null);
 		byte[] length = new byte[INT_32_BYTES];
 		ByteUtils.writeUint32(labelBytes.length, length, 0);
 		mac.update(length, 0, length.length);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/SignatureImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/SignatureImpl.java
index 2a4f8704773e843fb8152a75e96d709160fb0196..02ec2eabb0ecba3f61314c46f4a24ac74f7632f7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/SignatureImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/SignatureImpl.java
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.PrivateKey;
 import org.briarproject.bramble.api.crypto.PublicKey;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 import org.spongycastle.crypto.params.ECPrivateKeyParameters;
 import org.spongycastle.crypto.params.ECPublicKeyParameters;
 import org.spongycastle.crypto.params.ParametersWithRandom;
@@ -32,7 +33,7 @@ class SignatureImpl implements Signature {
 
 	SignatureImpl(SecureRandom secureRandom) {
 		this.secureRandom = secureRandom;
-		Digest digest = new Blake2sDigest();
+		Digest digest = new Blake2bDigest(256);
 		DSAKCalculator calculator = new HMacDSAKCalculator(digest);
 		signer = new DSADigestSigner(new ECDSASigner(calculator), digest);
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java
index ea992491716e6f0e6f57231703086857aacefc6b..db35c9d5e3c99a126e4b8e623705b27402c7b6d9 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java
@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.transport.TransportKeys;
 import org.briarproject.bramble.util.ByteUtils;
 import org.briarproject.bramble.util.StringUtils;
 import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 
 import javax.inject.Inject;
 
@@ -115,7 +116,7 @@ class TransportCryptoImpl implements TransportCrypto {
 		if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
 		// Initialise the PRF
-		Digest prf = new Blake2sDigest(tagKey.getBytes());
+		Digest prf = new Blake2bDigest(tagKey.getBytes(), 32, null, null);
 		// The output of the PRF must be long enough to use as a tag
 		int macLength = prf.getDigestSize();
 		if (macLength < TAG_LENGTH) throw new IllegalStateException();
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2sDigestTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2bDigestTest.java
similarity index 62%
rename from bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2sDigestTest.java
rename to bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2bDigestTest.java
index da0e7d5dad1a4c63f48ced68e34554afffe5314b..3154e925f4bf8d9e3866a953ae02200e31c20f4e 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2sDigestTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/Blake2bDigestTest.java
@@ -3,75 +3,76 @@ package org.briarproject.bramble.crypto;
 import org.briarproject.bramble.test.BrambleTestCase;
 import org.briarproject.bramble.util.StringUtils;
 import org.junit.Test;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 
 import java.util.Random;
 
 import static org.junit.Assert.assertArrayEquals;
 
-public class Blake2sDigestTest extends BrambleTestCase {
+public class Blake2bDigestTest extends BrambleTestCase {
 
-	// Vectors from BLAKE2 web site: https://blake2.net/blake2s-test.txt
-	private static final String[][] keyedTestVectors = {
+	// Vectors from BLAKE2 web site: https://blake2.net/Blake2b-test.txt
+	private static final String[][] KEYED_TEST_VECTORS = {
 			// input/message, key, hash
 			{
 					"",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568",
 			},
 			{
 					"00",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"40d15fee7c328830166ac3f918650f807e7e01e177258cdc0a39b11f598066f1",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd",
 			},
 			{
 					"0001",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"6bb71300644cd3991b26ccd4d274acd1adeab8b1d7914546c1198bbe9fc9d803",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965",
 			},
 			{
 					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"172ffc67153d12e0ca76a8b6cd5d4731885b39ce0cac93a8972a18006c8b8baf",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd",
 			},
 			{
 					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"4f8ce1e51d2fe7f24043a904d898ebfc91975418753413aa099b795ecb35cedb",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f",
 			},
 			{
 					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
-					"3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd",
+					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+					"142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461",
 			},
 	};
 
 	@Test
 	public void testDigestWithKeyedTestVectors() {
-		Blake2sDigest digest = new Blake2sDigest(StringUtils.fromHexString(
-				keyedTestVectors[0][1]));
-		for (String[] keyedTestVector : keyedTestVectors) {
+		for (String[] keyedTestVector : KEYED_TEST_VECTORS) {
 			byte[] input = StringUtils.fromHexString(keyedTestVector[0]);
-			digest.reset();
+			byte[] key = StringUtils.fromHexString(keyedTestVector[1]);
+			byte[] expected = StringUtils.fromHexString(keyedTestVector[2]);
 
+			Blake2bDigest digest = new Blake2bDigest(key);
 			digest.update(input, 0, input.length);
-			byte[] hash = new byte[32];
+			byte[] hash = new byte[64];
 			digest.doFinal(hash, 0);
 
-			assertArrayEquals(StringUtils.fromHexString(keyedTestVector[2]),
-					hash);
+			assertArrayEquals(expected, hash);
 		}
 	}
 
 	@Test
 	public void testDigestWithKeyedTestVectorsAndRandomUpdate() {
-		Blake2sDigest digest = new Blake2sDigest(StringUtils.fromHexString(
-				keyedTestVectors[0][1]));
 		Random random = new Random();
 		for (int i = 0; i < 100; i++) {
-			for (String[] keyedTestVector : keyedTestVectors) {
+			for (String[] keyedTestVector : KEYED_TEST_VECTORS) {
 				byte[] input = StringUtils.fromHexString(keyedTestVector[0]);
-				if (input.length < 3) continue;
-				digest.reset();
+				if (input.length == 0) continue;
+				byte[] key = StringUtils.fromHexString(keyedTestVector[1]);
+				byte[] expected = StringUtils.fromHexString(keyedTestVector[2]);
+
+				Blake2bDigest digest = new Blake2bDigest(key);
 
 				int pos = random.nextInt(input.length);
 				if (pos > 0)
@@ -80,11 +81,10 @@ public class Blake2sDigestTest extends BrambleTestCase {
 				if (pos < (input.length - 1))
 					digest.update(input, pos + 1, input.length - (pos + 1));
 
-				byte[] hash = new byte[32];
+				byte[] hash = new byte[64];
 				digest.doFinal(hash, 0);
 
-				assertArrayEquals(StringUtils.fromHexString(keyedTestVector[2]),
-						hash);
+				assertArrayEquals(expected, hash);
 			}
 		}
 	}
@@ -98,12 +98,12 @@ public class Blake2sDigestTest extends BrambleTestCase {
 		byte[] input = new byte[key.length + 1];
 		for (byte i = 0; i < input.length; i++) input[i] = i;
 		// Hash the input
-		Blake2sDigest digest = new Blake2sDigest(key);
+		Blake2bDigest digest = new Blake2bDigest(key);
 		digest.update(input, 0, input.length);
 		byte[] hash = new byte[digest.getDigestSize()];
 		digest.doFinal(hash, 0);
 		// Create a second instance, hash the input without calling doFinal()
-		Blake2sDigest digest1 = new Blake2sDigest(key);
+		Blake2bDigest digest1 = new Blake2bDigest(key);
 		digest1.update(input, 0, input.length);
 		// Reset the second instance and hash the input again
 		digest1.reset();
@@ -116,9 +116,10 @@ public class Blake2sDigestTest extends BrambleTestCase {
 
 	// Self-test routine from https://tools.ietf.org/html/rfc7693#appendix-E
 	private static final String SELF_TEST_RESULT =
-			"6A411F08CE25ADCDFB02ABA641451CEC53C598B24F4FC787FBDC88797F4C1DFE";
-	private static final int[] SELF_TEST_DIGEST_LEN = {16, 20, 28, 32};
-	private static final int[] SELF_TEST_INPUT_LEN = {0, 3, 64, 65, 255, 1024};
+			"C23A7800D98123BD10F506C61E29DA5603D763B8BBAD2E737F5E765A7BCCD475";
+	private static final int[] SELF_TEST_DIGEST_LEN = {20, 32, 48, 64};
+	private static final int[] SELF_TEST_INPUT_LEN =
+			{0, 3, 128, 129, 255, 1024};
 
 	private static byte[] selfTestSequence(int len, int seed) {
 		int a = 0xDEAD4BAD * seed;
@@ -138,8 +139,8 @@ public class Blake2sDigestTest extends BrambleTestCase {
 
 	@Test
 	public void runSelfTest() {
-		Blake2sDigest testDigest = new Blake2sDigest();
-		byte[] md = new byte[32];
+		Blake2bDigest testDigest = new Blake2bDigest(256);
+		byte[] md = new byte[64];
 
 		for (int i = 0; i < 4; i++) {
 			int outlen = SELF_TEST_DIGEST_LEN[i];
@@ -148,7 +149,7 @@ public class Blake2sDigestTest extends BrambleTestCase {
 
 				// unkeyed hash
 				byte[] in = selfTestSequence(inlen, inlen);
-				Blake2sDigest unkeyedDigest = new Blake2sDigest(outlen * 8);
+				Blake2bDigest unkeyedDigest = new Blake2bDigest(outlen * 8);
 				unkeyedDigest.update(in, 0, inlen);
 				unkeyedDigest.doFinal(md, 0);
 				// hash the hash
@@ -156,7 +157,7 @@ public class Blake2sDigestTest extends BrambleTestCase {
 
 				// keyed hash
 				byte[] key = selfTestSequence(outlen, outlen);
-				Blake2sDigest keyedDigest = new Blake2sDigest(key, outlen, null,
+				Blake2bDigest keyedDigest = new Blake2bDigest(key, outlen, null,
 						null);
 				keyedDigest.update(in, 0, inlen);
 				keyedDigest.doFinal(md, 0);
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/EllipticCurvePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/EllipticCurvePerformanceTest.java
index b9e695eda67af04b34f8272232497e4bf05ec3f9..ac42f290e31c8f5018c2a0af6d06fd24cef81cc2 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/EllipticCurvePerformanceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/EllipticCurvePerformanceTest.java
@@ -6,6 +6,7 @@ import org.spongycastle.asn1.x9.X9ECParameters;
 import org.spongycastle.crypto.AsymmetricCipherKeyPair;
 import org.spongycastle.crypto.Digest;
 import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 import org.spongycastle.crypto.generators.ECKeyPairGenerator;
 import org.spongycastle.crypto.params.ECDomainParameters;
 import org.spongycastle.crypto.params.ECKeyGenerationParameters;
@@ -82,7 +83,7 @@ public class EllipticCurvePerformanceTest {
 		List<byte[]> signatures = new ArrayList<>();
 		samples.clear();
 		for (int i = 0; i < SAMPLES; i++) {
-			Digest digest = new Blake2sDigest();
+			Digest digest = new Blake2bDigest(256);
 			DSAKCalculator calculator = new HMacDSAKCalculator(digest);
 			DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
 					calculator), digest);
@@ -96,7 +97,7 @@ public class EllipticCurvePerformanceTest {
 		// Time some signature verifications
 		samples.clear();
 		for (int i = 0; i < SAMPLES; i++) {
-			Digest digest = new Blake2sDigest();
+			Digest digest = new Blake2bDigest(256);
 			DSAKCalculator calculator = new HMacDSAKCalculator(digest);
 			DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
 					calculator), digest);
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/PseudoRandom.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/PseudoRandom.java
index fdc715c469a318e82d67b316124584c2cf7fb9f3..e778a1fdaf2e103ecfc2f19867b1b61be751eca9 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/PseudoRandom.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/PseudoRandom.java
@@ -2,6 +2,7 @@ package org.briarproject.bramble.crypto;
 
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.Blake2bDigest;
 import org.spongycastle.crypto.engines.Salsa20Engine;
 import org.spongycastle.crypto.params.KeyParameter;
 import org.spongycastle.crypto.params.ParametersWithIV;
@@ -17,7 +18,7 @@ class PseudoRandom {
 	PseudoRandom(byte[] seed) {
 		// Hash the seed to produce a 32-byte key
 		byte[] key = new byte[32];
-		Digest digest = new Blake2sDigest();
+		Digest digest = new Blake2bDigest(256);
 		digest.update(seed, 0, seed.length);
 		digest.doFinal(key, 0);
 		// Initialise the stream cipher with an all-zero nonce