chore: remove leftover stray vendor files from earlier fetch attempt

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
2026-10-11 00:01:33 +02:00
co-authored by Codebuff
parent 2ff58441f4
commit 7cbc2eecd2
5 changed files with 0 additions and 3066 deletions
-896
View File
@@ -1,896 +0,0 @@
/**
* Utilities for hex, bytes, CSPRNG.
* @module
*/
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
export function aarray(item, title, inner = () => { }) {
if (!Array.isArray(item))
throw new TypeError(`"${title}" expected array, got type=${typeof item}`);
for (let i = 0; i < item.length; i++)
inner(item[i], `${title}[${i}]`);
return item;
}
/**
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
* @param a - Value to inspect.
* @returns `true` when the value is a Uint8Array view, including Node's `Buffer`.
* @example
* Guards a value before treating it as raw key material.
*
* ```ts
* isBytes(new Uint8Array());
* ```
*/
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));
}
// Shared error-message prefix builder. Only called on throw paths, so assert
// success paths never pay for the string concatenation.
const atitle = (title) => (title ? `"${title}" ` : '');
/**
* Asserts something is boolean.
* @param value - Value to validate.
* @returns The validated boolean.
* @throws On wrong argument types. {@link TypeError}
* @example
* Validates a boolean option before branching on it.
*
* ```ts
* abool(true);
* ```
*/
export function abool(value, title = '') {
if (typeof value !== 'boolean')
throw new TypeError(atitle(title) + 'expected boolean, got type=' + typeof value);
return value;
}
/**
* Asserts something is a non-negative safe integer.
* @param n - Value to validate.
* @returns The validated number.
* @throws On wrong argument types. {@link TypeError}
* @throws On wrong argument ranges or values. {@link RangeError}
* @example
* Validates a non-negative length or counter.
*
* ```ts
* anumber(1);
* ```
*/
export function anumber(n, title = '') {
if (typeof n !== 'number')
throw new TypeError(atitle(title) + 'expected number, got ' + typeof n);
if (!Number.isSafeInteger(n) || n < 0)
throw new RangeError(atitle(title) + 'expected integer >= 0, got ' + n);
return n;
}
/**
* Asserts something is Uint8Array.
* @param value - Value to validate.
* @param length - Expected byte length.
* @param title - Optional label used in error messages.
* @returns The validated byte array.
* On Node, `Buffer` is accepted too because it is a Uint8Array view.
* @throws On wrong argument types. {@link TypeError}
* @throws On wrong argument lengths. {@link RangeError}
* @example
* Validates a fixed-length nonce or key buffer.
*
* ```ts
* abytes(new Uint8Array([1, 2]), 2);
* ```
*/
export function abytes(value, length, title = '') {
// Success path first: this runs at the start of every update() / digestInto(), and the
// common `abytes(data)` form must not pay for length handling it does not use.
if (isBytes(value) && (length === undefined || value.length === length))
return value;
// Error path: recompute freely to build the exact message.
if (length !== undefined)
anumber(length, 'length');
const bytes = isBytes(value);
const ofLen = length !== undefined ? ` of length ${length}` : '';
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
const message = atitle(title) + 'expected Uint8Array' + ofLen + ', got ' + got;
if (!bytes)
throw new TypeError(message);
throw new RangeError(message);
}
const aobject = (value, label) => {
if (value === null || typeof value !== 'object' || Array.isArray(value))
throw new TypeError(label === 'object'
? 'expected valid options object'
: `"${label}" expected object, got type=${typeof value}`);
};
/**
* Asserts a hash- or MAC-like instance has not been destroyed or finished.
* @param instance - Stateful instance to validate.
* @param checkFinished - Whether to reject finished instances.
* When `false`, only `destroyed` is checked.
* @throws If the hash instance has already been destroyed or finalized. {@link Error}
* @example
* Guards against calling `update()` or `digest()` on a finished hash.
*
* ```ts
* aexists({ destroyed: false, finished: false });
* ```
*/
export function aexists(instance, checkFinished = true) {
// Runs on every update()/digestInto(); the flags are library-owned booleans, so only their
// truthiness is checked - re-validating their type per call was pure hot-path overhead.
if (instance.destroyed)
throw new Error('hash was destroyed');
if (checkFinished && instance.finished)
throw new Error('digest() was already called');
}
/**
* Asserts output is a sufficiently-sized byte array.
* @param out - Output buffer to validate.
* @param instance - Hash-like instance providing `outputLen`.
* This is the relaxed `digestInto()`-style contract: output must be at least `outputLen`,
* unlike one-shot cipher helpers elsewhere in the repo that often require exact lengths.
* @throws On wrong argument types. {@link TypeError}
* @throws On wrong output buffer lengths. {@link RangeError}
* @example
* Verifies that a caller-provided output buffer is large enough.
*
* ```ts
* aoutput(new Uint8Array(16), { outputLen: 16 });
* ```
*/
export function aoutput(out, instance) {
abytes(out, undefined, 'output');
// `outputLen` is a library-owned readonly number; the negated comparison keeps failing fast
// when it is missing/NaN (comparisons with undefined/NaN are false) without an anumber() call.
const min = instance.outputLen;
if (!(out.length >= min)) {
throw new RangeError('"output" expected length >= ' + min);
}
}
/**
* Asserts output is a sufficiently-sized, 4-byte-aligned byte array.
* {@link aoutput} plus an {@link isAligned32} check, for `digestInto()` paths
* that write through zero-allocation `u32` word views.
* @param out - Output buffer to validate.
* @param instance - Hash-like instance providing `outputLen`.
* @throws On wrong argument types. {@link TypeError}
* @throws On wrong output buffer lengths. {@link RangeError}
* @throws On wrong output buffer alignment. {@link Error}
* @example
* Verifies that a caller-provided output buffer is large enough and aligned.
*
* ```ts
* aoutput32(new Uint8Array(16), { outputLen: 16 });
* ```
*/
export function aoutput32(out, instance) {
aoutput(out, instance);
if (!isAligned32(out))
throw new Error('invalid output, must be aligned');
}
/**
* Casts a typed-array view to Uint8Array.
* @param arr - Typed-array view to reinterpret.
* @returns Uint8Array view over the same bytes.
* @example
* Views 32-bit words as raw bytes without copying.
*
* ```ts
* u8(new Uint32Array([1]));
* ```
*/
export function u8(arr) {
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
}
/**
* Casts a typed-array view to Uint32Array.
* @param arr - Typed-array view to reinterpret.
* @returns Uint32Array view over the same bytes. Callers are expected to provide a
* 4-byte-aligned offset; trailing `1..3` bytes are silently dropped.
* @example
* Views a byte buffer as 32-bit words for block processing.
*
* ```ts
* u32(new Uint8Array(4));
* ```
*/
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 wipe.
* @example
* Wipes a temporary key buffer after use.
*
* ```ts
* const bytes = new Uint8Array([1]);
* clean(bytes);
* ```
*/
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 - Typed-array view to wrap.
* @returns DataView over the same bytes.
* @example
* Creates an endian-aware view for length encoding.
*
* ```ts
* createView(new Uint8Array(4));
* ```
*/
export function createView(arr) {
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
}
/**
* Whether the current platform is little-endian.
* Most are; some IBM systems are not.
*/
export const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
/**
* Reverses byte order of one 32-bit word.
* @param word - Unsigned 32-bit word to swap.
* @returns The same word with bytes reversed.
* @example
* Swaps a big-endian word into little-endian byte order.
*
* ```ts
* byteSwap(0x11223344);
* ```
*/
export function byteSwap(word) {
return (((word << 24) & 0xff000000) |
((word << 8) & 0xff0000) |
((word >>> 8) & 0xff00) |
((word >>> 24) & 0xff));
}
/**
* Normalizes one 32-bit word to the little-endian representation expected by cipher cores.
* @param n - Unsigned 32-bit word to normalize.
* @returns Little-endian normalized word on big-endian hosts, else the input word unchanged.
* @example
* Normalizes a host-endian word before passing it into an ARX/AES core.
*
* ```ts
* swap8IfBE(0x11223344);
* ```
*/
export const swap8IfBE = isLE
? (n) => n
: (n) => byteSwap(n) >>> 0;
/**
* Byte-swaps every word of a Uint32Array in place.
* @param arr - Uint32Array whose words should be swapped.
* @returns The same array after in-place byte swapping.
* @example
* Swaps every 32-bit word in a word-view buffer.
*
* ```ts
* byteSwap32(new Uint32Array([0x11223344]));
* ```
*/
export function byteSwap32(arr) {
for (let i = 0; i < arr.length; i++) {
arr[i] = byteSwap(arr[i]);
}
return arr;
}
/**
* Normalizes a Uint32Array view to the little-endian representation expected by cipher cores.
* @param u - Word view to normalize in place.
* @returns Little-endian normalized word view.
* @example
* Normalizes a word-view buffer before block processing.
*
* ```ts
* swap32IfBE(new Uint32Array([0x11223344]));
* ```
*/
export const swap32IfBE = isLE
? (u) => u
: byteSwap32;
// Built-in hex conversion:
// {@link https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex | caniuse entry}
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 built-in function, when available.
* @param bytes - Bytes to encode.
* @returns Lowercase hexadecimal string.
* @throws On wrong argument types. {@link TypeError}
* @example
* Formats ciphertext bytes for logs or test vectors.
*
* ```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;
}
// Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.
// ASCII codes: '0'..'9' = 48..57, 'A'..'F' = 65..70, 'a'..'f' = 97..102.
// prettier-ignore
function asciiToBase16(ch) {
return ch >= 48 && ch <= 57 ? ch - 48 // '2' => 50-48
: ch >= 65 && ch <= 70 ? ch - (65 - 10) // 'B' => 66-(65-10)
: ch >= 97 && ch <= 102 ? ch - (97 - 10) // 'b' => 98-(97-10)
: undefined;
}
/**
* 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)); // parse first char, multiply it by 16
const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); // parse second char
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; // example: 'A9' => 10*16 + 9
}
return array;
}
const _0n = /* @__PURE__ */ BigInt(0);
// Used in ff1, via bytesToNumberBE
/**
* Converts a big-endian hex string into bigint.
* @param hex - Hexadecimal string without `0x`.
* @returns Parsed bigint value. The empty string is treated as `0n`.
* @throws On wrong argument types. {@link TypeError}
* @example
* Parses a big-endian field element or counter from hex.
*
* ```ts
* hexToNumber('ff');
* ```
*/
export function hexToNumber(hex) {
if (typeof hex !== 'string')
throw new TypeError('hex string expected, got ' + typeof hex);
// Numeric parser, not byte-hex decoder: odd-length forms like 'f' are valid,
// and malformed syntax follows BigInt's native error behavior.
return hex === '' ? _0n : BigInt('0x' + hex); // Big Endian
}
// Used in ff1
// BE: Big Endian, LE: Little Endian
/**
* Converts big-endian bytes into bigint.
* @param bytes - Big-endian bytes.
* @returns Parsed bigint value. Empty input is treated as `0n`.
* @throws On invalid byte input passed to the internal hex conversion. {@link TypeError}
* @example
* Reads a big-endian integer from serialized bytes.
*
* ```ts
* bytesToNumberBE(new Uint8Array([1, 0]));
* ```
*/
export function bytesToNumberBE(bytes) {
return hexToNumber(bytesToHex(bytes));
}
/**
* Validates that a value is a non-negative bigint or safe integer.
* @param n - Value to validate.
* @returns The same validated value.
* @throws On wrong argument ranges or values. {@link RangeError}
*/
function abignumber(n) {
if (typeof n === 'bigint') {
if (!(_0n <= n))
throw new RangeError('positive bigint expected, got ' + n);
}
else
anumber(n);
return n;
}
// Used in ff1
/**
* Converts a number into big-endian bytes of fixed length.
* @param n - Number to encode.
* @param len - Output length in bytes. Must be greater than zero.
* @returns Big-endian bytes padded to `len`.
* Negative values, `len = 0`, and values that do not fit are rejected before
* downstream hex parsing.
* @throws On wrong argument ranges or values. {@link RangeError}
* @throws If a documented runtime validation or state check fails. {@link Error}
* @example
* Encodes a counter as fixed-width big-endian bytes.
*
* ```ts
* numberToBytesBE(1, 2);
* ```
*/
export function numberToBytesBE(n, len) {
anumber(len);
if (len === 0)
throw new Error('zero output length is invalid');
n = abignumber(n);
const expectedLen = len * 2;
const hex = n.toString(16);
// Detect overflow before hex parsing so oversized values don't leak the shared odd-hex error.
if (hex.length > expectedLen)
throw new RangeError('number is too large');
return hexToBytes(hex.padStart(expectedLen, '0'));
}
/**
* Converts string to bytes using UTF8 encoding.
* @param str - String to encode.
* @returns UTF-8 bytes in a detached fresh Uint8Array copy.
* @throws On wrong argument types. {@link TypeError}
* @example
* Encodes application text before encryption or MACing.
*
* ```ts
* utf8ToBytes('abc'); // new Uint8Array([97, 98, 99])
* ```
*/
export function utf8ToBytes(str) {
if (typeof str !== 'string')
throw new TypeError('string expected');
return new Uint8Array(new TextEncoder().encode(str)); // {@link https://bugzil.la/1681809 | Firefox bug 1681809}
}
/**
* Converts bytes to string using UTF8 encoding.
* @param bytes - UTF-8 bytes.
* @returns Decoded string. Input validation is delegated to `TextDecoder`, and malformed
* UTF-8 is replacement-decoded instead of rejected.
* @example
* Decodes UTF-8 plaintext back into a string.
*
* ```ts
* bytesToUtf8(new Uint8Array([97, 98, 99])); // 'abc'
* ```
*/
export function bytesToUtf8(bytes) {
return new TextDecoder().decode(bytes);
}
/**
* Checks if two U8A use same underlying buffer and overlaps.
* This is invalid and can corrupt data.
* @param a - First byte view.
* @param b - Second byte view.
* @returns `true` when the views overlap in memory.
* @example
* Detects whether two slices alias the same backing buffer.
*
* ```ts
* overlapBytes(new Uint8Array(4), new Uint8Array(4));
* ```
*/
export function overlapBytes(a, b) {
// Zero-length views cannot overwrite anything, even if their offset sits inside another range.
if (!a.byteLength || !b.byteLength)
return false;
return (a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy
a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end
b.byteOffset < a.byteOffset + a.byteLength // b starts before a end
);
}
/**
* If input and output overlap and input starts before output, we will overwrite end of input before
* we start processing it, so this is not supported by forward-processing ciphers.
* @param input - Input bytes.
* @param output - Output bytes.
* @throws If the output view would overwrite unread input bytes. {@link Error}
* @example
* Rejects an in-place layout that would overwrite unread input bytes.
*
* ```ts
* const buffer = new Uint8Array(8);
* complexOverlapBytes(buffer.subarray(0, 4), buffer.subarray(2, 6));
* ```
*/
export function complexOverlapBytes(input, output) {
// This is very cursed. It works somehow, but I'm completely unsure,
// reasoning about overlapping aligned windows is very hard.
if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)
throw new Error('complex overlap of input and output is not supported');
}
/**
* Copies several Uint8Arrays into one.
* @param arrays - Byte arrays to concatenate.
* @returns Combined byte array.
* @throws On wrong argument types inside the byte-array list. {@link TypeError}
* @example
* Builds a `nonce || ciphertext` style buffer.
*
* ```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 user options into defaults.
* @param defaults - Default option values.
* @param opts - User-provided overrides.
* @returns Combined options object.
* `defaults` is a library-owned mutable object; user-provided `opts` only need to be
* object-shaped, since "plain object" checks reject valid proxy/cross-realm containers.
* The merge mutates `defaults` in place and returns the same object, so direct callers
* should pass a fresh defaults object unless they intentionally want shared state updated.
* @throws If options are missing or not an object. {@link Error}
* @example
* Applies user overrides to the default cipher options.
*
* ```ts
* checkOpts({ rounds: 20 }, { rounds: 8 });
* ```
*/
export function checkOpts(defaults, opts) {
aobject(defaults, 'defaults');
aobject(opts, 'opts');
// Mutates defaults by design. __proto__ follows Object.assign semantics here and can only
// affect this local option object; callers already control this low-level options surface.
const merged = Object.assign(defaults, opts);
return merged;
}
/**
* Compares two byte arrays in kinda constant time once lengths already match.
* @param a - First byte array.
* @param b - Second byte array.
* @returns `true` when the arrays contain the same bytes. Different lengths still return early.
* @example
* Compares an expected authentication tag with the received one.
*
* ```ts
* equalBytes(new Uint8Array([1]), new Uint8Array([1]));
* ```
*/
export function equalBytes(a, b) {
a = abytes(a);
b = abytes(b);
if (a.length !== b.length)
return false;
let diff = 0;
for (let i = 0; i < a.length; i++)
diff |= a[i] ^ b[i];
return diff === 0;
}
/**
* Wraps a keyed MAC constructor into a one-shot helper with `.create()`.
* @param keyLen - Valid probe-key length used to read static metadata once.
* The probe key is only used for `outputLen` / `blockLen`, so callers with several valid key sizes
* can pass any representative size as long as those values stay fixed.
* @param macCons - Keyed MAC constructor or factory.
* @param fromMsg - Optional adapter that derives extra constructor args from the one-shot message.
* @returns Callable MAC helper with `.create()`.
*/
export function wrapMacConstructor(keyLen, macCons, fromMsg) {
const mac = macCons;
const getArgs = (fromMsg || (() => []));
const macC = (msg, key) => mac(key, ...getArgs(msg))
.update(msg)
.digest();
const tmp = mac(new Uint8Array(keyLen), ...getArgs(new Uint8Array(0)));
macC.outputLen = tmp.outputLen;
macC.blockLen = tmp.blockLen;
macC.create = (key, ...args) => mac(key, ...args);
return macC;
}
/**
* Wraps a cipher: validates args, ensures encrypt() can only be called once.
* Used internally by the exported cipher constructors.
* Output-buffer support is inferred from the wrapped `encrypt` / `decrypt`
* arity (`fn.length === 2`), so wrapped output-capable methods must use a normal
* second parameter, not a default/rest parameter. AAD support is explicit in
* `params.withAAD`; optional AAD starts after the nonce slot when one is present.
* @__NO_SIDE_EFFECTS__
* @param params - Static cipher metadata. See {@link CipherParams}.
* @param constructor - Cipher constructor.
* @returns Wrapped constructor with validation.
*/
export const wrapCipher = (params, constructor) => {
function wrappedCipher(key, ...args) {
// Validate key
abytes(key, undefined, 'key');
// Validate nonce if nonceLength is present
if (params.nonceLength !== undefined) {
const nonce = args[0];
abytes(nonce, params.varSizeNonce ? undefined : params.nonceLength, 'nonce');
}
// Keep tag length available for decrypt-size checks after constructor validation.
const tagl = params.tagLength;
const aadStart = params.nonceLength !== undefined ? 1 : 0;
// No-AAD constructors otherwise silently ignore byte args meant as AAD.
if (!params.withAAD) {
for (let i = aadStart; i < args.length; i++)
if (isBytes(args[i]))
throw new Error('AAD not supported');
}
// Validate the first AAD slot early; rest-arg AAD constructors validate the tail themselves.
if (params.withAAD && args[aadStart] !== undefined)
abytes(args[aadStart], undefined, 'AAD');
const cipher = constructor(key, ...args);
const checkOutput = (fnLength, output) => {
if (output !== undefined) {
if (fnLength !== 2)
throw new Error('cipher output not supported');
abytes(output, undefined, 'output');
}
};
// Create wrapped cipher with validation and single-use encryption
let called = false;
const wrCipher = {
encrypt(data, output) {
if (called)
throw new Error('cannot encrypt() twice with same key + nonce');
// Any encrypt attempt consumes the instance, even if validation rejects below.
called = true;
abytes(data, undefined, 'data');
checkOutput(cipher.encrypt.length, output);
return cipher.encrypt(data, output);
},
decrypt(data, output) {
abytes(data, undefined, 'data');
if (tagl && data.length < tagl)
throw new Error('"ciphertext" expected length >= tagLength=' + tagl);
checkOutput(cipher.decrypt.length, output);
return cipher.decrypt(data, output);
},
};
return wrCipher;
}
Object.assign(wrappedCipher, params);
return wrappedCipher;
};
/**
* By default, returns u8a of length.
* When out is available, it checks it for validity and uses it.
* @param expectedLength - Required output length.
* @param out - Optional destination buffer.
* @param onlyAligned - Whether `out` must be 4-byte aligned.
* @returns Output buffer ready for writing.
* @throws On wrong argument types. {@link TypeError}
* @throws If the provided output buffer has the wrong size. {@link RangeError}
* @throws If the provided output buffer has the wrong alignment. {@link Error}
* @example
* Reuses a caller-provided output buffer when lengths match.
*
* ```ts
* getOutput(16, new Uint8Array(16));
* ```
*/
export function getOutput(expectedLength, out, onlyAligned = true) {
if (out === undefined)
return new Uint8Array(expectedLength);
// Keep Buffer/cross-realm Uint8Array support here instead of trusting a shape-compatible object.
abytes(out, expectedLength, 'output');
if (onlyAligned && !isAligned32(out))
throw new Error('invalid output, must be aligned');
return out;
}
/**
* Encodes data and AAD lengths into a 16-byte buffer.
* @param dataLength - Data length. Units are caller-defined: GCM passes bit
* lengths, ChaCha20-Poly1305 passes byte lengths — the helper writes the raw values.
* @param aadLength - AAD length, same unit convention as `dataLength`.
* The serialized block is still `aadLength || dataLength`, matching GCM/Poly1305
* conventions even though the helper parameter order is `(dataLength, aadLength)`.
* @param isLE - Whether to encode lengths as little-endian.
* @returns 16-byte length block.
* @throws On wrong argument types passed to the endian validator. {@link TypeError}
* @throws On wrong argument ranges or values. {@link RangeError}
* @example
* Builds the length block appended by GCM and Poly1305.
*
* ```ts
* u64Lengths(16, 8, true);
* ```
*/
export function u64Lengths(dataLength, aadLength, isLE) {
// Reject coercible non-number lengths like '10' and true before BigInt(...) accepts them.
anumber(dataLength);
anumber(aadLength);
abool(isLE);
const num = new Uint8Array(16);
const view = createView(num);
view.setBigUint64(0, BigInt(aadLength), isLE);
view.setBigUint64(8, BigInt(dataLength), isLE);
return num;
}
/**
* Checks whether a byte array is aligned to a 4-byte offset.
* @param bytes - Byte array to inspect.
* @returns `true` when the view is 4-byte aligned.
* @example
* Checks whether a buffer can be safely viewed as Uint32Array.
*
* ```ts
* isAligned32(new Uint8Array(4));
* ```
*/
export function isAligned32(bytes) {
return bytes.byteOffset % 4 === 0;
}
/**
* Copies bytes into a new Uint8Array.
* @param bytes - Bytes to copy.
* @returns Copied byte array.
* @throws On wrong argument types. {@link TypeError}
* @example
* Copies input into an aligned Uint8Array before block processing.
*
* ```ts
* copyBytes(new Uint8Array([1, 2]));
* ```
*/
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));
}
/**
* 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));
}
/**
* Uses CSPRNG for nonce, nonce injected in ciphertext.
* For `encrypt`, a `nonceBytes`-length buffer is fetched from CSPRNG and
* prepended to encrypted ciphertext. For `decrypt`, first `nonceBytes` of ciphertext
* are treated as nonce. The wrapper always allocates a fresh `nonce || ciphertext`
* buffer on encrypt and intentionally does not support caller-provided destination buffers.
* Too-short decrypt inputs are split into short/empty nonce views and then delegated
* to the wrapped cipher instead of being rejected here first.
*
* NOTE: Under the same key, using random nonces (e.g. `managedNonce`) with AES-GCM and ChaCha
* should be limited to `2**23` (8M) messages to get a collision chance of
* `2**-50`. Stretching to `2**32` (4B) messages would raise that chance to
* `2**-33`, still negligible but creeping up.
* @param fn - Cipher constructor that expects a nonce.
* @param randomBytes_ - Random-byte source used for nonce generation.
* @returns Cipher constructor that prepends the nonce to ciphertext.
* @throws On wrong argument types. {@link TypeError}
* @throws On invalid nonce lengths observed at wrapper construction or use. {@link RangeError}
* @example
* Prepends a fresh random nonce to every ciphertext.
*
* ```ts
* import { gcm } from '@noble/ciphers/aes.js';
* import { managedNonce, randomBytes } from '@noble/ciphers/utils.js';
* const wrapped = managedNonce(gcm);
* const key = randomBytes(16);
* const ciphertext = wrapped(key).encrypt(new Uint8Array([1, 2, 3]));
* wrapped(key).decrypt(ciphertext);
* ```
*/
export function managedNonce(fn, randomBytes_ = randomBytes) {
if (typeof fn !== 'function')
throw new TypeError('"fn" expected cipher constructor, got type=' + typeof fn);
if (typeof randomBytes_ !== 'function')
throw new TypeError('"randomBytes_" expected function, got type=' + typeof randomBytes_);
const { nonceLength } = fn;
anumber(nonceLength, 'fn.nonceLength');
const addNonce = (nonce, ciphertext, plaintext) => {
const out = concatBytes(nonce, ciphertext);
// Wrapped ciphers may alias caller plaintext on encrypt(); never zero
// caller-owned buffers here.
if (!overlapBytes(plaintext, ciphertext))
ciphertext.fill(0);
return out;
};
// NOTE: we cannot support DST here, it would be mistake:
// - we don't know how much dst length cipher requires
// - nonce may unalign dst and break everything
// - we create new u8a anyway (concatBytes)
// - previously we passed all args to cipher, but that was mistake!
const res = ((key, ...args) => ({
encrypt(plaintext) {
abytes(plaintext, undefined, 'data');
const nonce = randomBytes_(nonceLength);
const encrypted = fn(key, nonce, ...args).encrypt(plaintext);
// @ts-ignore
if (encrypted instanceof Promise)
return encrypted.then((ct) => addNonce(nonce, ct, plaintext));
return addNonce(nonce, encrypted, plaintext);
},
decrypt(ciphertext) {
abytes(ciphertext, undefined, 'data');
const nonce = ciphertext.subarray(0, nonceLength);
const decrypted = ciphertext.subarray(nonceLength);
return fn(key, nonce, ...args).decrypt(decrypted);
},
}));
// Auto-nonce wrappers still preserve the wrapped payload geometry.
if ('blockSize' in fn)
res.blockSize = fn.blockSize;
if ('tagLength' in fn)
res.tagLength = fn.tagLength;
if ('withAAD' in fn)
res.withAAD = fn.withAAD;
return res;
}
-460
View File
@@ -1,460 +0,0 @@
/**
* ChaCha stream cipher, released
* in 2008. Developed after Salsa20, ChaCha aims to increase diffusion per round.
* It was standardized in
* {@link https://www.rfc-editor.org/rfc/rfc8439 | RFC 8439} and
* is now used in TLS 1.3.
*
* {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | XChaCha20}
* extended-nonce variant is also provided. Similar to XSalsa, it's safe to use with
* randomly-generated nonces.
*
* Check out
* {@link http://cr.yp.to/chacha/chacha-20080128.pdf | PDF},
* {@link https://en.wikipedia.org/wiki/Salsa20 | wiki}, and
* {@link https://cr.yp.to/chacha.html | website}.
*
* @module
*/
import { createCipher, createPRG, rotl } from "./_arx.js";
import { poly1305 } from "./_poly1305.js";
import { abytes, clean, equalBytes, getOutput, isLE, swap32IfBE, u64Lengths, wrapCipher, } from "./utils.js";
/**
* ChaCha core function. Uses an unrolled loop (chachaCore, hchacha) - 4x
* faster than a simple loop, but larger & harder to read. A simple-loop
* reference version lives in `test/misc/micro-ciphers.ts`;
* `test/arx.test.ts` keeps the two aligned.
* The specific implementation is selected in `createCipher` below.
*/
/** RFC 8439 §2.3 block core for `state = constants | key | counter | nonce`. */
// prettier-ignore
function chachaCore(s, k, n, out, cnt, rounds = 20) {
let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // "expa" "nd 3" "2-by" "te k"
y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key
y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key
y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Nonce Nonce Nonce
// Save state to temporary variables
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
for (let r = 0; r < rounds; r += 2) {
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 16);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 12);
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 8);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 7);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 16);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 12);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 8);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 7);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 16);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 12);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 8);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 7);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 16);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 12);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 8);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 7);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 16);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 12);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 8);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 7);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 16);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 12);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 8);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 7);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 16);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 12);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 8);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 7);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 16);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 12);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 8);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 7);
}
// RFC 8439 §2.3 / §2.3.1: add the original state words back in state order.
let oi = 0;
out[oi++] = (y00 + x00) | 0;
out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0;
out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0;
out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0;
out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0;
out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0;
out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0;
out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0;
out[oi++] = (y15 + x15) | 0;
}
/**
* hchacha hashes key and nonce into key' and nonce' for xchacha20.
* Algorithmically identical to `hchacha_small` from `test/misc/micro-ciphers.ts`,
* but this exported path normalizes word order on big-endian hosts.
* Reuses `chachaCore` and subtracts its feed-forward, keeping the hot per-block
* path untouched.
* @param s - Sigma constants as 32-bit words.
* @param k - Key words.
* @param i - Nonce-prefix words.
* @param out - Output buffer for the derived subkey.
* @example
* Derives the XChaCha subkey from sigma, key, and nonce-prefix words.
*
* ```ts
* const sigma = new Uint32Array(4);
* const key = new Uint32Array(8);
* const nonce = new Uint32Array(4);
* const out = new Uint32Array(8);
* hchacha(sigma, key, nonce, out);
* ```
*/
// prettier-ignore
export function hchacha(s, k, i, out) {
// Runs the shared chachaCore permutation, then subtracts the RFC 8439 feed-forward
// it applies, recovering the raw permutation words hchacha needs.
// LE hosts read the caller arrays in place (no copies); BE hosts get
// byte-swapped scratch copies, wiped before returning.
const s2 = isLE ? s : swap32IfBE(s.slice(0, 4));
const k2 = isLE ? k : swap32IfBE(k.slice(0, 8));
const i2 = isLE ? i : swap32IfBE(i.slice(0, 4));
const t = new Uint32Array(16);
chachaCore(s2, k2, i2.subarray(1), t, i2[0]);
let oi = 0;
out[oi++] = (t[0] - s2[0]) | 0;
out[oi++] = (t[1] - s2[1]) | 0;
out[oi++] = (t[2] - s2[2]) | 0;
out[oi++] = (t[3] - s2[3]) | 0;
out[oi++] = (t[12] - i2[0]) | 0;
out[oi++] = (t[13] - i2[1]) | 0;
out[oi++] = (t[14] - i2[2]) | 0;
out[oi++] = (t[15] - i2[3]) | 0;
swap32IfBE(out);
if (!isLE)
clean(s2, k2, i2);
clean(t);
}
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
* The nonce/counter layout still reserves 8 counter bytes internally, but the shared public
* `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`
* near `MAX_COUNTER` for the full counter-policy rationale.
* @param key - 16-byte or 32-byte key.
* @param nonce - 8-byte nonce.
* @param data - Input bytes to xor with the keystream.
* @param output - Optional destination buffer.
* @param counter - Initial block counter.
* @returns Encrypted or decrypted bytes.
* @example
* Encrypts bytes with the original 8-byte-nonce ChaCha variant and a fresh key/nonce.
*
* ```ts
* import { chacha20orig } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(8);
* chacha20orig(key, nonce, new Uint8Array(4));
* ```
*/
export const chacha20orig = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 8,
allowShortKeys: true,
});
/**
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
* With smaller nonce, it's not safe to make it random (CSPRNG), due to collision chance.
* @param key - 32-byte key.
* @param nonce - 12-byte nonce.
* @param data - Input bytes to xor with the keystream.
* @param output - Optional destination buffer.
* @param counter - Initial block counter.
* @returns Encrypted or decrypted bytes.
* @example
* Encrypts bytes with the RFC 8439 ChaCha20 stream cipher and a fresh key/nonce.
*
* ```ts
* import { chacha20 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(12);
* chacha20(key, nonce, new Uint8Array(4));
* ```
*/
export const chacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
allowShortKeys: false,
});
/**
* XChaCha eXtended-nonce ChaCha. With 24-byte nonce, it's safe to make it random (CSPRNG).
* See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.
* The nonce/counter layout still reserves 8 counter bytes internally, but the shared public
* `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`
* near `MAX_COUNTER` for the full counter-policy rationale.
* @param key - 32-byte key.
* @param nonce - 24-byte extended nonce.
* @param data - Input bytes to xor with the keystream.
* @param output - Optional destination buffer.
* @param counter - Initial block counter.
* @returns Encrypted or decrypted bytes.
* @example
* Encrypts bytes with XChaCha20 using a fresh key and random 24-byte nonce.
*
* ```ts
* import { xchacha20 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(24);
* xchacha20(key, nonce, new Uint8Array(4));
* ```
*/
export const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 8,
extendNonceFn: hchacha,
allowShortKeys: false,
});
/**
* Reduced 8-round chacha, described in original paper.
* @param key - 32-byte key.
* @param nonce - 12-byte nonce.
* @param data - Input bytes to xor with the keystream.
* @param output - Optional destination buffer.
* @param counter - Initial block counter.
* @returns Encrypted or decrypted bytes.
* @example
* Uses the reduced 8-round variant for non-critical workloads with a fresh key/nonce.
*
* ```ts
* import { chacha8 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(12);
* chacha8(key, nonce, new Uint8Array(4));
* ```
*/
export const chacha8 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 8,
});
/**
* Reduced 12-round chacha, described in original paper.
* @param key - 32-byte key.
* @param nonce - 12-byte nonce.
* @param data - Input bytes to xor with the keystream.
* @param output - Optional destination buffer.
* @param counter - Initial block counter.
* @returns Encrypted or decrypted bytes.
* @example
* Uses the reduced 12-round variant for non-critical workloads with a fresh key/nonce.
*
* ```ts
* import { chacha12 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(12);
* chacha12(key, nonce, new Uint8Array(4));
* ```
*/
export const chacha12 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 12,
});
// Test-only hook: exposes the unrolled production core so tests can compare it
// with the simple/reference core from `test/misc/micro-ciphers.ts`.
export const __TESTS = /* @__PURE__ */ Object.freeze({ chachaCore });
// RFC 8439 §2.8.1 pad16(x): shared zero block for AAD/ciphertext padding.
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
// RFC 8439 §2.8 / §2.8.1: aligned inputs add nothing, otherwise append 16-(len%16) zero bytes.
const updatePadded = (h, msg) => {
h.update(msg);
const leftover = msg.length % 16;
if (leftover)
h.update(ZEROS16.subarray(leftover));
};
// RFC 8439 §2.6.1 poly1305_key_gen returns `block[0..31]`, so AEAD key
// generation only needs 32 zero bytes.
const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
function computeTag(fn, key, nonce, ciphertext, AAD) {
if (AAD !== undefined)
abytes(AAD, undefined, 'AAD');
// RFC 8439 §2.6 / §2.8: derive the Poly1305 one-time key from counter 0,
// then MAC AAD || pad16(AAD) || ciphertext || pad16(ciphertext) || len(AAD) || len(ciphertext).
const authKey = fn(key, nonce, ZEROS32);
const lengths = u64Lengths(ciphertext.length, AAD ? AAD.length : 0, true);
// Methods below can be replaced with
// `return poly1305_computeTag_small(authKey, lengths, ciphertext, AAD)`
// from `test/misc/micro-ciphers.ts`.
const h = poly1305.create(authKey);
if (AAD)
updatePadded(h, AAD);
updatePadded(h, ciphertext);
h.update(lengths);
const res = h.digest();
clean(authKey, lengths);
return res;
}
/**
* AEAD algorithm from RFC 8439.
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
* We could have composed them, but it's hard because of authKey:
* In salsa20, authKey changes position in salsa stream.
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
*/
export const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
// This borrows caller key/nonce/AAD buffers by reference; mutating them after construction
// changes future encrypt/decrypt results.
const tagLength = 16;
return {
encrypt(plaintext, output) {
const plength = plaintext.length;
output = getOutput(plength + tagLength, output, false);
output.set(plaintext);
const oPlain = output.subarray(0, -tagLength);
// RFC 8439 §2.8: payload encryption starts at counter 1 because counter 0 produced the OTK.
xorStream(key, nonce, oPlain, oPlain, 1);
const tag = computeTag(xorStream, key, nonce, oPlain, AAD);
output.set(tag, plength); // append tag
clean(tag);
return output;
},
decrypt(ciphertext, output) {
output = getOutput(ciphertext.length - tagLength, output, false);
const data = ciphertext.subarray(0, -tagLength);
const passedTag = ciphertext.subarray(-tagLength);
const tag = computeTag(xorStream, key, nonce, data, AAD);
// RFC 8439 §2.8 / §4: authenticate ciphertext before decrypting it, and compare tags with
// the constant-time equalBytes() helper rather than decrypting speculative plaintext first.
if (!equalBytes(passedTag, tag)) {
clean(tag);
throw new Error('invalid tag');
}
output.set(ciphertext.subarray(0, -tagLength));
// Actual decryption
xorStream(key, nonce, output, output, 1); // start stream with i=1
clean(tag);
return output;
},
};
};
/**
* ChaCha20-Poly1305 from RFC 8439.
*
* Unsafe to use random nonces under the same key, due to collision chance.
* Prefer XChaCha instead.
* @param key - 32-byte key.
* @param nonce - 12-byte nonce.
* @param AAD - Additional authenticated data.
* @returns AEAD cipher instance.
* @example
* Encrypts and authenticates plaintext with a fresh key and nonce.
*
* ```ts
* import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(12);
* const aad = new TextEncoder().encode('session metadata');
* const cipher = chacha20poly1305(key, nonce, aad);
* cipher.encrypt(new Uint8Array([1, 2, 3]));
* ```
*/
export const chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16, withAAD: true },
/* @__PURE__ */ _poly1305_aead(chacha20));
/**
* XChaCha20-Poly1305 extended-nonce chacha.
*
* Can be safely used with random nonces (CSPRNG).
* See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.
* @param key - 32-byte key.
* @param nonce - 24-byte nonce.
* @param AAD - Additional authenticated data.
* @returns AEAD cipher instance.
* @example
* Encrypts and authenticates plaintext with a fresh key and random 24-byte nonce.
*
* ```ts
* import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const key = randomBytes(32);
* const nonce = randomBytes(24);
* const aad = new TextEncoder().encode('session metadata');
* const cipher = xchacha20poly1305(key, nonce, aad);
* cipher.encrypt(new Uint8Array([1, 2, 3]));
* ```
*/
export const xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16, withAAD: true },
/* @__PURE__ */ _poly1305_aead(xchacha20));
/**
* Chacha20 CSPRNG (cryptographically secure pseudorandom number generator).
* It's best to limit usage to non-production, non-critical cases: for example, test-only.
* Compatible with libtomcrypt. It does not have a specification, so unclear how secure it is.
* @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,
* only 32 random bytes are mixed into the 40-byte state.
* @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.
* @example
* Seeds the test-only ChaCha20 DRBG from fresh entropy.
*
* ```ts
* import { rngChacha20 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const seed = randomBytes(32);
* const prg = rngChacha20(seed);
* prg.randomBytes(8);
* ```
*/
export const rngChacha20 = /* @__PURE__ */ createPRG(chacha20orig, 64, 32, 8);
/**
* Chacha20/8 CSPRNG (cryptographically secure pseudorandom number generator).
* It's best to limit usage to non-production, non-critical cases: for example, test-only.
* Faster than `rngChacha20`.
* @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,
* only 32 random bytes are mixed into the 44-byte state.
* @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.
* @example
* Seeds the faster test-only ChaCha8 DRBG from fresh entropy.
*
* ```ts
* import { rngChacha8 } from '@noble/ciphers/chacha.js';
* import { randomBytes } from '@noble/ciphers/utils.js';
* const seed = randomBytes(32);
* const prg = rngChacha8(seed);
* prg.randomBytes(8);
* ```
*/
export const rngChacha8 = /* @__PURE__ */ createPRG(chacha8, 64, 32, 12);
-446
View File
@@ -1,446 +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
*/
import { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from "./_md.js";
import * as u64 from "./_u64.js";
import { clean, createHasher, oidNist, rotr } from "./utils.js";
/**
* 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 {
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
// Numeric initializers matter: starting the fields as `undefined` changes
// V8's field representation and makes sha256 3x slower (measured).
A = 0;
B = 0;
C = 0;
D = 0;
E = 0;
F = 0;
G = 0;
H = 0;
constructor(outputLen, IV) {
super(64, outputLen, 8, false);
this.A = IV[0] | 0;
this.B = IV[1] | 0;
this.C = IV[2] | 0;
this.D = IV[3] | 0;
this.E = IV[4] | 0;
this.F = IV[5] | 0;
this.G = IV[6] | 0;
this.H = IV[7] | 0;
}
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;
}
_cloneInto(to) {
(to ||= new this.constructor()).set(...this.get());
return this._cloneIntoMeta(to);
}
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. */
export class _SHA256 extends SHA2_32B {
constructor() {
super(32, SHA256_IV);
}
}
/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */
export class _SHA224 extends SHA2_32B {
constructor() {
super(28, SHA224_IV);
}
}
// 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__ */ (() => u64.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 {
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
// h -- high 32 bits, l -- low 32 bits
// Numeric initializers matter: starting the fields as `undefined` changes
// V8's field representation and slows hashing down (measured on sha256).
Ah = 0;
Al = 0;
Bh = 0;
Bl = 0;
Ch = 0;
Cl = 0;
Dh = 0;
Dl = 0;
Eh = 0;
El = 0;
Fh = 0;
Fl = 0;
Gh = 0;
Gl = 0;
Hh = 0;
Hl = 0;
constructor(outputLen, IV) {
super(128, outputLen, 16, false);
this.Ah = IV[0] | 0;
this.Al = IV[1] | 0;
this.Bh = IV[2] | 0;
this.Bl = IV[3] | 0;
this.Ch = IV[4] | 0;
this.Cl = IV[5] | 0;
this.Dh = IV[6] | 0;
this.Dl = IV[7] | 0;
this.Eh = IV[8] | 0;
this.El = IV[9] | 0;
this.Fh = IV[10] | 0;
this.Fl = IV[11] | 0;
this.Gh = IV[12] | 0;
this.Gl = IV[13] | 0;
this.Hh = IV[14] | 0;
this.Hl = IV[15] | 0;
}
// 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;
}
_cloneInto(to) {
(to ||= new this.constructor()).set(...this.get());
return this._cloneIntoMeta(to);
}
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 = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);
const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.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 = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);
const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);
// SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];
const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
const SUMh = u64.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 = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);
const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.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 = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
const T1h = u64.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 = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);
const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.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 } = u64.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 = u64.add3L(T1l, sigma0l, MAJl);
Ah = u64.add3H(All, T1h, sigma0h, MAJh);
Al = All | 0;
}
// Add the compressed chunk to the current hash value
({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
({ h: Hh, l: Hl } = u64.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);
}
}
/** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */
export class _SHA512 extends SHA2_64B {
constructor() {
super(64, SHA512_IV);
}
}
/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */
export class _SHA384 extends SHA2_64B {
constructor() {
super(48, SHA384_IV);
}
}
/**
* Truncated SHA512/256 and SHA512/224.
* SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as "intermediary" IV of SHA512/t.
* Then t hashes string to produce result IV.
* See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.
* These IV literals are checked against that script rather than a dedicated
* local RFC section.
*/
/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and
* stored as sixteen big-endian 32-bit halves. */
const T224_IV = /* @__PURE__ */ Uint32Array.from([
0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,
0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,
]);
/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and
* stored as sixteen big-endian 32-bit halves. */
const T256_IV = /* @__PURE__ */ Uint32Array.from([
0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,
0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,
]);
/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared
* RFC 6234 §6.4 compression engine. */
export class _SHA512_224 extends SHA2_64B {
constructor() {
super(28, T224_IV);
}
}
/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared
* RFC 6234 §6.4 compression engine. */
export class _SHA512_256 extends SHA2_64B {
constructor() {
super(32, T256_IV);
}
}
/**
* 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
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-256.
* ```ts
* sha256(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(),
/* @__PURE__ */ oidNist(0x01));
/**
* SHA2-224 hash function from RFC 4634.
* @param msg - message bytes to hash
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-224.
* ```ts
* sha224(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha224 = /* @__PURE__ */ createHasher(() => new _SHA224(),
/* @__PURE__ */ oidNist(0x04));
/**
* SHA2-512 hash function from RFC 4634.
* @param msg - message bytes to hash
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-512.
* ```ts
* sha512(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha512 = /* @__PURE__ */ createHasher(() => new _SHA512(),
/* @__PURE__ */ oidNist(0x03));
/**
* SHA2-384 hash function from RFC 4634.
* @param msg - message bytes to hash
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-384.
* ```ts
* sha384(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha384 = /* @__PURE__ */ createHasher(() => new _SHA384(),
/* @__PURE__ */ oidNist(0x02));
/**
* SHA2-512/256 "truncated" hash function, with improved resistance to length extension attacks.
* See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.
* @param msg - message bytes to hash
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-512/256.
* ```ts
* sha512_256(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha512_256 = /* @__PURE__ */ createHasher(() => new _SHA512_256(),
/* @__PURE__ */ oidNist(0x06));
/**
* SHA2-512/224 "truncated" hash function, with improved resistance to length extension attacks.
* See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.
* @param msg - message bytes to hash
* @param opts - Reserved hash options.
* @returns Digest bytes.
* @example
* Hash a message with SHA2-512/224.
* ```ts
* sha512_224(new Uint8Array([97, 98, 99]));
* ```
*/
export const sha512_224 = /* @__PURE__ */ createHasher(() => new _SHA512_224(),
/* @__PURE__ */ oidNist(0x05));
-687
View File
@@ -1,687 +0,0 @@
/**
* 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));
}
// Shared error-message prefix builder. Only called on throw paths, so assert
// success paths never pay for the string concatenation.
const atitle = (title) => (title ? `"${title}" ` : '');
/**
* Asserts something is a non-negative integer.
* @param n - number to validate
* @param title - label included in thrown errors
* @returns The validated number.
* @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')
throw new TypeError(atitle(title) + 'expected number, got ' + typeof n);
if (!Number.isSafeInteger(n) || n < 0)
throw new RangeError(atitle(title) + 'expected integer >= 0, got ' + n);
return n;
}
/**
* Asserts something is a boolean.
* @param value - value to validate
* @param title - label included in thrown errors
* @returns The validated boolean.
* @throws On wrong argument types. {@link TypeError}
* @example
* Validate a boolean option.
* ```ts
* abool(true, 'enableXOF');
* ```
*/
export function abool(value, title = '') {
if (typeof value !== 'boolean')
throw new TypeError(atitle(title) + 'expected boolean, got type=' + typeof value);
return value;
}
/**
* 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 = '') {
// Success path first: this runs at the start of every update() / digestInto(), and the
// common `abytes(data)` form must not pay for length handling it does not use.
if (isBytes(value) && (length === undefined || value.length === length))
return value;
// Error path: recompute freely to build the exact message.
if (length !== undefined)
anumber(length, 'length');
const bytes = isBytes(value);
const ofLen = length !== undefined ? ` of length ${length}` : '';
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
const message = atitle(title) + 'expected Uint8Array' + ofLen + ', got ' + got;
if (!bytes)
throw new TypeError(message);
throw new RangeError(message);
}
/**
* 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('expected hash 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 || h.blockLen < 1)
throw new Error('hash blockLen / outputLen must be >= 1');
}
const aobject = (value, label) => {
if (value === null || typeof value !== 'object' || Array.isArray(value))
throw new TypeError((label === 'object' ? '' : `"${label}" `) + 'expected object, got type=' + typeof value);
};
const aopts = (value, label) => {
aobject(value, label);
const proto = Object.getPrototypeOf(value);
if (proto !== Object.prototype && proto !== null)
throw new TypeError(`"${label}" expected plain object`);
// Object.assign() treats an own "__proto__" source key as a write to the target's legacy
// prototype setter. Reject it before merging so inherited option values cannot be injected.
if (Object.hasOwn(value, '__proto__'))
throw new TypeError(`"${label}.__proto__" is not allowed`);
};
/**
* 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) {
// Runs on every update()/digestInto(); the flags are library-owned booleans, so only their
// truthiness is checked - re-validating their type per call was pure hot-path overhead.
if (instance.destroyed)
throw new Error('hash was destroyed');
if (checkFinished && instance.finished)
throw new Error('digest() was already 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, 'output');
// `outputLen` is a library-owned readonly number; the negated comparison keeps failing fast
// when it is missing/NaN (comparisons with undefined/NaN are false) without an anumber() call.
const min = instance.outputLen;
if (!(out.length >= min)) {
throw new RangeError('"output" expected 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;
}
// Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.
// ASCII codes: '0'..'9' = 48..57, 'A'..'F' = 65..70, 'a'..'f' = 97..102.
// prettier-ignore
function asciiToBase16(ch) {
return ch >= 48 && ch <= 57 ? ch - 48 // '2' => 50-48
: ch >= 65 && ch <= 70 ? ch - (65 - 10) // 'B' => 66-(65-10)
: ch >= 97 && ch <= 102 ? ch - (97 - 10) // 'b' => 98-(97-10)
: undefined;
}
/**
* 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)); // parse first char, multiply it by 16
const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); // parse second char
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; // example: 'A9' => 10*16 + 9
}
return array;
}
/**
* Yields to the host task scheduler so timers, I/O, and rendering can make progress.
* Uses the Web Scheduling API when available or `setTimeout` as a cross-platform fallback.
* Host-task yields are much slower than microtasks (roughly 1ms with the timer fallback), so
* async loops should use `asyncTick >= 10` to amortize scheduling overhead to about 10% while
* still allowing other event-loop work to proceed.
* @param onReject - optional cleanup invoked only if the host yield fails
* @example
* Yield to the next scheduler tick.
* ```ts
* await nextTick();
* ```
*/
export function nextTick(onReject) {
const host = globalThis;
if (typeof host.scheduler?.yield === 'function') {
const promise = host.scheduler.yield();
// Keep the original scheduler rejection; this handler exists only for cleanup.
if (onReject)
promise.catch(onReject);
return promise;
}
return new Promise((resolve) => host.setTimeout(resolve, 0));
}
/**
* Returns control to the host event loop 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
* @param onReject - optional cleanup invoked only if a host yield fails
* @throws On wrong argument types. {@link TypeError}
* @throws On wrong argument ranges or values. {@link RangeError}
* @example
* Run a loop that periodically yields back to the event loop.
* ```ts
* await asyncLoop(2, 0, () => {});
* ```
*/
export async function asyncLoop(iters, tick, cb, onReject) {
anumber(iters, 'iters');
anumber(tick, 'tick');
if (typeof cb !== 'function')
throw new TypeError('callback must be a function');
// Callback is synchronous by contract; asyncLoop only yields between sync work windows.
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(onReject);
// Track only synchronous work time; scheduler delay after yielding is outside our budget.
ts = Date.now();
}
}
/**
* 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');
const encoded = new TextEncoder().encode(str);
try {
// Copy into the current realm for Firefox extension contexts. Callers that own the returned
// buffer can then wipe it independently of TextEncoder's temporary result.
return new Uint8Array(encoded); // https://bugzil.la/1681809
}
finally {
clean(encoded);
}
}
/**
* 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;
}
/**
* Validates declared required and optional field types on a plain object.
* This walks field schemas and formats detailed errors, so avoid it on hot paths; use direct
* one-line guards such as `abytes()`, `abool()`, or `anumber()` instead.
* @param object - object to validate
* @param fields - map of required field names to expected types
* @param optFields - map of optional field names to expected types
* @param title - label included in thrown errors
* @throws On wrong argument types. {@link TypeError}
* @example
* Validate required and optional option fields.
* ```ts
* validateObject({ N: 1024, dkLen: 32 }, { N: 'number' }, { dkLen: 'number' });
* ```
*/
export const validateObject = (object, fields = {}, optFields = {}, title = 'object') => {
aobject(object, title);
aobject(fields, 'fields');
aobject(optFields, 'optFields');
function checkField(fieldName, expectedType, isOpt) {
const label = title === 'object' ? `param "${String(fieldName)}"` : `"${title}.${String(fieldName)}"`;
// Config fields must be explicit own properties. Optional inherited values are rejected too
// because callers keep reading the same options object after validation.
const val = object[fieldName];
// Runtime objects such as Field instances intentionally satisfy required method slots
// via their shared prototype.
if (!Object.hasOwn(object, fieldName) &&
(isOpt ? val !== undefined : expectedType !== 'function')) {
throw new TypeError(`${label} is invalid: expected own property`);
}
if (isOpt && val === undefined)
return;
const current = typeof val;
if (current !== expectedType || val === null)
throw new TypeError(`${label} is invalid: expected ${expectedType}, got ${current}`);
}
const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));
iter(fields, false);
iter(optFields, true);
};
/**
* Merges default options and passed options.
* @param defaults - base option object
* @param opts - user overrides
* @param title - label included in thrown override errors
* @returns Fresh merged option object with a null prototype.
* @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, title = 'opts') {
aopts(defaults, 'defaults');
if (opts !== undefined)
aopts(opts, title);
// Callers read optional fields directly, so omitted values must not fall through to ambient
// Object.prototype pollution (for example a forged `dkLen` changing SHAKE's default output).
const merged = Object.assign(Object.create(null), 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.
* @throws On wrong argument types. {@link TypeError}
* @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 = {}) {
if (typeof hashCons !== 'function')
throw new TypeError('"hashCons" expected function, got type=' + typeof hashCons);
info = checkOpts({}, info, '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]),
});
-577
View File
@@ -1,577 +0,0 @@
/**
* Argon2 KDF from RFC 9106. Can be used to create a key from password and salt.
* We suggest to use Scrypt. JS Argon is 2-10x slower than native code because of 64-bitness:
* * argon uses uint64, but JS doesn't have fast uint64array
* * uint64 multiplication is 1/3 of time
* * `P` function would be very nice with u64, because most of value will be in registers,
* hovewer with u32 it will require 32 registers, which is too much.
* * JS arrays do slow bound checks, so reading from `A2_BUF` slows it down
* @module
*/
import { blake2b } from "./blake2.js";
import { anumber, checkOpts, clean, kdfInputToBytes, nextTick, swap32IfBE, swap8IfBE, u32, u8, } from "./utils.js";
// RFC 9106 §3.1 type `y`: 0 = Argon2d, 1 = Argon2i, 2 = Argon2id. The numeric values are the
// spec-bound part here; the object keys are internal labels.
const AT = { Argon2d: 0, Argon2i: 1, Argon2id: 2 };
// RFC 9106 sync points constant `SL = 4`, fixed by the design rather than exposed as a tuning knob.
const ARGON2_SYNC_POINTS = 4;
// Preserve Argon2's `LE32(len(X)) || X` encoding for omitted
// optional fields by emitting empty bytes.
const abytesOrZero = (buf, errorTitle = '') => {
if (buf === undefined)
return Uint8Array.of();
return kdfInputToBytes(buf, errorTitle);
};
// Temporary block buffer.
// 1024-byte block: 256 u32 = 128 interleaved low/high halves = RFC's
// 8x8 matrix of 16-byte registers.
const A2_BUF = new Uint32Array(256);
// Quarter-round over 64-bit word indices into `A2_BUF`; each index maps to adjacent low/high u32s.
// Each BlaMka step `X = X + Y + 2 * trunc(X) * trunc(Y)` (trunc = low 32 bits) starts
// with an exact low product from `Math.imul`. The rounded double product is within 1024 of the
// exact u64 product; subtracting that exact low half and rounding to the nearest multiple of
// 2^32 therefore recovers the exact high half. RFC 9106 Figure 19 GB then rotates by 32, 24,
// 16, and 63 bits after each XOR.
function G(a, b, c, d) {
let Al = A2_BUF[2 * a], Ah = A2_BUF[2 * a + 1]; // prettier-ignore
let Bl = A2_BUF[2 * b], Bh = A2_BUF[2 * b + 1]; // prettier-ignore
let Cl = A2_BUF[2 * c], Ch = A2_BUF[2 * c + 1]; // prettier-ignore
let Dl = A2_BUF[2 * d], Dh = A2_BUF[2 * d + 1]; // prettier-ignore
let ml = 0, mh = 0, rl = 0, xh = 0, xl = 0; // prettier-ignore
// A = blamka(A, B); D = rotr64(D ^ A, 32)
ml = Math.imul(Al, Bl);
mh = (((Al >>> 0) * (Bl >>> 0) - (ml >>> 0)) / 0x100000000 + 0.5) | 0; // prettier-ignore
rl = (Al >>> 0) + (Bl >>> 0) + ((ml << 1) >>> 0);
Ah = (Ah + Bh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
Al = rl | 0; // prettier-ignore
xh = Dh ^ Ah;
xl = Dl ^ Al; // prettier-ignore
Dh = xl;
Dl = xh; // prettier-ignore
// C = blamka(C, D); B = rotr64(B ^ C, 24)
ml = Math.imul(Cl, Dl);
mh = (((Cl >>> 0) * (Dl >>> 0) - (ml >>> 0)) / 0x100000000 + 0.5) | 0; // prettier-ignore
rl = (Cl >>> 0) + (Dl >>> 0) + ((ml << 1) >>> 0);
Ch = (Ch + Dh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
Cl = rl | 0; // prettier-ignore
xh = Bh ^ Ch;
xl = Bl ^ Cl; // prettier-ignore
Bh = (xh >>> 24) | (xl << 8);
Bl = (xh << 8) | (xl >>> 24); // prettier-ignore
// A = blamka(A, B); D = rotr64(D ^ A, 16)
ml = Math.imul(Al, Bl);
mh = (((Al >>> 0) * (Bl >>> 0) - (ml >>> 0)) / 0x100000000 + 0.5) | 0; // prettier-ignore
rl = (Al >>> 0) + (Bl >>> 0) + ((ml << 1) >>> 0);
Ah = (Ah + Bh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
Al = rl | 0; // prettier-ignore
xh = Dh ^ Ah;
xl = Dl ^ Al; // prettier-ignore
Dh = (xh >>> 16) | (xl << 16);
Dl = (xh << 16) | (xl >>> 16); // prettier-ignore
// C = blamka(C, D); B = rotr64(B ^ C, 63)
ml = Math.imul(Cl, Dl);
mh = (((Cl >>> 0) * (Dl >>> 0) - (ml >>> 0)) / 0x100000000 + 0.5) | 0; // prettier-ignore
rl = (Cl >>> 0) + (Dl >>> 0) + ((ml << 1) >>> 0);
Ch = (Ch + Dh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
Cl = rl | 0; // prettier-ignore
xh = Bh ^ Ch;
xl = Bl ^ Cl; // prettier-ignore
Bh = (xh << 1) | (xl >>> 31);
Bl = (xh >>> 31) | (xl << 1); // prettier-ignore
((A2_BUF[2 * a] = Al), (A2_BUF[2 * a + 1] = Ah));
((A2_BUF[2 * b] = Bl), (A2_BUF[2 * b + 1] = Bh));
((A2_BUF[2 * c] = Cl), (A2_BUF[2 * c + 1] = Ch));
((A2_BUF[2 * d] = Dl), (A2_BUF[2 * d + 1] = Dh));
}
// Argon2 permutation over 16 register indices into `A2_BUF`, not the register values themselves.
// RFC 9106 Figure 17: these arguments are the 16 `v0..v15` 64-bit word
// indices inside eight 16-byte inputs, not copied word values.
// prettier-ignore
function P(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14, v15) {
// RFC 9106 Figure 18: first apply GB across rows, then across columns of the 8x8 register matrix.
G(v00, v04, v08, v12);
G(v01, v05, v09, v13);
G(v02, v06, v10, v14);
G(v03, v07, v11, v15);
G(v00, v05, v10, v15);
G(v01, v06, v11, v12);
G(v02, v07, v08, v13);
G(v03, v04, v09, v14);
}
function block(x, xPos, yPos, outPos, needXor) {
// Stage R = X xor Y in the destination before permuting it. This avoids rereading both source
// blocks when folding R into the permuted scratch block below.
if (needXor) {
for (let i = 0; i < 256; i++) {
const r = x[xPos + i] ^ x[yPos + i];
A2_BUF[i] = r;
x[outPos + i] ^= r;
}
}
else {
for (let i = 0; i < 256; i++) {
const r = x[xPos + i] ^ x[yPos + i];
A2_BUF[i] = r;
x[outPos + i] = r;
}
}
// rows (8 consecutive 16-register groups)
for (let i = 0; i < 128; i += 16) {
// prettier-ignore
P(i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, i + 10, i + 11, i + 12, i + 13, i + 14, i + 15);
}
// columns (8 strided 16-register groups)
for (let i = 0; i < 16; i += 2) {
// prettier-ignore
P(i, i + 1, i + 16, i + 17, i + 32, i + 33, i + 48, i + 49, i + 64, i + 65, i + 80, i + 81, i + 96, i + 97, i + 112, i + 113);
}
// RFC 9106 step 6: passes after the first XOR the old destination block into the new G(X, Y).
for (let i = 0; i < 256; i++)
x[outPos + i] ^= A2_BUF[i];
clean(A2_BUF);
}
// Variable-Length Hash Function H'
// Returns bytes, not words; 1024-byte block callers explicitly reinterpret with `u32(...)`.
function Hp(A, dkLen) {
const A8 = u8(A);
const T = new Uint32Array(1);
const T8 = u8(T);
// Argon2 H' prefixes dkLen as LE32; native Uint32Array writes would serialize as BE on s390x.
T[0] = swap8IfBE(dkLen);
// Fast path
if (dkLen <= 64)
return blake2b.create({ dkLen }).update(T8).update(A8).digest();
const out = new Uint8Array(dkLen);
let V = blake2b.create({}).update(T8).update(A8).digest();
let pos = 0;
// RFC 9106 Figure 8: each intermediate `V_i` contributes only `W_i`, its first 32 bytes; only
// `V_{r+1}` is emitted in full at the remaining length.
out.set(V.subarray(0, 32));
pos += 32;
// Rest blocks
for (; dkLen - pos > 64; pos += 32) {
const Vh = blake2b.create({}).update(V);
Vh.digestInto(V);
Vh.destroy();
out.set(V.subarray(0, 32), pos);
}
// Last block
out.set(blake2b(V, { dkLen: dkLen - pos }), pos);
clean(V, T);
// H' is byte-oriented; returning `u32(out)` would silently drop dkLen % 4 tail bytes.
return out;
}
// Used only inside argon2Blocks!
function indexAlpha(r, s, laneLen, segmentLen, index, randL, sameLane = false) {
// RFC 9106 §3.4.2 Figures 12-13: map `J1` / `J2` into the current lane's reference area `W`.
let area;
if (r === 0) {
if (s === 0)
area = index - 1;
else if (sameLane)
area = s * segmentLen + index - 1;
else
area = s * segmentLen + (index == 0 ? -1 : 0);
}
else if (sameLane)
area = laneLen - segmentLen + index - 1;
else
area = laneLen - segmentLen + (index == 0 ? -1 : 0);
const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
// Use the same exact-high recovery as G. `areaHigh` is floor(|W| * J1^2/2^32 / 2^32).
const randLow = Math.imul(randL, randL);
const randHigh = (((randL >>> 0) * (randL >>> 0) - (randLow >>> 0)) / 0x100000000 + 0.5) | 0;
const areaLow = Math.imul(area, randHigh);
const areaHigh = (((area >>> 0) * (randHigh >>> 0) - (areaLow >>> 0)) / 0x100000000 + 0.5) | 0;
const rel = area - 1 - areaHigh;
return (startPos + rel) % laneLen;
}
// Exclusive `2^32` sentinel used by `isU32(...)`, not the inclusive maximum u32 value.
const maxUint32 = Math.pow(2, 32);
const ARGON2_DEFAULT_MEMORY = 1024 ** 2; // KiB: 1 GiB
const ARGON2_DEFAULT_MAXMEM = ARGON2_DEFAULT_MEMORY * 1024;
// Validate safe JS integers in `[0, 2^32 - 1]`.
function isU32(num) {
return Number.isSafeInteger(num) && num >= 0 && num < maxUint32;
}
function argon2Opts(opts = {}) {
opts = checkOpts({}, opts);
const merged = {
t: 3,
m: ARGON2_DEFAULT_MEMORY,
p: 1,
version: 0x13,
dkLen: 32,
maxmem: ARGON2_DEFAULT_MAXMEM,
asyncTick: 10,
};
// Unknown keys are copied through unchanged here and later ignored unless
// destructuring consumes them.
for (let [k, v] of Object.entries(opts))
if (v !== undefined)
merged[k] = v;
const { dkLen, p, m, t, version, onProgress, asyncTick } = merged;
// RFC 9106 §3.1: tag length `T` MUST be an integer number of bytes from 4 to 2^32-1.
if (!isU32(dkLen) || dkLen < 4)
throw new Error('"dkLen" must be 4..');
if (!isU32(p) || p < 1 || p >= Math.pow(2, 24))
throw new Error('"p" must be 1..2^24');
if (!isU32(m))
throw new Error('"m" must be 0..2^32');
if (!isU32(t) || t < 1)
throw new Error('"t" (iterations) must be 1..2^32');
if (onProgress !== undefined && typeof onProgress !== 'function')
throw new Error('"onProgress" must be a function');
anumber(asyncTick, 'asyncTick');
/*
Memory size m MUST be an integer number of kibibytes from 8*p
to 2^(32)-1. The actual number of blocks is m', which is m
rounded down to the nearest multiple of 4*p.
*/
if (!isU32(m) || m < 8 * p)
throw new Error('"m" (memory) must be at least 8*p bytes');
// Accept legacy `0x10` for compatibility even though RFC 9106 profiles standardize `0x13`.
if (version !== 0x10 && version !== 0x13)
throw new Error('"version" must be 0x10 or 0x13, got ' + version);
return merged;
}
function argon2InitialHash(password, salt, type, opts) {
const ownedInputs = [];
const BUF = new Uint32Array(1);
const BUF8 = u8(BUF);
let h;
let H0;
let succeeded = false;
const rememberOwned = (input, bytes) => {
if (typeof input === 'string')
ownedInputs.push(bytes);
return bytes;
};
try {
const passwordBytes = rememberOwned(password, kdfInputToBytes(password, 'password'));
const saltBytes = rememberOwned(salt, kdfInputToBytes(salt, 'salt'));
if (!isU32(passwordBytes.length))
throw new Error('"password" must be less of length 1..4Gb');
// RFC 9106 §3.1 only requires S <= 2^32-1 bytes and says 16 bytes is RECOMMENDED for password
// hashing; this library intentionally takes the stricter common >=8-byte salt path.
if (!isU32(saltBytes.length) || saltBytes.length < 8)
throw new Error('"salt" must be of length 8..4Gb');
if (!Object.values(AT).includes(type))
throw new Error('"type" was invalid');
let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress, asyncTick } = argon2Opts(opts);
// Validation
const keyInput = key;
key = rememberOwned(keyInput, abytesOrZero(keyInput, 'key'));
const personalizationInput = personalization;
personalization = rememberOwned(personalizationInput, abytesOrZero(personalizationInput, 'personalization'));
// H_0 = H^(64)(LE32(p) || LE32(T) || LE32(m) || LE32(t) ||
// LE32(v) || LE32(y) || LE32(length(P)) || P ||
// LE32(length(S)) || S || LE32(length(K)) || K ||
// LE32(length(X)) || X)
h = blake2b.create();
for (let item of [p, dkLen, m, t, version, type]) {
// RFC 9106 H0 encodes these scalars as LE32, so normalize the host word before
// exposing bytes.
BUF[0] = swap8IfBE(item);
h.update(BUF8);
}
for (let i of [passwordBytes, saltBytes, key, personalization]) {
BUF[0] = swap8IfBE(i.length); // BUF is u32 array, this is valid once normalized to LE bytes
h.update(BUF8).update(i);
}
// Reserve two extra LE32 words after the 64-byte `H_0` so Figures 3-4 can append
// `LE32(0 or 1) || LE32(i)` in place for the lane-starting blocks.
H0 = new Uint32Array(18);
h.digestInto(u8(H0));
succeeded = true;
return { H0, p, dkLen, m, t, version, maxmem, onProgress, asyncTick };
}
finally {
// digestInto() does not destroy BLAKE2 state. It and all string-derived inputs contain secrets.
if (h)
h.destroy();
clean(BUF, ...ownedInputs);
if (!succeeded && H0)
clean(H0);
}
}
function argon2Init(password, salt, type, opts) {
const { H0, p, dkLen, m, t, version, maxmem, onProgress, asyncTick } = argon2InitialHash(password, salt, type, opts);
// 256 u32 = 1024 (BLOCK_SIZE), fills A2_BUF on processing
try {
// Params
const lanes = p;
// m' = 4 * p * floor (m / 4p)
const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
//q = m' / p columns
const laneLen = Math.floor(mP / p);
const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
// `maxmem` is documented in bytes; compare against the actual 1024-byte block allocation.
const memUsed = mP * 1024;
if (!isU32(maxmem))
throw new Error('"maxmem" expected <2**32, got ' + maxmem);
if (memUsed > maxmem)
throw new Error('"maxmem" limit was hit: memUsed(mP*1024)=' + memUsed + ', maxmem=' + maxmem);
const B = new Uint32Array(memUsed / 4);
// Fill first blocks
for (let l = 0; l < p; l++) {
const i = 256 * laneLen * l;
// B[i][0] = H'^(1024)(H_0 || LE32(0) || LE32(i))
H0[17] = swap8IfBE(l);
H0[16] = swap8IfBE(0);
B.set(swap32IfBE(u32(Hp(H0, 1024))), i);
// B[i][1] = H'^(1024)(H_0 || LE32(1) || LE32(i))
H0[16] = swap8IfBE(1);
B.set(swap32IfBE(u32(Hp(H0, 1024))), i + 256);
}
let perBlock = () => { };
if (onProgress) {
// The first segment of the first pass skips two preinitialized blocks per lane.
const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen - 2 * p;
// Invoke callback if progress changes from 10.01 to 10.02
// Allows to draw smooth progress bar on up to 8K screen
const callbackPer = Math.max(Math.floor(totalBlock / 10000), 1);
let blockCnt = 0;
perBlock = () => {
blockCnt++;
if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
onProgress(blockCnt / totalBlock);
};
}
return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick };
}
finally {
clean(H0);
}
}
function argon2Output(B, p, laneLen, dkLen) {
const B_final = new Uint32Array(256);
for (let l = 0; l < p; l++)
for (let j = 0; j < 256; j++)
B_final[j] ^= B[256 * (laneLen * l + laneLen - 1) + j];
// RFC 9106 steps 7-8 feed the byte string `C` into `H'^T(C)`, so normalize the xor'ed words
// back to spec byte order before `Hp(...)` reinterprets them as bytes.
const res = Hp(swap32IfBE(B_final), dkLen);
// Wipe both the xor scratch and the full working matrix once final digest bytes exist.
// JS cleanup is still only best-effort, but this local buffer is no longer needed here.
clean(B, B_final);
return res;
}
/**
* Fills every Argon2 block for all passes / slices / lanes, yielding once per
* processed block so callers control pacing: the sync driver just drains the
* generator, while the async driver awaits `nextTick()` between time slices.
*/
function* argon2Blocks(ctx, address) {
const { type, mP, p, t, version, B, laneLen, lanes, segmentLen, perBlock } = ctx;
// [address, input, zero_block] format so we can pass single U32 to block function
address[256 + 6] = mP;
address[256 + 8] = t;
address[256 + 10] = type;
for (let r = 0; r < t; r++) {
// RFC 9106 step 6 applies the XOR-on-later-passes rule only for version `0x13`; legacy
// `0x10` keeps the older overwrite behavior used by the v16 test vectors.
const needXor = r !== 0 && version === 0x13;
address[256 + 0] = r;
for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
address[256 + 4] = s;
// RFC 9106 §3.4.1.3: Argon2id uses Argon2i's data-independent `J1` / `J2` generation only
// in pass 0, slices 0 and 1; Argon2i uses it in every segment.
const dataIndependent = type == AT.Argon2i || (type == AT.Argon2id && r === 0 && s < 2);
for (let l = 0; l < p; l++) {
address[256 + 2] = l;
address[256 + 12] = 0;
let startPos = 0;
if (r === 0 && s === 0) {
startPos = 2;
if (dataIndependent) {
address[256 + 12]++;
block(address, 256, 2 * 256, 0, false);
block(address, 0, 2 * 256, 0, false);
}
}
// current block position
let offset = l * laneLen + s * segmentLen + startPos;
for (let index = startPos; index < segmentLen; index++, offset++) {
perBlock();
// Previous block position: wraps to the lane's last block only at lane start,
// which can happen here only for the first block of slice 0 on passes > 0.
const prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
let randL, randH;
if (dataIndependent) {
let i128 = index % 128;
// RFC 9106 §3.4.1.2: each 1024-byte address block yields 128 `(J1, J2)` pairs, so
// regenerate it whenever the segment index crosses a multiple of 128.
if (i128 === 0) {
address[256 + 12]++;
block(address, 256, 2 * 256, 0, false);
block(address, 0, 2 * 256, 0, false);
}
randL = address[2 * i128];
randH = address[2 * i128 + 1];
}
else {
const T = 256 * prev;
randL = B[T];
randH = B[T + 1];
}
// Address-block path selects `J1` / `J2`, then maps them to the reference
// lane/block per RFC 9106 §3.4.
const refLane = r === 0 && s === 0 ? l : randH % lanes;
const refPos = indexAlpha(r, s, laneLen, segmentLen, index, randL, refLane == l);
const refBlock = laneLen * refLane + refPos;
// B[i][j] = G(B[i][j-1], B[l][z])
block(B, 256 * prev, 256 * refBlock, offset * 256, needXor);
yield;
}
}
}
}
clean(address);
}
function argon2(type, password, salt, opts) {
const ctx = argon2Init(password, salt, type, opts);
const blocks = argon2Blocks(ctx, new Uint32Array(3 * 256));
while (!blocks.next().done) { }
return argon2Output(ctx.B, ctx.p, ctx.laneLen, ctx.dkLen);
}
/**
* Argon2d GPU-resistant version.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2d.
* ```ts
* argon2d('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
* @example
* Derive a key with optional secret and scheduler controls.
* ```ts
* const progressLog: number[] = [];
* argon2d('password', 'salt1234', {
* t: 1,
* m: 8,
* p: 1,
* dkLen: 32,
* version: 0x13,
* key: 'secret',
* personalization: 'application',
* maxmem: 1024 * 1024,
* asyncTick: 1,
* onProgress(progress) {
* progressLog.push(progress);
* },
* });
* ```
*/
export const argon2d = (password, salt, opts = {}) => argon2(AT.Argon2d, password, salt, opts);
/**
* Argon2i side-channel-resistant version.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2i.
* ```ts
* argon2i('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
*/
export const argon2i = (password, salt, opts = {}) => argon2(AT.Argon2i, password, salt, opts);
/**
* Argon2id, combining i+d, the most popular version from RFC 9106.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2id.
* ```ts
* argon2id('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
*/
export const argon2id = (password, salt, opts = {}) => argon2(AT.Argon2id, password, salt, opts);
async function argon2Async(type, password, salt, opts) {
const ctx = argon2Init(password, salt, type, opts);
// Keep the generator-local address block reachable so an aborted scheduler yield can wipe it.
const address = new Uint32Array(3 * 256);
const blocks = argon2Blocks(ctx, address);
const abort = () => clean(address, ctx.B);
let ts = Date.now();
while (!blocks.next().done) {
// Date.now() is not monotonic. If the clock goes backwards,
// still yield control.
const diff = Date.now() - ts;
if (diff >= 0 && diff < ctx.asyncTick)
continue;
await nextTick(abort);
// Scheduler delay is outside the synchronous work budget.
ts = Date.now();
}
return argon2Output(ctx.B, ctx.p, ctx.laneLen, ctx.dkLen);
}
/**
* Argon2d async GPU-resistant version.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Promise resolving to derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2d asynchronously.
* ```ts
* await argon2dAsync('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
* @example
* Derive a key asynchronously with optional secret and scheduler controls.
* ```ts
* await argon2dAsync('password', 'salt1234', {
* t: 1,
* m: 8,
* p: 1,
* dkLen: 32,
* version: 0x13,
* key: 'secret',
* personalization: 'application',
* maxmem: 1024 * 1024,
* asyncTick: 1,
* onProgress(progress) {
* if (progress > 1) throw new Error('invalid progress');
* },
* });
* ```
*/
export const argon2dAsync = (password, salt, opts = {}) => argon2Async(AT.Argon2d, password, salt, opts);
/**
* Argon2i async side-channel-resistant version.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Promise resolving to derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2i asynchronously.
* ```ts
* await argon2iAsync('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
*/
export const argon2iAsync = (password, salt, opts = {}) => argon2Async(AT.Argon2i, password, salt, opts);
/**
* Argon2id async, combining i+d, the most popular version from RFC 9106.
* @param password - password or input key material
* @param salt - unique salt value
* @param opts - Argon2 cost and optional tuning parameters. See {@link ArgonOpts}.
* @returns Promise resolving to derived key bytes.
* @throws If the Argon2 input or cost parameters are invalid. {@link Error}
* @example
* Derive a key with Argon2id asynchronously.
* ```ts
* await argon2idAsync('password', 'salt1234', { t: 1, m: 8, p: 1, dkLen: 32 });
* ```
*/
export const argon2idAsync = (password, salt, opts = {}) => argon2Async(AT.Argon2id, password, salt, opts);