Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt
The BLAKE family includes three generations of high-performance cryptographic hash functions. BLAKE2 is faster than MD5, SHA-1, and SHA-2 while being as secure as SHA-3. BLAKE3 is even faster with additional features like tree hashing, keyed hashing, and extendable output.
// BLAKE1 (legacy)
import { blake224, blake256, blake384, blake512 } from '@noble/hashes/blake1.js';
// BLAKE2
import { blake2b, blake2s } from '@noble/hashes/blake2.js';
// BLAKE3
import { blake3 } from '@noble/hashes/blake3.js';BLAKE1 was one of the SHA-3 competition finalists. It is rarely used today and considered legacy. Included for compatibility with older systems.
/**
* Options for BLAKE1 functions
*/
interface BlakeOpts {
/** Salt value for domain separation (MUST be Uint8Array of exactly 16 bytes for all BLAKE1 variants) */
salt?: Uint8Array;
}
/**
* Computes BLAKE1-224 hash of input data
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 16 bytes)
* @returns 28-byte hash digest
*/
function blake224(msg: Uint8Array, opts?: BlakeOpts): Uint8Array;
// Properties
blake224.outputLen: 28; // Output length in bytes
blake224.blockLen: 64; // Block length in bytes
// Create incremental hasher
function create(opts?: BlakeOpts): _BLAKE224;
/**
* Computes BLAKE1-256 hash of input data
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 16 bytes)
* @returns 32-byte hash digest
*/
function blake256(msg: Uint8Array, opts?: BlakeOpts): Uint8Array;
// Properties
blake256.outputLen: 32; // Output length in bytes
blake256.blockLen: 64; // Block length in bytes
// Create incremental hasher
function create(opts?: BlakeOpts): _BLAKE256;
/**
* Computes BLAKE1-384 hash of input data
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 16 bytes)
* @returns 48-byte hash digest
*/
function blake384(msg: Uint8Array, opts?: BlakeOpts): Uint8Array;
// Properties
blake384.outputLen: 48; // Output length in bytes
blake384.blockLen: 128; // Block length in bytes
// Create incremental hasher
function create(opts?: BlakeOpts): _BLAKE384;
/**
* Computes BLAKE1-512 hash of input data
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 16 bytes)
* @returns 64-byte hash digest
*/
function blake512(msg: Uint8Array, opts?: BlakeOpts): Uint8Array;
// Properties
blake512.outputLen: 64; // Output length in bytes
blake512.blockLen: 128; // Block length in bytes
// Create incremental hasher
function create(opts?: BlakeOpts): _BLAKE512;Usage:
import { blake256 } from '@noble/hashes/blake1.js';
// Simple hash
const hash = blake256(data);
// With salt (must be exactly 16 bytes for all BLAKE1 variants)
const saltedHash = blake256(data, {
salt: new Uint8Array(16).fill(0x42)
});BLAKE2 is a fast, secure hash function suitable for general-purpose hashing, keyed hashing, and MAC. BLAKE2b targets 64-bit platforms while BLAKE2s targets 8-32 bit platforms.
/**
* Options for BLAKE2 functions
*/
interface Blake2Opts {
/** Desired output length in bytes (1-64 for blake2b, 1-32 for blake2s) */
dkLen?: number;
/** Key for keyed hashing/MAC (0-64 bytes for blake2b, 0-32 bytes for blake2s) */
key?: Uint8Array;
/** Salt for domain separation (MUST be Uint8Array of exactly 16 bytes for blake2b, 8 bytes for blake2s) */
salt?: Uint8Array;
/** Personalization string (MUST be Uint8Array of exactly 16 bytes for blake2b, 8 bytes for blake2s) */
personalization?: Uint8Array;
}
/**
* Computes BLAKE2b hash of input data (optimized for 64-bit platforms)
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.dkLen - Desired output length in bytes (1-64, default: 64)
* @param opts.key - Optional key for keyed hashing (0-64 bytes)
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 16 bytes)
* @param opts.personalization - Optional personalization string (MUST be Uint8Array of exactly 16 bytes)
* @returns Hash digest of specified length
*/
function blake2b(msg: Uint8Array, opts?: Blake2Opts): Uint8Array;
// Properties
blake2b.outputLen: 64; // Default output length in bytes
blake2b.blockLen: 128; // Block length in bytes
// Create incremental hasher
function create(opts?: Blake2Opts): _BLAKE2b;
/**
* Computes BLAKE2s hash of input data (optimized for 8-32 bit platforms)
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.dkLen - Desired output length in bytes (1-32, default: 32)
* @param opts.key - Optional key for keyed hashing (0-32 bytes)
* @param opts.salt - Optional salt value (MUST be Uint8Array of exactly 8 bytes)
* @param opts.personalization - Optional personalization string (MUST be Uint8Array of exactly 8 bytes)
* @returns Hash digest of specified length
*/
function blake2s(msg: Uint8Array, opts?: Blake2Opts): Uint8Array;
// Properties
blake2s.outputLen: 32; // Default output length in bytes
blake2s.blockLen: 64; // Block length in bytes
// Create incremental hasher
function create(opts?: Blake2Opts): _BLAKE2s;Usage:
import { blake2b, blake2s } from '@noble/hashes/blake2.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';
// Simple hashing
const hash = blake2b(data);
// Custom output length
const hash256 = blake2b(data, { dkLen: 32 });
// Keyed hashing (MAC)
const key = new Uint8Array(32).fill(0x42);
const mac = blake2b(message, { key });
// With personalization (MUST be Uint8Array, exactly 16 bytes for blake2b)
// Use utf8ToBytes() to convert strings
const personalizationBytes = new Uint8Array(16);
personalizationBytes.set(utf8ToBytes('my-app-v1'));
const appHash = blake2b(data, {
personalization: personalizationBytes,
dkLen: 32
});
// With salt (MUST be Uint8Array, exactly 16 bytes for blake2b)
const saltBytes = new Uint8Array(16).fill(0x42);
const saltedHash = blake2b(data, {
salt: saltBytes
});
// BLAKE2s for 32-bit platforms (salt and personalization are 8 bytes for blake2s)
const hash32 = blake2s(data);
const mac32 = blake2s(message, {
key: new Uint8Array(32).fill(0x42)
});
// Incremental hashing
const hasher = blake2b.create({ dkLen: 32 });
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.digest();BLAKE3 is the latest generation, offering even better performance with additional features like tree hashing, keyed mode, and key derivation context mode. Supports extendable output (XOF).
/**
* Options for BLAKE3
*/
interface Blake3Opts {
/** Desired output length in bytes (default: 32, unlimited for XOF) */
dkLen?: number;
/** 32-byte key for keyed mode (MAC) - mutually exclusive with context */
key?: Uint8Array;
/** Context for key derivation mode (MUST be Uint8Array, not string) - mutually exclusive with key */
context?: Uint8Array;
}
/**
* Computes BLAKE3 hash of input data
* @param msg - Input data as Uint8Array
* @param opts - Options
* @param opts.dkLen - Desired output length in bytes (default: 32)
* @param opts.key - Optional 32-byte key for keyed mode (MAC)
* @param opts.context - Optional context string for key derivation mode (MUST be Uint8Array, not string)
* @returns Hash digest of specified length
*/
function blake3(msg: Uint8Array, opts?: Blake3Opts): Uint8Array;
// Properties
blake3.outputLen: 32; // Default output length in bytes
blake3.blockLen: 64; // Block length in bytes
// Create incremental hasher
function create(opts?: Blake3Opts): _BLAKE3;BLAKE3 XOF Methods:
class _BLAKE3 {
blockLen: number;
outputLen: number;
/**
* Updates hash with new data
* @param buf - Data to hash
* @returns this for chaining
*/
update(buf: Uint8Array): this;
/**
* Finalizes hash and returns digest
* @returns Hash digest as Uint8Array
*/
digest(): Uint8Array;
/**
* Finalizes hash and writes digest to provided buffer
* @param out - Output buffer (must be >= outputLen)
*/
digestInto(out: Uint8Array): void;
/**
* Reads bytes from XOF stream
* @param bytes - Number of bytes to read
* @returns Bytes from XOF stream
*/
xof(bytes: number): Uint8Array;
/**
* Reads bytes from XOF into provided buffer
* @param out - Output buffer
* @returns The output buffer
*/
xofInto(out: Uint8Array): Uint8Array;
/**
* Destroys internal state (zeros memory)
*/
destroy(): void;
/**
* Creates independent copy of current hash state
* @returns Cloned hash instance
*/
clone(): _BLAKE3;
}Usage:
import { blake3 } from '@noble/hashes/blake3.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';
// Simple hashing
const hash = blake3(data);
// Custom output length
const hash512 = blake3(data, { dkLen: 64 });
// Keyed mode (MAC)
const key = new Uint8Array(32).fill(0x42);
const mac = blake3(message, { key });
// Key derivation context mode (MUST be Uint8Array, not string)
// Use utf8ToBytes() to convert strings
const derivedKey = blake3(inputMaterial, {
context: utf8ToBytes('application-v1-encryption-key')
});
// XOF mode - read as much output as needed
const hasher = blake3.create();
hasher.update(data);
// Read multiple outputs from same hash state
const out1 = hasher.xof(32);
const out2 = hasher.xof(32);
const out3 = hasher.xof(64);
// Or use digest with custom length
const out = blake3(data, { dkLen: 256 }); // 256 bytesBoth BLAKE2 and BLAKE3 instances implement a common interface:
interface Hash<T> {
blockLen: number;
outputLen: number;
/**
* Updates hash with new data
* @param buf - Data to hash
* @returns this for chaining
*/
update(buf: Uint8Array): this;
/**
* Finalizes hash and returns digest
* @returns Hash digest as Uint8Array
*/
digest(): Uint8Array;
/**
* Finalizes hash and writes digest to provided buffer
* @param out - Output buffer (must be >= outputLen)
*/
digestInto(out: Uint8Array): void;
/**
* Destroys internal state (zeros memory)
*/
destroy(): void;
/**
* Creates independent copy of current hash state
* @returns Cloned hash instance
*/
clone(): T;
}import { blake2b } from '@noble/hashes/blake2.js';
function createMAC(key: Uint8Array, message: Uint8Array): Uint8Array {
return blake2b(message, { key, dkLen: 32 });
}
function verifyMAC(key: Uint8Array, message: Uint8Array, tag: Uint8Array): boolean {
const computed = createMAC(key, message);
if (computed.length !== tag.length) return false;
let diff = 0;
for (let i = 0; i < computed.length; i++) {
diff |= computed[i] ^ tag[i];
}
return diff === 0;
}
const key = new Uint8Array(32).fill(0x42);
const message = new Uint8Array([1, 2, 3, 4]);
const tag = createMAC(key, message);
const valid = verifyMAC(key, message, tag);import { blake3 } from '@noble/hashes/blake3.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';
function deriveKey(
masterKey: Uint8Array,
context: string,
keyLen: number
): Uint8Array {
// Convert context string to Uint8Array
// This is required because context MUST be Uint8Array, not string
return blake3(masterKey, {
context: utf8ToBytes(context),
dkLen: keyLen
});
}
const masterKey = new Uint8Array(32).fill(0x42);
// Derive different keys for different purposes
const encryptionKey = deriveKey(masterKey, 'encryption-key-v1', 32);
const authKey = deriveKey(masterKey, 'authentication-key-v1', 32);
const signingKey = deriveKey(masterKey, 'signing-key-v1', 32);
// All keys are cryptographically independentimport { blake2b } from '@noble/hashes/blake2.js';
async function hashLargeFile(fileChunks: Uint8Array[]): Promise<Uint8Array> {
const hasher = blake2b.create({ dkLen: 32 });
for (const chunk of fileChunks) {
hasher.update(chunk);
}
return hasher.digest();
}import { blake3 } from '@noble/hashes/blake3.js';
// BLAKE3 internally uses tree hashing, which allows for parallelization
// In JavaScript (single-threaded), this still benefits from better CPU cache usage
function hashLargeData(data: Uint8Array): Uint8Array {
return blake3(data, { dkLen: 32 });
}
// For truly parallel hashing, you would need Web Workers or similarimport { blake2b } from '@noble/hashes/blake2.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';
function domainSeparatedHash(
data: Uint8Array,
domain: string
): Uint8Array {
// Convert domain string to bytes and pad/truncate to exactly 16 bytes
// This is required because personalization MUST be Uint8Array of exactly 16 bytes
const domainBytes = utf8ToBytes(domain);
const personalization = new Uint8Array(16);
personalization.set(domainBytes.slice(0, 16));
return blake2b(data, {
personalization,
dkLen: 32
});
}
// Different domains produce different hashes
const hash1 = domainSeparatedHash(data, 'email-verify');
const hash2 = domainSeparatedHash(data, 'password-hash');On Apple M4 for 32-byte input:
For 1MB input:
BLAKE3 is fastest for large inputs due to tree structure.
BLAKE1: Only for compatibility with legacy systems
BLAKE2b:
BLAKE2s:
BLAKE3:
vs SHA-2:
vs SHA-3:
vs MD5/SHA-1: