/** * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. * @param a - value to test * @returns `true` when the value is a Uint8Array-compatible view. * @example * Check whether a value is a Uint8Array-compatible view. * ```ts * isBytes(new Uint8Array([1, 2, 3])); * ``` */ export function isBytes(a) { // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases. // The fallback still requires a real ArrayBuffer view, so plain // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views. return (a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array' && 'BYTES_PER_ELEMENT' in a && a.BYTES_PER_ELEMENT === 1)); } /** * Asserts something is a non-negative integer. * @param n - number to validate * @param title - label included in thrown errors * @throws On wrong argument types. {@link TypeError} * @throws On wrong argument ranges or values. {@link RangeError} * @example * Validate a non-negative integer option. * ```ts * anumber(32, 'length'); * ``` */ export function anumber(n, title = '') { if (typeof n !== 'number') { const prefix = title && `"${title}" `; throw new TypeError(`${prefix}expected number, got ${typeof n}`); } if (!Number.isSafeInteger(n) || n < 0) { const prefix = title && `"${title}" `; throw new RangeError(`${prefix}expected integer >= 0, got ${n}`); } } /** * Asserts something is Uint8Array. * @param value - value to validate * @param length - optional exact length constraint * @param title - label included in thrown errors * @returns The validated byte array. * @throws On wrong argument types. {@link TypeError} * @throws On wrong argument ranges or values. {@link RangeError} * @example * Validate that a value is a byte array. * ```ts * abytes(new Uint8Array([1, 2, 3])); * ``` */ export function abytes(value, length, title = '') { const bytes = isBytes(value); const len = value?.length; const needsLen = length !== undefined; if (!bytes || (needsLen && len !== length)) { const prefix = title && `"${title}" `; const ofLen = needsLen ? ` of length ${length}` : ''; const got = bytes ? `length=${len}` : `type=${typeof value}`; const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got; if (!bytes) throw new TypeError(message); throw new RangeError(message); } return value; } /** * Copies bytes into a fresh Uint8Array. * Buffer-style slices can alias the same backing store, so callers that need ownership should copy. * @param bytes - source bytes to clone * @returns Freshly allocated copy of `bytes`. * @throws On wrong argument types. {@link TypeError} * @example * Clone a byte array before mutating it. * ```ts * const copy = copyBytes(new Uint8Array([1, 2, 3])); * ``` */ export function copyBytes(bytes) { // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict // because callers use it at byte-validation boundaries before mutating the detached copy. return Uint8Array.from(abytes(bytes)); } /** * Asserts something is a wrapped hash constructor. * @param h - hash constructor to validate * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError} * @throws On invalid hash metadata ranges or values. {@link RangeError} * @throws If the hash metadata allows empty outputs or block sizes. {@link Error} * @example * Validate a callable hash wrapper. * ```ts * import { ahash } from '@noble/hashes/utils.js'; * import { sha256 } from '@noble/hashes/sha2.js'; * ahash(sha256); * ``` */ export function ahash(h) { if (typeof h !== 'function' || typeof h.create !== 'function') throw new TypeError('Hash must wrapped by utils.createHasher'); anumber(h.outputLen); anumber(h.blockLen); // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass // validation and can produce empty outputs instead of failing fast. if (h.outputLen < 1) throw new Error('"outputLen" must be >= 1'); if (h.blockLen < 1) throw new Error('"blockLen" must be >= 1'); } /** * Asserts a hash instance has not been destroyed or finished. * @param instance - hash instance to validate * @param checkFinished - whether to reject finalized instances * @throws If the hash instance has already been destroyed or finalized. {@link Error} * @example * Validate that a hash instance is still usable. * ```ts * import { aexists } from '@noble/hashes/utils.js'; * import { sha256 } from '@noble/hashes/sha2.js'; * const hash = sha256.create(); * aexists(hash); * ``` */ export function aexists(instance, checkFinished = true) { if (instance.destroyed) throw new Error('Hash instance has been destroyed'); if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called'); } /** * Asserts output is a sufficiently-sized byte array. * @param out - destination buffer * @param instance - hash instance providing output length * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes. * @throws On wrong argument types. {@link TypeError} * @throws On wrong argument ranges or values. {@link RangeError} * @example * Validate a caller-provided digest buffer. * ```ts * import { aoutput } from '@noble/hashes/utils.js'; * import { sha256 } from '@noble/hashes/sha2.js'; * const hash = sha256.create(); * aoutput(new Uint8Array(hash.outputLen), hash); * ``` */ export function aoutput(out, instance) { abytes(out, undefined, 'digestInto() output'); const min = instance.outputLen; if (out.length < min) { throw new RangeError('"digestInto() output" expected to be of length >=' + min); } } /** * Casts a typed array view to Uint8Array. * @param arr - source typed array * @returns Uint8Array view over the same buffer. * @example * Reinterpret a typed array as bytes. * ```ts * u8(new Uint32Array([1, 2])); * ``` */ export function u8(arr) { return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength); } /** * Casts a typed array view to Uint32Array. * `arr.byteOffset` must already be 4-byte aligned or the platform * Uint32Array constructor will throw. * @param arr - source typed array * @returns Uint32Array view over the same buffer. * @example * Reinterpret a byte array as 32-bit words. * ```ts * u32(new Uint8Array(8)); * ``` */ export function u32(arr) { return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); } /** * Zeroizes typed arrays in place. Warning: JS provides no guarantees. * @param arrays - arrays to overwrite with zeros * @example * Zeroize sensitive buffers in place. * ```ts * clean(new Uint8Array([1, 2, 3])); * ``` */ export function clean(...arrays) { for (let i = 0; i < arrays.length; i++) { arrays[i].fill(0); } } /** * Creates a DataView for byte-level manipulation. * @param arr - source typed array * @returns DataView over the same buffer region. * @example * Create a DataView over an existing buffer. * ```ts * createView(new Uint8Array(4)); * ``` */ export function createView(arr) { return new DataView(arr.buffer, arr.byteOffset, arr.byteLength); } /** * Rotate-right operation for uint32 values. * @param word - source word * @param shift - shift amount in bits * @returns Rotated word. * @example * Rotate a 32-bit word to the right. * ```ts * rotr(0x12345678, 8); * ``` */ export function rotr(word, shift) { return (word << (32 - shift)) | (word >>> shift); } /** * Rotate-left operation for uint32 values. * @param word - source word * @param shift - shift amount in bits * @returns Rotated word. * @example * Rotate a 32-bit word to the left. * ```ts * rotl(0x12345678, 8); * ``` */ export function rotl(word, shift) { return (word << shift) | ((word >>> (32 - shift)) >>> 0); } /** Whether the current platform is little-endian. */ export const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)(); /** * Byte-swap operation for uint32 values. * @param word - source word * @returns Word with reversed byte order. * @example * Reverse the byte order of a 32-bit word. * ```ts * byteSwap(0x11223344); * ``` */ export function byteSwap(word) { return (((word << 24) & 0xff000000) | ((word << 8) & 0xff0000) | ((word >>> 8) & 0xff00) | ((word >>> 24) & 0xff)); } /** * Conditionally byte-swaps one 32-bit word on big-endian platforms. * @param n - source word * @returns Original or byte-swapped word depending on platform endianness. * @example * Normalize a 32-bit word for host endianness. * ```ts * swap8IfBE(0x11223344); * ``` */ export const swap8IfBE = isLE ? (n) => n : (n) => byteSwap(n) >>> 0; /** * Byte-swaps every word of a Uint32Array in place. * @param arr - array to mutate * @returns The same array after mutation; callers pass live state arrays here. * @example * Reverse the byte order of every word in place. * ```ts * byteSwap32(new Uint32Array([0x11223344])); * ``` */ export function byteSwap32(arr) { for (let i = 0; i < arr.length; i++) { arr[i] = byteSwap(arr[i]); } return arr; } /** * Conditionally byte-swaps a Uint32Array on big-endian platforms. * @param u - array to normalize for host endianness * @returns Original or byte-swapped array depending on platform endianness. * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`. * @example * Normalize a word array for host endianness. * ```ts * swap32IfBE(new Uint32Array([0x11223344])); * ``` */ export const swap32IfBE = isLE ? (u) => u : byteSwap32; // Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex const hasHexBuiltin = /* @__PURE__ */ (() => // @ts-ignore typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')(); // Array where index 0xf0 (240) is mapped to string 'f0' const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0')); /** * Convert byte array to hex string. * Uses the built-in function when available and assumes it matches the tested * fallback semantics. * @param bytes - bytes to encode * @returns Lowercase hexadecimal string. * @throws On wrong argument types. {@link TypeError} * @example * Convert bytes to lowercase hexadecimal. * ```ts * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123' * ``` */ export function bytesToHex(bytes) { abytes(bytes); // @ts-ignore if (hasHexBuiltin) return bytes.toHex(); // pre-caching improves the speed 6x let hex = ''; for (let i = 0; i < bytes.length; i++) { hex += hexes[bytes[i]]; } return hex; } // We use optimized technique to convert hex string to byte array const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; function asciiToBase16(ch) { if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48 if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10) if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10) return; } /** * Convert hex string to byte array. Uses built-in function, when available. * @param hex - hexadecimal string to decode * @returns Decoded bytes. * @throws On wrong argument types. {@link TypeError} * @throws On wrong argument ranges or values. {@link RangeError} * @example * Decode lowercase hexadecimal into bytes. * ```ts * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23]) * ``` */ export function hexToBytes(hex) { if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex); if (hasHexBuiltin) { try { return Uint8Array.fromHex(hex); } catch (error) { if (error instanceof SyntaxError) throw new RangeError(error.message); throw error; } } const hl = hex.length; const al = hl / 2; if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase16(hex.charCodeAt(hi)); const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); if (n1 === undefined || n2 === undefined) { const char = hex[hi] + hex[hi + 1]; throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163 } return array; } /** * There is no setImmediate in browser and setTimeout is slow. * This yields to the Promise/microtask scheduler queue, not to timers or the * full macrotask event loop. * @example * Yield to the next scheduler tick. * ```ts * await nextTick(); * ``` */ export const nextTick = async () => { }; /** * Returns control to the Promise/microtask scheduler every `tick` * milliseconds to avoid blocking long loops. * @param iters - number of loop iterations to run * @param tick - maximum time slice in milliseconds * @param cb - callback executed on each iteration * @example * Run a loop that periodically yields back to the event loop. * ```ts * await asyncLoop(2, 0, () => {}); * ``` */ export async function asyncLoop(iters, tick, cb) { let ts = Date.now(); for (let i = 0; i < iters; i++) { cb(i); // Date.now() is not monotonic, so in case if clock goes backwards we return return control too const diff = Date.now() - ts; if (diff >= 0 && diff < tick) continue; await nextTick(); ts += diff; } } /** * Converts string to bytes using UTF8 encoding. * Built-in doesn't validate input to be string: we do the check. * Non-ASCII details are delegated to the platform `TextEncoder`. * @param str - string to encode * @returns UTF-8 encoded bytes. * @throws On wrong argument types. {@link TypeError} * @example * Encode a string as UTF-8 bytes. * ```ts * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99]) * ``` */ export function utf8ToBytes(str) { if (typeof str !== 'string') throw new TypeError('string expected'); return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809 } /** * Helper for KDFs: consumes Uint8Array or string. * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer. * @param data - user-provided KDF input * @param errorTitle - label included in thrown errors * @returns Byte representation of the input. * @throws On wrong argument types. {@link TypeError} * @example * Normalize KDF input to bytes. * ```ts * kdfInputToBytes('password'); * ``` */ export function kdfInputToBytes(data, errorTitle = '') { if (typeof data === 'string') return utf8ToBytes(data); return abytes(data, undefined, errorTitle); } /** * Copies several Uint8Arrays into one. * @param arrays - arrays to concatenate * @returns Concatenated byte array. * @throws On wrong argument types. {@link TypeError} * @example * Concatenate multiple byte arrays. * ```ts * concatBytes(new Uint8Array([1]), new Uint8Array([2])); * ``` */ export function concatBytes(...arrays) { let sum = 0; for (let i = 0; i < arrays.length; i++) { const a = arrays[i]; abytes(a); sum += a.length; } const res = new Uint8Array(sum); for (let i = 0, pad = 0; i < arrays.length; i++) { const a = arrays[i]; res.set(a, pad); pad += a.length; } return res; } /** * Merges default options and passed options. * @param defaults - base option object * @param opts - user overrides * @returns Merged option object. The merge mutates `defaults` in place. * @throws On wrong argument types. {@link TypeError} * @example * Merge user overrides onto default options. * ```ts * checkOpts({ dkLen: 32 }, { asyncTick: 10 }); * ``` */ export function checkOpts(defaults, opts) { if (opts !== undefined && {}.toString.call(opts) !== '[object Object]') throw new TypeError('options must be object or undefined'); const merged = Object.assign(defaults, opts); return merged; } /** * Creates a callable hash function from a stateful class constructor. * @param hashCons - hash constructor or factory * @param info - optional metadata such as DER OID * @returns Frozen callable hash wrapper with `.create()`. * Wrapper construction eagerly calls `hashCons(undefined)` once to read * `outputLen` / `blockLen`, so constructor side effects happen at module * init time. * @example * Wrap a stateful hash constructor into a callable helper. * ```ts * import { createHasher } from '@noble/hashes/utils.js'; * import { sha256 } from '@noble/hashes/sha2.js'; * const wrapped = createHasher(sha256.create, { oid: sha256.oid }); * wrapped(new Uint8Array([1])); * ``` */ export function createHasher(hashCons, info = {}) { const hashC = (msg, opts) => hashCons(opts) .update(msg) .digest(); const tmp = hashCons(undefined); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.canXOF = tmp.canXOF; hashC.create = (opts) => hashCons(opts); Object.assign(hashC, info); return Object.freeze(hashC); } /** * Cryptographically secure PRNG backed by `crypto.getRandomValues`. * @param bytesLength - number of random bytes to generate * @returns Random bytes. * The platform `getRandomValues()` implementation still defines any * single-call length cap, and this helper rejects oversize requests * with a stable library `RangeError` instead of host-specific errors. * @throws On wrong argument types. {@link TypeError} * @throws On wrong argument ranges or values. {@link RangeError} * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error} * @example * Generate a fresh random key or nonce. * ```ts * const key = randomBytes(16); * ``` */ export function randomBytes(bytesLength = 32) { // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion. anumber(bytesLength, 'bytesLength'); const cr = typeof globalThis === 'object' ? globalThis.crypto : null; if (typeof cr?.getRandomValues !== 'function') throw new Error('crypto.getRandomValues must be defined'); // Web Cryptography API Level 2 ยง10.1.1: // if `byteLength > 65536`, throw `QuotaExceededError`. // Keep the guard explicit so callers can see the quota in code // instead of discovering it by reading the spec or host errors. // This wrapper surfaces the same quota as a stable library RangeError. if (bytesLength > 65536) throw new RangeError(`"bytesLength" expected <= 65536, got ${bytesLength}`); return cr.getRandomValues(new Uint8Array(bytesLength)); } /** * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`. * @param suffix - final OID byte for the selected hash. * The helper accepts any byte even though only the documented NIST hash * suffixes are meaningful downstream. * @returns Object containing the DER-encoded OID. * @example * Build OID metadata for a NIST hash. * ```ts * oidNist(0x01); * ``` */ export const oidNist = (suffix) => ({ // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet. // Larger suffix values would need base-128 OID encoding and a different length byte. oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]), }); //# sourceMappingURL=utils.js.map