UNPKG

@neuraiproject/neurai-key

Version:

Generate Neurai addresses from mnemonic code. BIP32, BIP39, BIP44

1,139 lines (1,133 loc) 484 kB
var NeuraiKeyBundle = (function () { 'use strict'; /** * 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])); * ``` */ function isBytes$2(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'); * ``` */ function anumber$2(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])); * ``` */ function abytes$1(value, length, title = '') { const bytes = isBytes$2(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; } /** * 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); * ``` */ function ahash(h) { if (typeof h !== 'function' || typeof h.create !== 'function') throw new TypeError('Hash must wrapped by utils.createHasher'); anumber$2(h.outputLen); anumber$2(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); * ``` */ 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); * ``` */ function aoutput(out, instance) { abytes$1(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 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)); * ``` */ 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])); * ``` */ 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)); * ``` */ 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); * ``` */ 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); * ``` */ function rotl(word, shift) { return (word << shift) | ((word >>> (32 - shift)) >>> 0); } /** Whether the current platform is little-endian. */ 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); * ``` */ function byteSwap(word) { return (((word << 24) & 0xff000000) | ((word << 8) & 0xff0000) | ((word >>> 8) & 0xff00) | ((word >>> 24) & 0xff)); } /** * 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])); * ``` */ 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])); * ``` */ 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' * ``` */ function bytesToHex$2(bytes) { abytes$1(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]) * ``` */ function hexToBytes$2(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; } /** * 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]) * ``` */ 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'); * ``` */ function kdfInputToBytes(data, errorTitle = '') { if (typeof data === 'string') return utf8ToBytes(data); return abytes$1(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])); * ``` */ function concatBytes$2(...arrays) { let sum = 0; for (let i = 0; i < arrays.length; i++) { const a = arrays[i]; abytes$1(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 }); * ``` */ 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])); * ``` */ 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); * ``` */ function randomBytes$2(bytesLength = 32) { // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion. anumber$2(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); * ``` */ 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]), }); /** * HMAC: RFC2104 message authentication code. * @module */ /** * Internal class for HMAC. * Accepts any byte key, although RFC 2104 §3 recommends keys at least * `HashLen` bytes long. */ class _HMAC { oHash; iHash; blockLen; outputLen; canXOF = false; finished = false; destroyed = false; constructor(hash, key) { ahash(hash); abytes$1(key, undefined, 'key'); this.iHash = hash.create(); if (typeof this.iHash.update !== 'function') throw new Error('Expected instance of class which extends utils.Hash'); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad = new Uint8Array(blockLen); // blockLen can be bigger than outputLen pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36; this.iHash.update(pad); // By doing update (processing of the first block) of the outer hash here, // we can re-use it between multiple calls via clone. this.oHash = hash.create(); // Undo internal XOR && apply outer XOR for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c; this.oHash.update(pad); clean(pad); } update(buf) { aexists(this); this.iHash.update(buf); return this; } digestInto(out) { aexists(this); aoutput(out, this); this.finished = true; const buf = out.subarray(0, this.outputLen); // Reuse the first outputLen bytes for the inner digest; the outer hash consumes them before // overwriting that same prefix with the final tag, leaving any oversized tail untouched. this.iHash.digestInto(buf); this.oHash.update(buf); this.oHash.digestInto(buf); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { // Create new instance without calling constructor since the key // is already in state and we don't know it. to ||= Object.create(Object.getPrototypeOf(this), {}); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } clone() { return this._cloneInto(); } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } } const hmac = /* @__PURE__ */ (() => { const hmac_ = ((hash, key, message) => new _HMAC(hash, key).update(message).digest()); hmac_.create = (hash, key) => new _HMAC(hash, key); return hmac_; })(); /** * PBKDF (RFC 2898). Can be used to create a key from password and salt. * @module */ // Common start and end for sync/async functions function pbkdf2Init(hash, _password, _salt, _opts) { ahash(hash); const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts); const { c, dkLen, asyncTick } = opts; anumber$2(c, 'c'); anumber$2(dkLen, 'dkLen'); anumber$2(asyncTick, 'asyncTick'); if (c < 1) throw new Error('iterations (c) must be >= 1'); // RFC 8018 §5.2 defines `dkLen` as "a positive integer". if (dkLen < 1) throw new Error('"dkLen" must be >= 1'); // RFC 8018 §5.2 step 1 requires rejecting oversize `dkLen` // before allocating the destination buffer. if (dkLen > (2 ** 32 - 1) * hash.outputLen) throw new Error('derived key too long'); const password = kdfInputToBytes(_password, 'password'); const salt = kdfInputToBytes(_salt, 'salt'); // DK = PBKDF2(PRF, Password, Salt, c, dkLen); const DK = new Uint8Array(dkLen); // U1 = PRF(Password, Salt + INT_32_BE(i)) const PRF = hmac.create(hash, password); // Cache PRF(P, S || ...) prefix state so each block only appends INT_32_BE(i). const PRFSalt = PRF._cloneInto().update(salt); return { c, dkLen, asyncTick, DK, PRF, PRFSalt }; } function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) { // Shared sync/async cleanup point: wipe transient PRF state // while preserving the derived key buffer. PRF.destroy(); PRFSalt.destroy(); if (prfW) prfW.destroy(); clean(u); return DK; } /** * PBKDF2-HMAC: RFC 8018 key derivation function. * @param hash - hash function that would be used e.g. sha256 * @param password - password from which a derived key is generated; * JS string inputs are UTF-8 encoded first * @param salt - cryptographic salt; JS string inputs are UTF-8 encoded first * @param opts - PBKDF2 work factor and output settings. `dkLen`, if provided, * must be `>= 1` per RFC 8018 §5.2. See {@link Pbkdf2Opt}. * @returns Derived key bytes. * @throws If the PBKDF2 iteration count or derived-key settings are invalid. {@link Error} * @example * PBKDF2-HMAC: RFC 2898 key derivation function. * ```ts * import { pbkdf2 } from '@noble/hashes/pbkdf2.js'; * import { sha256 } from '@noble/hashes/sha2.js'; * const key = pbkdf2(sha256, 'password', 'salt', { dkLen: 32, c: Math.pow(2, 18) }); * ``` */ function pbkdf2(hash, password, salt, opts) { const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts); let prfW; // Working copy const arr = new Uint8Array(4); const view = createView(arr); const u = new Uint8Array(PRF.outputLen); // DK = T1 + T2 + ⋯ + Tdklen/hlen for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { // Ti = F(Password, Salt, c, i) // The last Ti view can be shorter than hLen, which applies // RFC 8018 §5.2 step 4's T_l<0..r-1> truncation without extra copies. const Ti = DK.subarray(pos, pos + PRF.outputLen); view.setInt32(0, ti, false); // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc // U1 = PRF(Password, Salt + INT_32_BE(i)) (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u); Ti.set(u.subarray(0, Ti.length)); for (let ui = 1; ui < c; ui++) { // Uc = PRF(Password, Uc−1) PRF._cloneInto(prfW).update(u).digestInto(u); for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i]; } } return pbkdf2Output(PRF, PRFSalt, DK, prfW, u); } /** * Internal Merkle-Damgard hash utils. * @module */ /** * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`. * Returns bits from `b` when `a` is set, otherwise from `c`. * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never * set the same bit. * @param a - selector word * @param b - word chosen when selector bit is set * @param c - word chosen when selector bit is clear * @returns Mixed 32-bit word. * @example * Combine three words with the shared 32-bit choice primitive. * ```ts * Chi(0xffffffff, 0x12345678, 0x87654321); * ``` */ function Chi(a, b, c) { return (a & b) ^ (~a & c); } /** * Shared 32-bit majority primitive reused by SHA-256 and SHA-1. * Returns bits shared by at least two inputs. * @param a - first input word * @param b - second input word * @param c - third input word * @returns Mixed 32-bit word. * @example * Combine three words with the shared 32-bit majority primitive. * ```ts * Maj(0xffffffff, 0x12345678, 0x87654321); * ``` */ function Maj(a, b, c) { return (a & b) ^ (a & c) ^ (b & c); } /** * Merkle-Damgard hash construction base class. * Could be used to create MD5, RIPEMD, SHA1, SHA2. * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit * strings with partial-byte tails. * @param blockLen - internal block size in bytes * @param outputLen - digest size in bytes * @param padOffset - trailing length field size in bytes * @param isLE - whether length and state words are encoded in little-endian * @example * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow. * ```ts * import { _SHA1 } from '@noble/hashes/legacy.js'; * const hash = new _SHA1(); * hash.update(new Uint8Array([97, 98, 99])); * hash.digest(); * ``` */ class HashMD { blockLen; outputLen; canXOF = false; padOffset; isLE; // For partial updates less than block size buffer; view; finished = false; length = 0; pos = 0; destroyed = false; constructor(blockLen, outputLen, padOffset, isLE) { this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } update(data) { aexists(this); abytes$1(data); const { view, buffer, blockLen } = this; const len = data.length; for (let pos = 0; pos < len;) { const take = Math.min(blockLen - this.pos, len - pos); // Fast path only when there is no buffered partial block: `take === blockLen` implies // `this.pos === 0`, so we can process full blocks directly from the input view. if (take === blockLen) { const dataView = createView(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { aexists(this); aoutput(out, this); this.finished = true; // Padding // We can avoid allocation of buffer for padding completely if it // was previously not allocated here. But it won't change performance. const { buffer, view, blockLen, isLE } = this; let { pos } = this; // append the bit '1' to the message buffer[pos++] = 0b10000000; clean(this.buffer.subarray(pos)); // we have less than padOffset left in buffer, so we cannot put length in // current block, need process it and pad again if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } // Pad until full block byte with zeros for (let i = pos; i < blockLen; i++) buffer[i] = 0; // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from // the padding fill above, and JS will overflow before user input can make that half non-zero. // So we only need to write the low 64 bits here. view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE); this.process(view, 0); const oview = createView(out); const len = this.outputLen; // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit'); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state'); for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return // fresh bytes to the caller. const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to ||= new this.constructor(); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.destroyed = destroyed; to.finished = finished; to.length = length; to.pos = pos; // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it. if (length % blockLen) to.buffer.set(buffer); return to; } clone() { return this._cloneInto(); } } /** * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53. * Check out `test/misc/sha2-gen-iv.js` for recomputation guide. */ /** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the * square roots of the first eight prime numbers. Exported as a shared table; callers must treat * it as read-only because constructors copy words from it by index. */ const SHA256_IV = /* @__PURE__ */ Uint32Array.from([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]); /** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first * eight prime numbers. Exported as a shared table; callers must treat it as read-only because * constructors copy halves from it by index. */ const SHA512_IV = /* @__PURE__ */ Uint32Array.from([ 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1, 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179, ]); const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); const _32n = /* @__PURE__ */ BigInt(32); // Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high // }` to match little-endian word order rather than the property names. function fromBig(n, le = false) { if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) }; return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; } // Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array // holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`. function split(lst, le = false) { const len = lst.length; let Ah = new Uint32Array(len); let Al = new Uint32Array(len); for (let i = 0; i < len; i++) { const { h, l } = fromBig(lst[i], le); [Ah[i], Al[i]] = [h, l]; } return [Ah, Al]; } // High 32-bit half of a 64-bit logical right shift for `s` in `0..31`. const shrSH = (h, _l, s) => h >>> s; // Low 32-bit half of a 64-bit logical right shift, valid for `s` in `1..31`. const shrSL = (h, l, s) => (h << (32 - s)) | (l >>> s); // High 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`. const rotrSH = (h, l, s) => (h >>> s) | (l << (32 - s)); // Low 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`. const rotrSL = (h, l, s) => (h << (32 - s)) | (l >>> s); // High 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. const rotrBH = (h, l, s) => (h << (64 - s)) | (l >>> (s - 32)); // Low 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. const rotrBL = (h, l, s) => (h >>> (s - 32)) | (l << (64 - s)); // High 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`. const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s)); // Low 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`. const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s)); // High 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s)); // Low 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s)); // Add two split 64-bit words and return the split `{ h, l }` sum. // JS uses 32-bit signed integers for bitwise operations, so we cannot simply shift the carry out // of the low sum and instead use division. function add(Ah, Al, Bh, Bl) { const l = (Al >>> 0) + (Bl >>> 0); return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 }; } // Addition with more than 2 elements // Unmasked low-word accumulator for 3-way addition; pass the raw result into `add3H(...)`. const add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); // High-word finalize step for 3-way addition; `low` must be the untruncated output of `add3L(...)`. const add3H = (low, Ah, Bh, Ch) => (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0; // Unmasked low-word accumulator for 4-way addition; pass the raw result into `add4H(...)`. const add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); // High-word finalize step for 4-way addition; `low` must be the untruncated output of `add4L(...)`. const add4H = (low, Ah, Bh, Ch, Dh) => (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0; // Unmasked low-word accumulator for 5-way addition; pass the raw result into `add5H(...)`. const add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); // High-word finalize step for 5-way addition; `low` must be the untruncated output of `add5L(...)`. const add5H = (low, Ah, Bh, Ch, Dh, Eh) => (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0; /** * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256. * SHA256 is the fastest hash implementable in JS, even faster than Blake3. * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}. * @module */ /** * SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits * of the cube roots of the first 64 primes (2..311). */ // prettier-ignore const SHA256_K = /* @__PURE__ */ Uint32Array.from([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]); /** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */ const SHA256_W = /* @__PURE__ */ new Uint32Array(64); /** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */ class SHA2_32B extends HashMD { constructor(outputLen) { super(64, outputLen, 8, false); } get() { const { A, B, C, D, E, F, G, H } = this; return [A, B, C, D, E, F, G, H]; } // prettier-ignore set(A, B, C, D, E, F, G, H) { this.A = A | 0; this.B = B | 0; this.C = C | 0; this.D = D | 0; this.E = E | 0; this.F = F | 0; this.G = G | 0; this.H = H | 0; } process(view, offset) { // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false); for (let i = 16; i < 64; i++) { const W15 = SHA256_W[i - 15]; const W2 = SHA256_W[i - 2]; const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3); const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10); SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0; } // Compression function main loop, 64 rounds let { A, B, C, D, E, F, G, H } = this; for (let i = 0; i < 64; i++) { const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25); const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0; const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); const T2 = (sigma0 + Maj(A, B, C)) | 0; H = G; G = F; F = E; E = (D + T1) | 0; D = C; C = B; B = A; A = (T1 + T2) | 0; } // Add the compressed chunk to the current hash value A = (A + this.A) | 0; B = (B + this.B) | 0; C = (C + this.C) | 0; D = (D + this.D) | 0; E = (E + this.E) | 0; F = (F + this.F) | 0; G = (G + this.G) | 0; H = (H + this.H) | 0; this.set(A, B, C, D, E, F, G, H); } roundClean() { clean(SHA256_W); } destroy() { // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves // update()/digest() callable on reused instances. this.destroyed = true; this.set(0, 0, 0, 0, 0, 0, 0, 0); clean(this.buffer); } } /** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */ class _SHA256 extends SHA2_32B { // We cannot use array here since array allows indexing by variable // which means optimizer/compiler cannot use registers. A = SHA256_IV[0] | 0; B = SHA256_IV[1] | 0; C = SHA256_IV[2] | 0; D = SHA256_IV[3] | 0; E = SHA256_IV[4] | 0; F = SHA256_IV[5] | 0; G = SHA256_IV[6] | 0; H = SHA256_IV[7] | 0; constructor() { super(32); } } // SHA2-512 is slower than sha256 in js because u64 operations are slow. // SHA-384 / SHA-512 round constants from RFC 6234 §5.2: // 80 full 64-bit words split into high/low halves. // prettier-ignore const K512 = /* @__PURE__ */ (() => split([ '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc', '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118', '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2', '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694', '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65', '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5', '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4', '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70', '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df', '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b', '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30', '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8', '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8', '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3', '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec', '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b', '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178', '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b', '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c', '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817' ].map(n => BigInt(n))))(); const SHA512_Kh = /* @__PURE__ */ (() => K512[0])(); const SHA512_Kl = /* @__PURE__ */ (() => K512[1])(); // Reusable high-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words. const SHA512_W_H = /* @__PURE__ */ new Uint32Array(80); // Reusable low-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words. const SHA512_W_L = /* @__PURE__ */ new Uint32Array(80); /** Internal SHA-384 / SHA-512 compression engine from RFC 6234 §6.4. */ class SHA2_64B extends HashMD { constructor(outputLen) { super(128, outputLen, 16, false); } // prettier-ignore get() { const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; } // prettier-ignore set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) { this.Ah = Ah | 0; this.Al = Al | 0; this.Bh = Bh | 0; this.Bl = Bl | 0; this.Ch = Ch | 0; this.Cl = Cl | 0; this.Dh = Dh | 0; this.Dl = Dl | 0; this.Eh = Eh | 0; this.El = El | 0; this.Fh = Fh | 0; this.Fl = Fl | 0; this.Gh = Gh | 0; this.Gl = Gl | 0; this.Hh = Hh | 0; this.Hl = Hl | 0; } process(view, offset) { // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array for (let i = 0; i < 16; i++, offset += 4) { SHA512_W_H[i] = view.getUint32(offset); SHA512_W_L[i] = view.getUint32((offset += 4)); } for (let i = 16; i < 80; i++) { // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7) const W15h = SHA512_W_H[i - 15] | 0; const W15l = SHA512_W_L[i - 15] | 0; const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7); const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7); // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6) const W2h = SHA512_W_H[i - 2] | 0; const W2l = SHA512_W_L[i - 2] | 0; const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6); const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6); // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16]; const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]); const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]); SHA512_W_H[i] = SUMh | 0; SHA512_W_L[i] = SUMl | 0; } let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; // Compression function main loop, 80 rounds for (let i = 0; i < 80; i++) { // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41) const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41); const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41); //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0; const CHIh = (Eh & Fh) ^ (~Eh & Gh); const CHIl = (El & Fl) ^ (~El & Gl); // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i] // prettier-ignore const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]); const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]); const T1l = T1ll | 0; /