CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-noble--hashes

Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt

Overview
Eval results
Files

utils.mddocs/

Utilities: Core Helper Functions

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.

Imports

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';

Capabilities

Encoding and Decoding

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 bytes

Array Operations

Utility 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 zeros

Bitwise Operations

Low-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"

Validation Functions

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 range

Cryptographic Utilities

Core 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' }

Async Helpers

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
  });
}

Constants

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)');
}

Usage Examples

Hex String Utilities

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);
}

Secure Memory Cleanup

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);
}

Input Validation

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 });
}

Random Data Generation

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
}

Array Concatenation

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);
}

Async Processing

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');
}

Technical Details

Encoding Performance

  • bytesToHex: ~5μs per 32 bytes
  • hexToBytes: ~3μs per 64-char hex string
  • utf8ToBytes: Uses TextEncoder (fast, native)

Random Number Generation

Uses crypto.getRandomValues():

  • Cryptographically secure (CSPRNG)
  • Available in browsers and Node.js
  • Thread-safe
  • No user-space entropy collection needed

Memory Cleanup

clean() zeros out memory but JavaScript provides no guarantees:

  • Strings are immutable (can't be cleaned)
  • GC may leave copies in memory
  • Async operations may write to memory
  • Use as defense-in-depth, not absolute security

Endianness

Most systems are little-endian (x86, ARM). The library handles endianness automatically:

  • isLE constant for platform detection
  • swap8IfBE for conditional swapping
  • Hash algorithms use proper byte order internally

Type Safety

Validation functions provide runtime type safety:

  • Catch errors early with clear messages
  • Prevent undefined behavior
  • Useful for user input validation
  • TypeScript types + runtime checks = maximum safety

Performance Tips

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

Common Patterns

Safe Key Generation

import { randomBytes, clean } from '@noble/hashes/utils.js';

function generateAndUseKey() {
  const key = randomBytes(32);

  try {
    return performOperation(key);
  } finally {
    clean(key);
  }
}

Input Normalization

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);
}

Hex Display

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)}`;
}

References

  • TextEncoder API: UTF-8 encoding
  • Crypto.getRandomValues: Random number generation
  • TypedArray: Binary data arrays

Install with Tessl CLI

npx tessl i tessl/npm-noble--hashes@2.0.0

docs

argon2.md

blake.md

eskdf.md

hkdf.md

hmac.md

index.md

legacy.md

pbkdf2.md

scrypt.md

sha2.md

sha3-addons.md

sha3.md

utils.md

webcrypto.md

tile.json