UNPKG

otpauth

Version:

One Time Password (HOTP/TOTP) library for Node.js, Deno, Bun and browsers

1,351 lines (1,343 loc) 87.2 kB
//! otpauth 9.5.1 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth //! noble-hashes 2.2.0 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes /// <reference types="./otpauth.d.ts" /> // @ts-nocheck /** * Converts an integer to an Uint8Array. * @param {number} num Integer. * @returns {Uint8Array} Uint8Array. */ const uintDecode = (num)=>{ const buf = new ArrayBuffer(8); const arr = new Uint8Array(buf); let acc = num; for(let i = 7; i >= 0; i--){ if (acc === 0) break; arr[i] = acc & 255; acc -= arr[i]; acc /= 256; } return arr; }; /** * 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(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(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(value, length, title = '') { const bytes = isBytes(value); const len = value?.length; const needsLen = length !== undefined; if (!bytes || needsLen) { const prefix = title && `"${title}" `; const ofLen = ''; 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(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); * ``` */ 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(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; /** * 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); } /** * 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 ]) }); /** * Internal class for HMAC. * Accepts any byte key, although RFC 2104 §3 recommends keys at least * `HashLen` bytes long. */ class _HMAC { 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 || (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(); } constructor(hash, key){ this.canXOF = false; this.finished = false; this.destroyed = false; ahash(hash); abytes(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); } } 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_; })(); /** * 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 { update(data) { aexists(this); abytes(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 || (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(); } constructor(blockLen, outputLen, padOffset, isLE){ this.canXOF = false; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } } /** * 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 SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must * treat it as read-only because constructors copy words from it by index. */ const SHA224_IV = /* @__PURE__ */ Uint32Array.from([ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]); /** Initial SHA384 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 ninth * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only * because constructors copy halves from it by index. */ const SHA384_IV = /* @__PURE__ */ Uint32Array.from([ 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939, 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4 ]); /** 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 ]); /** Initial SHA-1 state from RFC 3174 §6.1. */ const SHA1_IV = /* @__PURE__ */ Uint32Array.from([ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]); // Reusable 80-word SHA-1 message schedule buffer. const SHA1_W = /* @__PURE__ */ new Uint32Array(80); /** Internal SHA1 legacy hash class. */ class _SHA1 extends HashMD { get() { const { A, B, C, D, E } = this; return [ A, B, C, D, E ]; } set(A, B, C, D, E) { this.A = A | 0; this.B = B | 0; this.C = C | 0; this.D = D | 0; this.E = E | 0; } process(view, offset) { for(let i = 0; i < 16; i++, offset += 4)SHA1_W[i] = view.getUint32(offset, false); for(let i = 16; i < 80; i++)SHA1_W[i] = rotl(SHA1_W[i - 3] ^ SHA1_W[i - 8] ^ SHA1_W[i - 14] ^ SHA1_W[i - 16], 1); // Compression function main loop, 80 rounds let { A, B, C, D, E } = this; for(let i = 0; i < 80; i++){ let F, K; if (i < 20) { F = Chi(B, C, D); K = 0x5a827999; } else if (i < 40) { F = B ^ C ^ D; K = 0x6ed9eba1; } else if (i < 60) { F = Maj(B, C, D); K = 0x8f1bbcdc; } else { F = B ^ C ^ D; K = 0xca62c1d6; } const T = rotl(A, 5) + F + E + K + SHA1_W[i] | 0; E = D; D = C; C = rotl(B, 30); B = A; A = T; } // 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; this.set(A, B, C, D, E); } roundClean() { clean(SHA1_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); clean(this.buffer); } constructor(){ super(64, 20, 8, false), this.A = SHA1_IV[0] | 0, this.B = SHA1_IV[1] | 0, this.C = SHA1_IV[2] | 0, this.D = SHA1_IV[3] | 0, this.E = SHA1_IV[4] | 0; } } /** * SHA1 (RFC 3174) legacy hash function. It was cryptographically broken. * @param msg - message bytes to hash * @returns Digest bytes. * @example * Hash a message with SHA1. * ```ts * sha1(new Uint8Array([97, 98, 99])); * ``` */ const sha1 = /* @__PURE__ */ createHasher(()=>new _SHA1()); 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; /** * 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 { 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); } constructor(outputLen){ super(64, outputLen, 8, false); } } /** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */ class _SHA256 extends SHA2_32B { constructor(){ super(32), // We cannot use array here since array allows indexing by variable // which means optimizer/compiler cannot use registers. this.A = SHA256_IV[0] | 0, this.B = SHA256_IV[1] | 0, this.C = SHA256_IV[2] | 0, this.D = SHA256_IV[3] | 0, this.E = SHA256_IV[4] | 0, this.F = SHA256_IV[5] | 0, this.G = SHA256_IV[6] | 0, this.H = SHA256_IV[7] | 0; } } /** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */ class _SHA224 extends SHA2_32B { constructor(){ super(28), this.A = SHA224_IV[0] | 0, this.B = SHA224_IV[1] | 0, this.C = SHA224_IV[2] | 0, this.D = SHA224_IV[3] | 0, this.E = SHA224_IV[4] | 0, this.F = SHA224_IV[5] | 0, this.G = SHA224_IV[6] | 0, this.H = SHA224_IV[7] | 0; } } // 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 { // 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; // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39) const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39); const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39); const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch; const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl; Hh = Gh | 0; Hl = Gl | 0; Gh = Fh | 0; Gl = Fl | 0; Fh = Eh | 0; Fl = El | 0; ({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); Dh = Ch | 0; Dl = Cl | 0; Ch = Bh | 0; Cl = Bl | 0; Bh = Ah | 0; Bl = Al | 0; const All = add3L(T1l, sigma0l, MAJl); Ah = add3H(All, T1h, sigma0h, MAJh); Al = All | 0; } // Add the compressed chunk to the current hash value ({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); ({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); ({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); ({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); ({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); ({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); ({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); ({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); } roundClean() { clean(SHA512_W_H, SHA512_W_L); } destroy() { // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves // update()/digest() callable on reused instances. this.destroyed = true; clean(this.buffer); this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } constructor(outputLen){ super(128, outputLen, 16, false); } } /** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */ class _SHA512 extends SHA2_64B { constructor(){ super(64), this.Ah = SHA512_IV[0] | 0, this.Al = SHA512_IV[1] | 0, this.Bh = SHA512_IV[2] | 0, this.Bl = SHA512_IV[3] | 0, this.Ch = SHA512_IV[4] | 0, this.Cl = SHA512_IV[5] | 0, this.Dh = SHA512_IV[6] | 0, this.Dl = SHA512_IV[7] | 0, this.Eh = SHA512_IV[8] | 0, this.El = SHA512_IV[9] | 0, this.Fh = SHA512_IV[10] | 0, this.Fl = SHA512_IV[11] | 0, this.Gh = SHA512_IV[12] | 0, this.Gl = SHA512_IV[13] | 0, this.Hh = SHA512_IV[14] | 0, this.Hl = SHA512_IV[15] | 0; } } /** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */ class _SHA384 extends SHA2_64B { constructor(){ super(48), this.Ah = SHA384_IV[0] | 0, this.Al = SHA384_IV[1] | 0, this.Bh = SHA384_IV[2] | 0, this.Bl = SHA384_IV[3] | 0, this.Ch = SHA384_IV[4] | 0, this.Cl = SHA384_IV[5] | 0, this.Dh = SHA384_IV[6] | 0, this.Dl = SHA384_IV[7] | 0, this.Eh = SHA384_IV[8] | 0, this.El = SHA384_IV[9] | 0, this.Fh = SHA384_IV[10] | 0, this.Fl = SHA384_IV[11] | 0, this.Gh = SHA384_IV[12] | 0, this.Gl = SHA384_IV[13] | 0, this.Hh = SHA384_IV[14] | 0, this.Hl = SHA384_IV[15] | 0; } } /** * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info: * * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack. * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025. * - Each sha256 hash is executing 2^18 bit operations. * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule. * @param msg - message bytes to hash * @returns Digest bytes. * @example * Hash a message with SHA2-256. * ```ts * sha256(new Uint8Array([97, 98, 99])); * ``` */ const sha256 = /* @__PURE__ */ createHasher(()=>new _SHA256(), /* @__PURE__ */ oidNist(0x01)); /** * SHA2-224 hash function from RFC 4634. * @param msg - message bytes to hash * @returns Digest bytes. * @example * Hash a message with SHA2-224. * ```ts * sha224(new Uint8Array([97, 98, 99])); * ``` */ const sha224 = /* @__PURE__ */ createHasher(()=>new _SHA224(), /* @__PURE__ */ oidNist(0x04)); /** * SHA2-512 hash function from RFC 4634. * @param msg - message bytes to hash * @returns Digest bytes. * @example * Hash a message with SHA2-512. * ```ts * sha512(new Uint8Array([97, 98, 99])); * ``` */ const sha512 = /* @__PURE__ */ createHasher(()=>new _SHA512(), /* @__PURE__ */ oidNist(0x03)); /** * SHA2-384 hash function from RFC 4634. * @param msg - message bytes to hash * @returns Digest bytes. * @example * Hash a message with SHA2-384. * ```ts * sha384(new Uint8Array([97, 98, 99])); * ``` */ const sha384 = /* @__PURE__ */ createHasher(()=>new _SHA384(), /* @__PURE__ */ oidNist(0x02)); // No __PURE__ annotations in sha3 header: // EVERYTHING is in fact used on every export. // Various per round constants calculations const _0n = BigInt(0); const _1n = BigInt(1); const _2n = BigInt(2); const _7n = BigInt(7); const _256n = BigInt(256); // FIPS 202 Algorithm 5 rc(): when the outgoing bit is 1, the 8-bit LFSR xors // taps 0, 4, 5, and 6, which compresses to the feedback mask `0x71`. const _0x71n = BigInt(0x71); const SHA3_PI = []; const SHA3_ROTL = []; const _SHA3_IOTA = []; // no pure annotation: var is always used for(let round = 0, R = _1n, x = 1, y = 0; round < 24; round++){ // Pi [x, y] = [ y, (2 * x + 3 * y) % 5 ]; SHA3_PI.push(2 * (5 * y + x)); // Rotational SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64); // Iota let t = _0n; for(let j = 0; j < 7; j++){ R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n; if (R & _2n) t ^= _1n << (_1n << BigInt(j)) - _1n; } _SHA3_IOTA.push(t); } const IOTAS = split(_SHA3_IOTA, true); // `split(..., true)` keeps the local little-endian lane-word layout used by // `state32`, so these `H` / `L` tables follow the file's first-word / // second-word lane slots rather than `_u64.ts`'s usual high/low naming. const SHA3_IOTA_H = IOTAS[0]; const SHA3_IOTA_L = IOTAS[1]; // Left rotation (without 0, 32, 64) const rotlH = (h, l, s)=>s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s); const rotlL = (h, l, s)=>s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s); /** * `keccakf1600` internal permutation, additionally allows adjusting the round count. * @param s - 5x5 Keccak state encoded as 25 lanes split into 50 uint32 words * in this file's local little-endian lane-word order * @param rounds - number of rounds to execute * @throws If `rounds` is outside the supported `1..24` range. {@link Error} * @example * Permute a Keccak state with the default 24 rounds. * ```ts * keccakP(new Uint32Array(50)); * ``` */ function keccakP(s, rounds = 24) { anumber(rounds, 'rounds'); // This implementation precomputes only the standard Keccak-f[1600] 24-round Iota table. if (rounds < 1 || rounds > 24) throw new Error('"rounds" expected integer 1..24'); const B = new Uint32Array(5 * 2); // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js) for(let round = 24 - rounds; round < 24; round++){ // Theta θ for(let x = 0; x < 10; x++)B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; for(let x = 0; x < 10; x += 2){ const idx1 = (x + 8) % 10; const idx0 = (x + 2) % 10; const B0 = B[idx0]; const B1 = B[idx0 + 1]; const Th = rotlH(B0, B1, 1) ^ B[idx1]; const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1]; for(let y = 0; y < 50; y += 10){ s[x + y] ^= Th; s[x + y + 1] ^= Tl; } } // Rho (ρ) and Pi (π) let curH = s[2]; let curL = s[3]; for(let t = 0; t < 24; t++){ const shift = SHA3_ROTL[t]; const Th = rotlH(curH, curL, shift); const Tl = rotlL(curH, curL, shift); const PI = SHA3_PI[t]; curH = s[PI]; curL = s[PI + 1]; s[PI] = Th; s[PI + 1] = Tl; } // Chi (χ) // Same as: // for (let x = 0; x < 10; x++) B[x] = s[y + x]; // for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; for(let y = 0; y < 50; y += 10){ const b0 = s[y], b1 = s[y + 1], b2 = s[y + 2], b3 = s[y + 3]; s[y] ^= ~s[y + 2] & s[y + 4]; s[y + 1] ^= ~s[y + 3] & s[y + 5]; s[y + 2] ^= ~s[y + 4] & s[y + 6]; s[y + 3] ^= ~s[y + 5] & s[y + 7]; s[y + 4] ^= ~s[y + 6] & s[y + 8]; s[y + 5] ^= ~s[y + 7] & s[y + 9]; s[y + 6] ^= ~s[y + 8] & b0; s[y + 7] ^= ~s[y + 9] & b1; s[y + 8] ^= ~b0 & b2; s[y + 9] ^= ~b1 & b3; } // Iota (ι) s[0] ^= SHA3_IOTA_H[round]; s[1] ^= SHA3_IOTA_L[round]; } clean(B); } /** * Keccak sponge function. * @param blockLen - absorb/squeeze rate in bytes * @param suffix - domain separation suffix byte * @param outputLen - default digest length in bytes. This base sponge only * requires a non-negative integer; wrappers that need positive output * lengths must enforce that themselves. * @param enableXOF - whether XOF output is allowed * @param rounds - number of Keccak-f rounds * @example * Build a sponge state, absorb bytes, then finalize a digest. * ```ts * const hash = new Keccak(136, 0x06, 32); * hash.update(new Uint8Array([1, 2, 3])); * hash.digest(); * ``` */ class Keccak { clone() { return this._cloneInto(); } keccak() { swap32IfBE(this.state32); keccakP(this.state32, this.rounds); swap32IfBE(this.state32); this.posOut = 0; this.pos = 0; } update(data) { aexists(this); abytes(data); const { blockLen, state } = this; const len = data.length; for(let pos = 0; pos < len;){ const take = Math.min(blockLen - this.pos, len - pos); for(let i = 0; i < take; i++)state[this.pos++] ^= data[pos++]; if (this.pos === blockLen) this.keccak(); } return this; } finish() { if (this.finished) return;