BLAKE3
Header: #include <cryptopp/blake3.h> | Namespace: CryptoPP
Since: cryptopp-modern 2025.11.0 (runtime input validation: 2026.8.0)
Thread Safety: Not thread-safe per instance; use separate instances per thread
Fast cryptographic hash function based on Bao and BLAKE2. BLAKE3 is designed for high performance and supports parallel hashing, tree hashing, keyed hashing (MAC), and key derivation. The cryptopp-modern implementation includes SIMD acceleration with automatic runtime CPU detection (AVX-512, AVX2, SSE4.1, or portable C++ fallback).
Quick Example
#include <cryptopp/blake3.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <iostream>
#include <string>
int main() {
using namespace CryptoPP;
BLAKE3 hash;
std::string message = "abc";
std::string digest, hexOutput;
hash.Update(reinterpret_cast<const byte*>(message.data()), message.size());
digest.resize(hash.DigestSize());
hash.Final(reinterpret_cast<byte*>(&digest[0]));
StringSource(digest, true, new HexEncoder(new StringSink(hexOutput)));
std::cout << "BLAKE3: " << hexOutput << std::endl;
// Expected: 6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85
return 0;
}Overview
BLAKE3 is a cryptographic hash function designed for speed in software while maintaining a high security margin; on the recorded benchmarks below it substantially outperforms the compared SHA-2 and BLAKE2 builds. It can be used as a general-purpose hash function, a keyed hash (MAC), or a key derivation function (KDF).
Key features:
- Fast in software - Designed for high throughput; see the recorded benchmarks below
- SIMD accelerated - Runtime detection of AVX-512 (16-way parallel), AVX2 (8-way parallel), SSE4.1 (4-way parallel), or portable C++
- Parallelisable - Merkle tree structure enables parallel chunk processing
- Variable output - Output lengths from 1 to 1,024 bytes, configured at construction
- Multiple modes - Standard hash, keyed hash (MAC), or KDF
- No length extension - Secure against length extension attacks
Usage Guidelines
Do:
- Use BLAKE3 for general-purpose hashing, file integrity, and content addressing
- Use keyed mode (MAC) for message authentication with a secret key
- Use KDF mode for deriving keys from high-entropy secrets or existing keys (for example, the output of Argon2)
- Reuse the same instance for multiple messages;
Final()andTruncatedFinal()restart it automatically - Check
AlgorithmProvider()for the highest SIMD capability the build detected
Avoid:
- Using BLAKE3 KDF as a replacement for Argon2 for password hashing (use Argon2 instead - it’s memory-hard)
- Using keyed mode as a replacement for digital signatures (use Ed25519 instead)
- Reusing the same key for multiple purposes (use different keys or context strings)
- Using user-supplied strings directly as KDF context (use fixed, application-specific strings)
Constants
DIGESTSIZE = 32- Default output size in bytesBLOCKSIZE = 64- Internal block size in bytesCHUNKSIZE = 1024- Chunk size for tree hashingDEFAULT_KEYLENGTH = 32- Recommended key length for keyed hashingMIN_KEYLENGTH = 32- Keyed mode requires exactly 32 bytes of key materialMAX_KEYLENGTH = 32- Maximum supported key length in bytes
Constructors
Default Constructor
BLAKE3(unsigned int digestSize = DIGESTSIZE)Constructs a BLAKE3 hash object with the specified output size. The implementation supports output lengths from 1 to 1,024 bytes.
Parameters:
digestSize- Desired hash output size in bytes (default: 32)
Exceptions:
- Throws
InvalidArgumentifdigestSizeis 0 or larger than 1,024
Example:
BLAKE3 hash; // 32-byte output
BLAKE3 hash256(32); // Explicit 32-byte output
BLAKE3 hashXOF(128); // 128-byte extended output
Keyed Constructor (MAC Mode)
BLAKE3(const byte* key, size_t keyLength, unsigned int digestSize = DIGESTSIZE)Constructs a BLAKE3 object for keyed hashing (MAC mode). Use this for message authentication with a secret key.
Parameters:
key- Pointer to key bytes (must not be null)keyLength- Length of key in bytes. Keyed BLAKE3 requires exactly 32 bytes of key material; use the default constructor for unkeyed hashingdigestSize- Desired hash output size in bytes (default: 32)
Exceptions:
- Throws
InvalidKeyLengthifkeyLengthis not 32 - Throws
InvalidArgumentifkeyis null, or ifdigestSizeis 0 or larger than 1,024
When to use: Message authentication where both parties share a secret key (similar to HMAC).
Example:
SecByteBlock key(32);
AutoSeededRandomPool rng;
rng.GenerateBlock(key, key.size());
BLAKE3 mac(key, key.size()); // Create MAC with 32-byte key
KDF Constructor (Key Derivation Mode)
BLAKE3(const char* context, unsigned int digestSize = DIGESTSIZE)Constructs a BLAKE3 object for key derivation (KDF mode) with a context string for domain separation.
Parameters:
context- Context string for domain separation; a fixed, globally unique application stringdigestSize- Desired output size in bytes (default: 32)
Exceptions:
- Throws
InvalidArgumentifcontextis null, or ifdigestSizeis 0 or larger than 1,024
When to use: Deriving multiple keys from a single secret, or creating domain-separated hashes. The context should be a fixed, application-specific string, not user input. An empty context remains accepted for compatibility, but it provides no domain separation; use a fixed, globally unique application string.
Example:
BLAKE3 kdf("MyApp 2025-11-25 Encryption Key", 32);
BLAKE3 kdf2("MyApp 2025-11-25 MAC Key", 32); // Different context = different output
Public Methods
StaticAlgorithmName()
static const char* StaticAlgorithmName()Returns the algorithm name as a static string: "BLAKE3".
Thread Safety: Thread-safe (static method).
AlgorithmName()
std::string AlgorithmName() constReturns the algorithm name with digest size, e.g. "BLAKE3-256" for 32-byte output.
Thread Safety: Thread-safe (const method, read-only).
AlgorithmProvider()
std::string AlgorithmProvider() constReturns the highest SIMD capability the runtime detection found. This is not necessarily the path used for every input: the parallel implementations engage only once enough complete chunks are buffered, so small messages use SSE4.1 or portable C++ even when this returns "AVX2" or "AVX512". ARM builds use the portable path and report "C++".
Returns: One of "C++", "SSE4.1", "AVX2", or "AVX512".
Thread Safety: Thread-safe (const method).
Example:
BLAKE3 hash;
std::cout << "Using: " << hash.AlgorithmProvider() << std::endl;
// Might print "AVX2" on modern x86 CPUs
DigestSize()
unsigned int DigestSize() constReturns the configured output size in bytes.
Thread Safety: Thread-safe (const method).
BlockSize()
unsigned int BlockSize() constReturns the internal block size (64 bytes).
Thread Safety: Thread-safe (const method).
Update()
void Update(const byte* input, size_t length)Updates the hash with additional input data. Can be called multiple times for incremental hashing.
Parameters:
input- Pointer to input datalength- Length of input data in bytes
Exceptions:
- None (safe to call with
length = 0)
Thread Safety: Not thread-safe. Do not call from multiple threads on the same instance.
TruncatedFinal()
void TruncatedFinal(byte* hash, size_t size)Finalizes the hash and writes the output. After calling this, the object is reset and can be reused.
Parameters:
hash- Buffer to receive hash output (must be allocated by caller)size- Number of bytes to write, up to the digest size configured at construction
Exceptions:
- Throws
InvalidArgumentifsizeexceeds the configured digest size. Safe to call withsize = 0
Thread Safety: Not thread-safe.
Note: Calling TruncatedFinal() automatically restarts the instance, so it can be immediately reused. This is not a seekable XOF reader like the official implementation’s OutputReader: producing a different output length requires hashing the input again.
Final()
void Final(byte* hash)Finalises the hash and writes the configured digest size to the output buffer. Equivalent to TruncatedFinal(hash, DigestSize()).
Parameters:
hash- Buffer to receive hash output (must be at leastDigestSize()bytes, which may be up to 1,024)
Exceptions: None
Thread Safety: Not thread-safe.
Note: For the default 32-byte output, Final() is simpler than TruncatedFinal(). Use TruncatedFinal() when you need a different output size (XOF mode).
Restart()
void Restart()Resets the hash to its initial state, allowing reuse of the object. Preserves the mode (standard, keyed, or KDF) and configuration.
Exceptions: None
Thread Safety: Not thread-safe.
Reserve explicit Restart() for abandoning a partially hashed message; Final() and TruncatedFinal() already restart the object.
Example:
BLAKE3 hash;
hash.Update(...);
hash.Final(...); // The object restarts automatically
// Can immediately reuse:
hash.Update(...);
hash.Final(...);Usage Modes
Basic Hash Mode
When to use: General-purpose hashing, file integrity verification, content addressing.
#include <cryptopp/blake3.h>
#include <cryptopp/hex.h>
#include <cryptopp/files.h>
#include <iostream>
int main() {
CryptoPP::BLAKE3 hash;
std::string message = "The quick brown fox jumps over the lazy dog";
std::string digest;
hash.Update(reinterpret_cast<const CryptoPP::byte*>(message.data()), message.size());
digest.resize(hash.DigestSize());
hash.Final(reinterpret_cast<CryptoPP::byte*>(&digest[0]));
std::string hexOutput;
CryptoPP::StringSource(digest, true,
new CryptoPP::HexEncoder(new CryptoPP::StringSink(hexOutput))
);
std::cout << "BLAKE3: " << hexOutput << std::endl;
return 0;
}File hashing example:
BLAKE3 hash;
std::string digest;
FileSource("document.pdf", true,
new HashFilter(hash,
new StringSink(digest)
)
);
// digest now contains the BLAKE3 hash of the file
Keyed Hash Mode (MAC)
When to use: Message authentication when both parties share a secret key.
#include <cryptopp/blake3.h>
#include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
#include <iostream>
int main() {
using namespace CryptoPP;
// Generate a random 32-byte key (do this once, store securely)
AutoSeededRandomPool rng;
SecByteBlock key(32);
rng.GenerateBlock(key, key.size());
// Create keyed BLAKE3 (MAC)
BLAKE3 mac(key, key.size());
std::string message = "Authenticate this message";
std::string tag;
mac.Update(reinterpret_cast<const byte*>(message.data()), message.size());
tag.resize(mac.DigestSize());
mac.Final(reinterpret_cast<byte*>(&tag[0]));
// Send message + tag to recipient
// Recipient verifies by recomputing MAC with same key
std::string hexTag;
StringSource(tag, true, new HexEncoder(new StringSink(hexTag)));
std::cout << "MAC: " << hexTag << std::endl;
return 0;
}Verification example:
// Receiver side:
BLAKE3 verifyMac(key, key.size());
verifyMac.Update(reinterpret_cast<const byte*>(receivedMessage.data()), receivedMessage.size());
std::string computedTag(32, '\0');
verifyMac.Final(reinterpret_cast<byte*>(&computedTag[0]));
// Check the length first (tag length is not secret), then compare in
// constant time. Comparing a fixed 32 bytes against a shorter received
// tag would read out of bounds.
const size_t tagSize = computedTag.size();
if (receivedTag.size() == tagSize &&
VerifyBufsEqual(
reinterpret_cast<const byte*>(computedTag.data()),
reinterpret_cast<const byte*>(receivedTag.data()),
tagSize))
{
std::cout << "Message is authentic!" << std::endl;
} else {
std::cout << "WARNING: Message has been tampered with!" << std::endl;
}Key Derivation Mode (KDF)
When to use: Deriving multiple keys from a single secret, creating domain-separated keys.
#include <cryptopp/blake3.h>
#include <cryptopp/osrng.h>
#include <cryptopp/secblock.h>
#include <iostream>
#include <string>
int main() {
using namespace CryptoPP;
// Input key material must be high-entropy: a randomly generated key,
// Argon2 output, or a secret loaded from protected key storage.
SecByteBlock inputKeyMaterial(32);
AutoSeededRandomPool rng;
rng.GenerateBlock(inputKeyMaterial, inputKeyMaterial.size());
// Derive encryption key
BLAKE3 kdfEncrypt("MyApplication 2025-11-25 Encryption Key", 32);
kdfEncrypt.Update(inputKeyMaterial.data(), inputKeyMaterial.size());
std::string encryptionKey(32, '\0');
kdfEncrypt.Final(reinterpret_cast<byte*>(&encryptionKey[0]));
// Derive MAC key (different context = different output)
BLAKE3 kdfMac("MyApplication 2025-11-25 MAC Key", 32);
kdfMac.Update(inputKeyMaterial.data(), inputKeyMaterial.size());
std::string macKey(32, '\0');
kdfMac.Final(reinterpret_cast<byte*>(&macKey[0]));
// encryptionKey and macKey are now independent, derived keys
std::cout << "Derived two independent keys from one secret" << std::endl;
return 0;
}For password hashing, use Argon2 instead!
BLAKE3 KDF is fast, which is good for key derivation but bad for password hashing. Argon2 is deliberately memory-hard and slow, making it resistant to brute-force attacks.
Extendable Output (XOF)
When to use: Protocols that need output longer than 32 bytes.
The implementation supports variable output lengths from 1 to 1,024 bytes. Configure the maximum output size in the constructor; TruncatedFinal() may emit up to that configured size. This is not a seekable XOF reader like the official implementation’s OutputReader: finalisation restarts the object, so producing another length means hashing the input again.
#include <cryptopp/blake3.h>
#include <iostream>
#include <string>
int main() {
using namespace CryptoPP;
BLAKE3 hash(128); // configure the maximum output size up front
std::string message = "Generate extended output";
hash.Update(
reinterpret_cast<const byte*>(message.data()),
message.size());
std::string output(128, '\0');
hash.TruncatedFinal(
reinterpret_cast<byte*>(&output[0]),
output.size());
std::cout << "Generated " << output.size() << "-byte hash output" << std::endl;
// A different length requires hashing the input again:
BLAKE3 short16(16);
short16.Update(
reinterpret_cast<const byte*>(message.data()),
message.size());
std::string small(16, '\0');
short16.TruncatedFinal(reinterpret_cast<byte*>(&small[0]), small.size());
return 0;
}Performance
The figures below are one recorded build on one machine, not a general claim; throughput varies with architecture, SIMD availability and message size.
SIMD Acceleration
cryptopp-modern’s BLAKE3 implementation includes optimised SIMD code paths with automatic runtime CPU detection:
| SIMD Level | Parallel Chunks | Minimum Buffer | Approx. Speed |
|---|---|---|---|
| AVX-512 | 16 at a time | 16KB | ~4500 MiB/s |
| AVX2 | 8 at a time | 8KB | ~2600 MiB/s |
| SSE4.1 | 4 at a time | 4KB | ~1800 MiB/s |
| C++ | 1 at a time | Any | ~800 MiB/s |
Benchmarks on Intel Core Ultra 7 155H, Windows 11, MinGW-w64 GCC
Comparison with Other Hash Functions
| Algorithm | Provider | Speed (MiB/s) | vs BLAKE2b |
|---|---|---|---|
| BLAKE3 | AVX-512 | ~4500 | 5.5x faster |
| BLAKE3 | AVX2 | 2599 | 3.15x faster |
| BLAKE3 | SSE4.1 | ~1800 | 2.2x faster |
| BLAKE3 | C++ | ~800 | Similar |
| BLAKE2b | SSE4.1 | 822 | baseline |
Performance Tips
- Use large buffers: For maximum throughput with large data, use 16KB+ buffers to enable full AVX-512 parallel processing (or 8KB+ for AVX2)
- Check your provider: Use
AlgorithmProvider()to inspect the highest detected SIMD capability - Small data is fine: BLAKE3 adapts automatically - small inputs work correctly, just without SIMD parallelism
#include <fstream>
#include <stdexcept>
#include <vector>
// Large buffers enable the parallel SIMD paths
BLAKE3 hash;
const size_t BUFFER_SIZE = 65536; // 64 KiB
std::vector<byte> buffer(BUFFER_SIZE);
std::ifstream file("largefile.bin", std::ios::binary);
if (!file)
throw std::runtime_error("Could not open largefile.bin");
while (file) {
file.read(
reinterpret_cast<char*>(buffer.data()),
static_cast<std::streamsize>(buffer.size()));
const std::streamsize count = file.gcount();
if (count > 0)
hash.Update(buffer.data(), static_cast<size_t>(count));
}
if (!file.eof())
throw std::runtime_error("Error reading largefile.bin");A while (file.read(...)) loop silently drops the final partial block: the last short read sets the stream failure state before its bytes are hashed.
Additional Performance Characteristics
- Side-channel shape - Compression uses fixed operations without secret-indexed lookup tables; no platform-wide constant-time guarantee is made
- Memory alignment - Buffers aligned to 32 bytes (AVX2) or 16 bytes (SSE4.1) may provide marginal improvements, though unaligned access is fully supported
Security
Security properties
- Security strength: BLAKE3 targets roughly 128-bit security for preimage, second-preimage and collision attacks.
- Output length: The default 32-byte output is recommended for general use. Longer outputs do not increase BLAKE3’s claimed security level.
- Length-extension resistance: Tree-based construction; not vulnerable to classic SHA-2 style length-extension attacks.
- Keyed mode (MAC / PRF): With a 32-byte secret key, BLAKE3 behaves as a secure PRF/MAC with ~128-bit security. It is suitable as a MAC/PRF in new designs that explicitly specify BLAKE3 keyed mode; it is not a drop-in replacement for HMAC at the API, protocol or interoperability level.
- KDF mode: With a fixed, application-specific context string and high-entropy key material, BLAKE3 provides a secure KDF for deriving multiple, domain-separated keys.
- Side-channel behaviour: The compression paths use fixed operations without secret-indexed lookup tables. No independent constant-time guarantee is made for every platform and compiler.
Security notes
- Use at least 32 bytes of output for cryptographic purposes. A 16-byte output provides at most about 64-bit collision resistance; use it only where that bound is acceptable.
- For a generic quantum search, a 128-bit preimage bound is reduced to roughly 64 bits.
- Do not use BLAKE3 directly for password hashing or deriving keys from low-entropy passwords. Use Argon2 for password hashing, and treat BLAKE3 KDF as a fast, follow-on KDF once you already have high-entropy key material.
- In keyed mode, treat BLAKE3 as a MAC/PRF, not as a digital signature. Use Ed25519/RSA/etc. where you need non-repudiation.
- When deriving multiple keys from the same input (e.g. encryption key + MAC key), always use distinct, fixed context strings to enforce domain separation between outputs.
Thread Safety
- Per-instance: Not thread-safe. Do not use the same
BLAKE3instance from multiple threads simultaneously. - Multi-instance: Thread-safe. You can safely use different
BLAKE3instances in different threads. - Static methods: Thread-safe (
StaticAlgorithmName()).
Example (safe):
// Each thread has its own instance
void workerThread(const std::vector<std::string>& messages) {
BLAKE3 hash; // Thread-local instance
for (const auto& msg : messages) {
hash.Update(...);
hash.TruncatedFinal(...); // restarts the object for the next message
}
}Use Cases
- File integrity - Checksums and file verification (often faster than SHA-256, depending on platform and input size)
- Signature prehashing - Only in protocols that explicitly specify BLAKE3 as their prehash; Ed25519 and the other signature schemes hash internally
- Content addressing - IPFS and similar systems
- Deduplication - Fast hash for identifying duplicate data
- Message authentication - Use keyed mode for MAC
- Key derivation - Derive multiple keys from one secret (use KDF mode)
- Hash tables - Fast keyed hashing for identifiers and keys
Test Vectors
Use these to verify your BLAKE3 implementation:
| Mode | Input | Output (hex) |
|---|---|---|
| Basic | "" (empty) | af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 |
| Basic | "abc" | 6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85 |
The official BLAKE3 project publishes the authoritative vector set covering the basic, keyed and derive-key modes with extended output; the library’s own known-answer tests run against TestVectors/blake3.txt.
See Also
- BLAKE3 Guide - Detailed guide with more examples
- Hash Functions Guide - Overview of all hash functions
- Algorithm Selection Guide - Choosing between the commonly used algorithms
- Argon2 - Use this for password hashing, not BLAKE3
- HMAC - Alternative MAC construction
- BLAKE3 Specification - Official specification