Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt
Core utility functions for encoding/decoding, byte manipulation, validation, cryptographic operations, and async helpers. These utilities are used throughout @noble/hashes and are also useful for working with the library's outputs.
import {
// Encoding/Decoding
bytesToHex,
hexToBytes,
utf8ToBytes,
kdfInputToBytes,
// Array Operations
u8,
u32,
concatBytes,
clean,
createView,
// Bitwise Operations
rotr,
rotl,
byteSwap,
swap8IfBE,
byteSwap32,
swap32IfBE,
// Validation
isBytes,
anumber,
abytes,
ahash,
aexists,
aoutput,
// Cryptographic Utilities
randomBytes,
checkOpts,
oidNist,
// Async Helpers
nextTick,
asyncLoop,
// Constants
isLE
} from '@noble/hashes/utils.js';Convert between different data representations.
/**
* Converts Uint8Array to hexadecimal string
* @param bytes - Byte array to convert
* @returns Lowercase hexadecimal string (e.g., "deadbeef")
*/
function bytesToHex(bytes: Uint8Array): string;
/**
* Converts hexadecimal string to Uint8Array
* @param hex - Hex string (with or without 0x prefix, case-insensitive)
* @returns Byte array
* @throws If hex string has invalid length or characters
*/
function hexToBytes(hex: string): Uint8Array;
/**
* Converts UTF-8 string to Uint8Array
* @param str - UTF-8 string to convert
* @returns Byte array with UTF-8 encoded string
*/
function utf8ToBytes(str: string): Uint8Array;
/**
* Converts KDF input (string or Uint8Array) to Uint8Array
* Used internally by KDF functions for password/salt conversion
* @param data - String or Uint8Array
* @param errorTitle - Optional error message prefix for validation
* @returns Byte array
*/
function kdfInputToBytes(data: string | Uint8Array, errorTitle?: string): Uint8Array;
/**
* Type for KDF inputs (password, salt, etc.)
*/
type KDFInput = string | Uint8Array;Usage:
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
// Hex conversion
const bytes = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
const hex = bytesToHex(bytes);
console.log(hex); // "deadbeef"
const restored = hexToBytes(hex);
console.log(restored); // Uint8Array [0xde, 0xad, 0xbe, 0xef]
// Works with 0x prefix and uppercase
const bytes2 = hexToBytes('0xDEADBEEF');
// UTF-8 conversion
const text = 'Hello, World!';
const textBytes = utf8ToBytes(text);
console.log(textBytes); // Uint8Array with UTF-8 encoded bytesUtility functions for working with typed arrays.
/**
* Casts any TypedArray to Uint8Array (view over same memory)
* @param arr - Any TypedArray
* @returns Uint8Array view of the same memory
*/
function u8(arr: TypedArray): Uint8Array;
/**
* Casts any TypedArray to Uint32Array (view over same memory)
* @param arr - Any TypedArray (length must be multiple of 4)
* @returns Uint32Array view of the same memory
*/
function u32(arr: TypedArray): Uint32Array;
/**
* Concatenates multiple Uint8Arrays into one
* @param arrays - Arrays to concatenate
* @returns New Uint8Array with concatenated data
*/
function concatBytes(...arrays: Uint8Array[]): Uint8Array;
/**
* Zero-fills typed arrays (for security cleanup)
* @param arrays - Arrays to zero out
*/
function clean(...arrays: TypedArray[]): void;
/**
* Creates DataView wrapper for a TypedArray
* @param arr - TypedArray to wrap
* @returns DataView over the array's buffer
*/
function createView(arr: TypedArray): DataView;
/**
* Type for any typed array
*/
type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
Uint16Array | Int16Array | Uint32Array | Int32Array;Usage:
import { u8, u32, concatBytes, clean } from '@noble/hashes/utils.js';
// Array casting
const arr32 = new Uint32Array([0x01020304, 0x05060708]);
const arr8 = u8(arr32);
console.log(arr8); // Uint8Array view: [4, 3, 2, 1, 8, 7, 6, 5] (little-endian)
// Concatenation
const part1 = new Uint8Array([1, 2, 3]);
const part2 = new Uint8Array([4, 5, 6]);
const combined = concatBytes(part1, part2);
console.log(combined); // Uint8Array [1, 2, 3, 4, 5, 6]
// Security cleanup
const secretKey = new Uint8Array(32);
// ... use key ...
clean(secretKey); // Zeros out the key
console.log(secretKey); // All zerosLow-level bit manipulation functions used internally by hash algorithms.
/**
* Circular right shift (rotate right)
* @param word - 32-bit unsigned integer
* @param shift - Number of bits to rotate (0-31)
* @returns Rotated value
*/
function rotr(word: number, shift: number): number;
/**
* Circular left shift (rotate left)
* @param word - 32-bit unsigned integer
* @param shift - Number of bits to rotate (0-31)
* @returns Rotated value
*/
function rotl(word: number, shift: number): number;
/**
* Byte swap for 32-bit integer (reverse endianness)
* @param word - 32-bit unsigned integer
* @returns Byte-swapped value
*/
function byteSwap(word: number): number;
/**
* Conditionally byte swap based on system endianness
* @param n - 32-bit unsigned integer
* @returns Byte-swapped value if big-endian system, unchanged if little-endian
*/
function swap8IfBE(n: number): number;
/**
* In-place byte swap for Uint32Array
* @param arr - Array to byte-swap
* @returns The same array (modified in-place)
*/
function byteSwap32(arr: Uint32Array): Uint32Array;
/**
* Conditionally byte swap array based on system endianness
* @param u - Array to potentially swap
* @returns The array (swapped if big-endian, unchanged if little-endian)
*/
function swap32IfBE(u: Uint32Array): Uint32Array;Usage:
import { rotr, rotl, byteSwap } from '@noble/hashes/utils.js';
// Rotate operations (common in hash functions)
const value = 0b10110001000000000000000000000000;
const rotated = rotr(value, 4);
console.log(rotated.toString(2)); // Rotated right by 4 bits
// Byte swap (endianness conversion)
const original = 0x01020304;
const swapped = byteSwap(original);
console.log(swapped.toString(16)); // "04030201"Type checking and assertion functions for input validation.
/**
* Type guard: checks if value is Uint8Array
* @param a - Value to check
* @returns True if value is Uint8Array
*/
function isBytes(a: unknown): a is Uint8Array;
/**
* Asserts value is a positive safe integer
* @param n - Number to validate
* @param title - Optional error message title
* @throws If not a positive safe integer
*/
function anumber(n: number, title?: string): void;
/**
* Asserts value is valid Uint8Array, optionally with specific length
* @param value - Value to validate
* @param length - Optional required length
* @param title - Optional error message title
* @returns The validated Uint8Array
* @throws If not Uint8Array or wrong length
*/
function abytes(value: Uint8Array, length?: number, title?: string): Uint8Array;
/**
* Asserts value is a valid hash function (CHash)
* @param h - Value to validate
* @throws If not a valid hash function
*/
function ahash(h: any): void;
/**
* Asserts hash instance exists and optionally not finished
* @param instance - Hash instance to check
* @param checkFinished - Whether to check if already finished
* @throws If instance destroyed or finished
*/
function aexists(instance: any, checkFinished?: boolean): void;
/**
* Asserts output buffer has correct size
* @param out - Output buffer
* @param instance - Hash instance
* @throws If output buffer too small
*/
function aoutput(out: any, instance: any): void;Usage:
import { isBytes, abytes, anumber } from '@noble/hashes/utils.js';
// Type checking
const data = new Uint8Array(32);
if (isBytes(data)) {
console.log('Valid byte array');
}
// Validation (throws on invalid input)
try {
abytes(data, 32, 'Key'); // OK: correct length
abytes(data, 16, 'IV'); // Throws: wrong length
} catch (e) {
console.error(e.message); // "IV: expected Uint8Array(16), got Uint8Array(32)"
}
// Number validation
anumber(100000); // OK
anumber(-5); // Throws: must be positive
anumber(2 ** 53); // Throws: exceeds safe integer rangeCore cryptographic helper functions.
/**
* Generates cryptographically secure random bytes
* Uses crypto.getRandomValues (browser/Node.js)
* @param bytesLength - Number of bytes to generate (default: 32)
* @returns Random byte array
* @throws If crypto.getRandomValues unavailable
*/
function randomBytes(bytesLength?: number): Uint8Array;
/**
* Merges default options with provided options
* @param defaults - Default option values
* @param opts - User-provided options
* @returns Merged options object
*/
function checkOpts<T1, T2>(defaults: T1, opts?: T2): T1 & T2;
/**
* Creates NIST OID for hash function
* @param suffix - NIST algorithm suffix
* @returns HashInfo with OID
*/
function oidNist(suffix: number): Required<HashInfo>;
/**
* Hash information interface
*/
interface HashInfo {
/** DER-encoded OID (Object Identifier) */
oid?: Uint8Array;
}Usage:
import { randomBytes, checkOpts } from '@noble/hashes/utils.js';
// Generate random bytes (CSPRNG)
const salt = randomBytes(16); // 16 random bytes
const key = randomBytes(32); // 32 random bytes
const nonce = randomBytes(); // 32 random bytes (default)
// Option merging
interface Options {
length: number;
encoding: string;
}
const defaults: Options = { length: 32, encoding: 'hex' };
const userOpts = { length: 64 };
const merged = checkOpts(defaults, userOpts);
console.log(merged); // { length: 64, encoding: 'hex' }Utilities for non-blocking operations.
/**
* Yields to event loop (allows other tasks to run)
* @returns Promise that resolves after yielding
*/
function nextTick(): Promise<void>;
/**
* Executes callback for each iteration, yielding periodically
* Used internally for async KDF operations
* @param iters - Number of iterations
* @param tick - Milliseconds between yields (max blocking time)
* @param cb - Callback for each iteration (receives index)
* @returns Promise that resolves when all iterations complete
*/
function asyncLoop(
iters: number,
tick: number,
cb: (i: number) => void
): Promise<void>;Usage:
import { nextTick, asyncLoop } from '@noble/hashes/utils.js';
// Yield to event loop
async function longOperation() {
for (let i = 0; i < 10; i++) {
// Do some work
processChunk(i);
// Yield to keep UI responsive
await nextTick();
}
}
// Async loop with periodic yielding
async function processWithProgress(items: any[]) {
await asyncLoop(items.length, 10, (i) => {
// Process item i
processItem(items[i]);
// Automatically yields every 10ms
});
}Platform information constants.
/**
* Is current platform little-endian?
* true on most systems (x86, ARM), false on big-endian systems
*/
const isLE: boolean;Usage:
import { isLE } from '@noble/hashes/utils.js';
if (isLE) {
console.log('Little-endian system (x86, ARM)');
} else {
console.log('Big-endian system (rare)');
}import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
import { sha256 } from '@noble/hashes/sha2.js';
// Hash and convert to hex
const data = new Uint8Array([1, 2, 3, 4, 5]);
const hash = sha256(data);
const hashHex = bytesToHex(hash);
console.log('SHA-256:', hashHex);
// Parse hex from user input
function parseHexHash(input: string): Uint8Array {
// Remove optional 0x prefix
const hex = input.startsWith('0x') ? input.slice(2) : input;
// Convert to bytes
return hexToBytes(hex);
}import { clean } from '@noble/hashes/utils.js';
function processSecretKey(key: Uint8Array) {
try {
// Use key for cryptographic operations
performEncryption(key);
} finally {
// Always clean up sensitive data
clean(key);
}
}
// For multiple arrays
function cleanupState(
key: Uint8Array,
nonce: Uint8Array,
buffer: Uint8Array
) {
clean(key, nonce, buffer);
}import { abytes, anumber, isBytes } from '@noble/hashes/utils.js';
function deriveKey(password: unknown, salt: unknown, iterations: unknown): Uint8Array {
// Validate inputs
if (!isBytes(password)) {
throw new Error('Password must be Uint8Array');
}
abytes(salt, 16, 'Salt'); // Must be exactly 16 bytes
anumber(iterations as number, 'Iterations'); // Must be positive integer
// Now safe to use
return pbkdf2(sha256, password, salt, { c: iterations as number });
}import { randomBytes, bytesToHex } from '@noble/hashes/utils.js';
// Generate random ID
function generateId(): string {
return bytesToHex(randomBytes(16));
}
// Generate random salt
function generateSalt(length: number = 16): Uint8Array {
return randomBytes(length);
}
// Generate random key
function generateKey(keySize: number = 32): Uint8Array {
return randomBytes(keySize);
}
// Generate random nonce
function generateNonce(): Uint8Array {
return randomBytes(12); // 96-bit nonce
}import { concatBytes } from '@noble/hashes/utils.js';
// Build message from parts
function buildMessage(
header: Uint8Array,
payload: Uint8Array,
footer: Uint8Array
): Uint8Array {
return concatBytes(header, payload, footer);
}
// Prepend length prefix
function prependLength(data: Uint8Array): Uint8Array {
const length = new Uint8Array(4);
new DataView(length.buffer).setUint32(0, data.length, false);
return concatBytes(length, data);
}import { asyncLoop } from '@noble/hashes/utils.js';
// Process large dataset without blocking
async function processLargeDataset(items: any[]) {
let processed = 0;
await asyncLoop(items.length, 10, (i) => {
processItem(items[i]);
processed++;
// Update progress every 100 items
if (processed % 100 === 0) {
updateProgress(processed / items.length);
}
});
console.log('Processing complete');
}Uses crypto.getRandomValues():
clean() zeros out memory but JavaScript provides no guarantees:
Most systems are little-endian (x86, ARM). The library handles endianness automatically:
isLE constant for platform detectionswap8IfBE for conditional swappingValidation functions provide runtime type safety:
Hex conversion: Faster than alternatives like Buffer.toString('hex')
Concatenation: Efficient single allocation, better than multiple push/concat operations
Random bytes: Fast and secure, use liberally for salts and keys
Validation: Minimal overhead, use freely for public APIs
import { randomBytes, clean } from '@noble/hashes/utils.js';
function generateAndUseKey() {
const key = randomBytes(32);
try {
return performOperation(key);
} finally {
clean(key);
}
}import { utf8ToBytes, hexToBytes, isBytes } from '@noble/hashes/utils.js';
function normalizeInput(input: string | Uint8Array, encoding: 'utf8' | 'hex' = 'utf8'): Uint8Array {
if (isBytes(input)) {
return input;
}
return encoding === 'hex' ? hexToBytes(input) : utf8ToBytes(input);
}import { bytesToHex } from '@noble/hashes/utils.js';
function formatHash(hash: Uint8Array, prefix: boolean = true): string {
const hex = bytesToHex(hash);
return prefix ? `0x${hex}` : hex;
}
function formatHashShort(hash: Uint8Array, chars: number = 8): string {
const hex = bytesToHex(hash);
return `${hex.slice(0, chars)}...${hex.slice(-chars)}`;
}