diff --git a/packages/noble-hashes/CHANGELOG.md b/packages/noble-hashes/CHANGELOG.md deleted file mode 100644 index d541f2d6..00000000 --- a/packages/noble-hashes/CHANGELOG.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [0.2.2](https://github.com/constructive-io/dev-utils/compare/@constructive-io/noble-hashes@0.2.1...@constructive-io/noble-hashes@0.2.2) (2026-07-31) - -**Note:** Version bump only for package @constructive-io/noble-hashes - -## [0.2.1](https://github.com/constructive-io/dev-utils/compare/@constructive-io/noble-hashes@0.2.0...@constructive-io/noble-hashes@0.2.1) (2026-05-11) - -**Note:** Version bump only for package @constructive-io/noble-hashes - -# 0.2.0 (2026-05-11) - -### Features - -- add @constructive-io/noble-hashes — dual CJS+ESM fork of @noble/hashes@2.2.0 ([aece559](https://github.com/constructive-io/dev-utils/commit/aece5592749de2a7921cc33d9c0d79c876a04b6b)) diff --git a/packages/noble-hashes/LICENSE b/packages/noble-hashes/LICENSE deleted file mode 100644 index 0876e182..00000000 --- a/packages/noble-hashes/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 Paul Miller (https://paulmillr.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/packages/noble-hashes/README.md b/packages/noble-hashes/README.md deleted file mode 100644 index a8637ebf..00000000 --- a/packages/noble-hashes/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# @constructive-io/noble-hashes - -> **NOTE:** This is a fork of [`@noble/hashes`](https://github.com/paulmillr/noble-hashes) (v2.2.0) by Paul Miller. -> We dual-publish as both **CJS and ESM** to avoid downstream compatibility issues with tools and -> runtimes that don't yet fully support ESM-only packages (e.g., Jest, older bundlers, CJS-based -> Node.js tooling). The source code is unchanged from upstream — only the build tooling differs. - -Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt. - -## Installation - -``` -pnpm add @constructive-io/noble-hashes -``` - -## Usage - -```typescript -import { sha256 } from '@constructive-io/noble-hashes/sha2'; -import { bytesToHex } from '@constructive-io/noble-hashes/utils'; - -const hash = sha256(new Uint8Array([1, 2, 3])); -console.log(bytesToHex(hash)); -``` - -Works with both `require()` (CJS) and `import` (ESM). - -## Why fork? - -`@noble/hashes` v2.x ships as ESM-only (`"type": "module"`). This causes `require()` calls to fail -with "Must use import to load ES Module" in CJS environments. Rather than patching every consumer, -we maintain this fork with dual CJS+ESM output via [makage](https://github.com/constructive-io/dev-utils). - -## Syncing upstream - -To update from a new `@noble/hashes` release: - -1. Copy the `src/` files from the target version -2. Strip `.ts` extensions from import paths (this repo uses `moduleResolution: "node"`) -3. Build and run tests -4. Bump the version and publish - -## License - -MIT — Copyright (c) 2022 Paul Miller (https://paulmillr.com). See [LICENSE](./LICENSE). diff --git a/packages/noble-hashes/__tests__/sha256.test.ts b/packages/noble-hashes/__tests__/sha256.test.ts deleted file mode 100644 index e995c401..00000000 --- a/packages/noble-hashes/__tests__/sha256.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import crypto from 'crypto'; - -import { sha256 } from '../src/sha2'; -import { bytesToHex, hexToBytes, utf8ToBytes } from '../src/utils'; - -// NIST test vectors from FIPS 180-4 -// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf -describe('sha256', () => { - it('should hash empty string', () => { - const hash = sha256(new Uint8Array(0)); - expect(bytesToHex(hash)).toBe( - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' - ); - }); - - it('should hash "abc"', () => { - const hash = sha256(utf8ToBytes('abc')); - expect(bytesToHex(hash)).toBe( - 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' - ); - }); - - it('should hash "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"', () => { - const hash = sha256( - utf8ToBytes('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') - ); - expect(bytesToHex(hash)).toBe( - '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1' - ); - }); - - it('should hash "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"', () => { - const hash = sha256( - utf8ToBytes( - 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu' - ) - ); - expect(bytesToHex(hash)).toBe( - 'cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1' - ); - }); - - it('should match Node.js crypto for random inputs', () => { - const inputs = [ - '', - 'hello world', - 'The quick brown fox jumps over the lazy dog', - 'a'.repeat(1000), - '\x00\x01\x02\x03\x04\x05', - ]; - - for (const input of inputs) { - const data = utf8ToBytes(input); - const noble = bytesToHex(sha256(data)); - const node = crypto.createHash('sha256').update(data).digest('hex'); - expect(noble).toBe(node); - } - }); - - it('should work with incremental hashing via .create()', () => { - const hasher = sha256.create(); - hasher.update(utf8ToBytes('hello')); - hasher.update(utf8ToBytes(' ')); - hasher.update(utf8ToBytes('world')); - const incremental = bytesToHex(hasher.digest()); - - const oneshot = bytesToHex(sha256(utf8ToBytes('hello world'))); - expect(incremental).toBe(oneshot); - }); -}); - -describe('utils', () => { - it('bytesToHex should convert bytes to hex', () => { - expect(bytesToHex(new Uint8Array([0xca, 0xfe, 0x01, 0x23]))).toBe( - 'cafe0123' - ); - }); - - it('hexToBytes should convert hex to bytes', () => { - const bytes = hexToBytes('cafe0123'); - expect(Array.from(bytes)).toEqual([0xca, 0xfe, 0x01, 0x23]); - }); - - it('hexToBytes and bytesToHex should round-trip', () => { - const hex = 'deadbeef0123456789abcdef'; - expect(bytesToHex(hexToBytes(hex))).toBe(hex); - }); -}); diff --git a/packages/noble-hashes/jest.config.js b/packages/noble-hashes/jest.config.js deleted file mode 100644 index f4c0ce04..00000000 --- a/packages/noble-hashes/jest.config.js +++ /dev/null @@ -1,18 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - transform: { - '^.+\\.tsx?$': [ - 'ts-jest', - { - babelConfig: false, - tsconfig: 'tsconfig.json', - }, - ], - }, - transformIgnorePatterns: [`/node_modules/*`], - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - modulePathIgnorePatterns: ['dist/*'], -}; diff --git a/packages/noble-hashes/package.json b/packages/noble-hashes/package.json deleted file mode 100644 index 551d36eb..00000000 --- a/packages/noble-hashes/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@constructive-io/noble-hashes", - "version": "0.2.2", - "description": "Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt. Dual CJS+ESM fork of @noble/hashes.", - "author": "Constructive ", - "homepage": "https://github.com/constructive-io/dev-utils", - "license": "MIT", - "main": "index.js", - "module": "esm/index.js", - "types": "index.d.ts", - "publishConfig": { - "access": "public", - "directory": "dist" - }, - "scripts": { - "copy": "makage assets", - "clean": "makage clean", - "prepublishOnly": "npm run build", - "build": "makage build && cp LICENSE dist/LICENSE", - "lint": "eslint . --fix", - "test": "jest", - "test:watch": "jest --watch" - }, - "repository": { - "type": "git", - "url": "https://github.com/constructive-io/dev-utils" - }, - "keywords": [ - "sha256", - "sha512", - "sha3", - "blake2", - "blake3", - "hmac", - "hkdf", - "pbkdf2", - "scrypt", - "hash", - "cryptography", - "noble-hashes" - ], - "bugs": { - "url": "https://github.com/constructive-io/dev-utils/issues" - }, - "devDependencies": { - "makage": "0.1.10" - }, - "gitHead": "9d6f42172ae6d5777212226e97959c05b4dddd32" -} diff --git a/packages/noble-hashes/src/_blake.ts b/packages/noble-hashes/src/_blake.ts deleted file mode 100644 index 71c1faf3..00000000 --- a/packages/noble-hashes/src/_blake.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Internal helpers for blake hash. - * @module - */ -import { rotr, type TRet } from './utils'; - -/** - * Internal blake permutation table. - * Rows `0..9` serve BLAKE2s, rows `0..11` serve BLAKE2b with `10..11 = 0..1`, and Blake1 also - * reuses the later rows shown below. Blake1 expands rounds `10..15` as `SIGMA[i % 10]`, so rows - * `10..15` intentionally repeat rows `0..5` for the 14-round (256) and 16-round (512) variants. - */ -// prettier-ignore -export const BSIGMA: TRet = /* @__PURE__ */ Uint8Array.from([ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, - 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, - 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, - 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, - 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, - 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, - 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, - 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, - // Blake1, unused in others - 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, - 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, - 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, -]); - -// prettier-ignore -export type Num4 = { a: number; b: number; c: number; d: number; }; - -// 32-bit / BLAKE2s first half of G, with the fixed `(16, 12)` rotation pair. -// Parameter `x` is the RFC 7693 first-half message word, or Blake1's pre-mixed -// `m[sigma[r][2i]] ^ u[sigma[r][2i+1]]` addend in the 32-bit path. -export function G1s(a: number, b: number, c: number, d: number, x: number): Num4 { - a = (a + b + x) | 0; - d = rotr(d ^ a, 16); - c = (c + d) | 0; - b = rotr(b ^ c, 12); - return { a, b, c, d }; -} - -// 32-bit / BLAKE2s second half of G. -// Parameter `x` is the RFC 7693 second-half (`y`) message word, or Blake1's pre-mixed -// `m[sigma[r][2i + 1]] ^ u[sigma[r][2i]]` addend in the 32-bit path. -export function G2s(a: number, b: number, c: number, d: number, x: number): Num4 { - a = (a + b + x) | 0; - d = rotr(d ^ a, 8); - c = (c + d) | 0; - b = rotr(b ^ c, 7); - return { a, b, c, d }; -} diff --git a/packages/noble-hashes/src/_md.ts b/packages/noble-hashes/src/_md.ts deleted file mode 100644 index e1fdb713..00000000 --- a/packages/noble-hashes/src/_md.ts +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Internal Merkle-Damgard hash utils. - * @module - */ -import { - abytes, - aexists, - aoutput, - clean, - createView, - type Hash, - type TArg, - type TRet, -} from './utils'; - -/** - * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`. - * Returns bits from `b` when `a` is set, otherwise from `c`. - * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never - * set the same bit. - * @param a - selector word - * @param b - word chosen when selector bit is set - * @param c - word chosen when selector bit is clear - * @returns Mixed 32-bit word. - * @example - * Combine three words with the shared 32-bit choice primitive. - * ```ts - * Chi(0xffffffff, 0x12345678, 0x87654321); - * ``` - */ -export function Chi(a: number, b: number, c: number): number { - return (a & b) ^ (~a & c); -} - -/** - * Shared 32-bit majority primitive reused by SHA-256 and SHA-1. - * Returns bits shared by at least two inputs. - * @param a - first input word - * @param b - second input word - * @param c - third input word - * @returns Mixed 32-bit word. - * @example - * Combine three words with the shared 32-bit majority primitive. - * ```ts - * Maj(0xffffffff, 0x12345678, 0x87654321); - * ``` - */ -export function Maj(a: number, b: number, c: number): number { - return (a & b) ^ (a & c) ^ (b & c); -} - -/** - * Merkle-Damgard hash construction base class. - * Could be used to create MD5, RIPEMD, SHA1, SHA2. - * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit - * strings with partial-byte tails. - * @param blockLen - internal block size in bytes - * @param outputLen - digest size in bytes - * @param padOffset - trailing length field size in bytes - * @param isLE - whether length and state words are encoded in little-endian - * @example - * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow. - * ```ts - * import { _SHA1 } from '@noble/hashes/legacy.js'; - * const hash = new _SHA1(); - * hash.update(new Uint8Array([97, 98, 99])); - * hash.digest(); - * ``` - */ -export abstract class HashMD> implements Hash { - // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input - // when it can process whole blocks without buffering first. - protected abstract process(buf: DataView, offset: number): void; - protected abstract get(): number[]; - protected abstract set(...args: number[]): void; - abstract destroy(): void; - protected abstract roundClean(): void; - - readonly blockLen: number; - readonly outputLen: number; - readonly canXOF = false; - readonly padOffset: number; - readonly isLE: boolean; - - // For partial updates less than block size - protected buffer: Uint8Array; - protected view: DataView; - protected finished = false; - protected length = 0; - protected pos = 0; - protected destroyed = false; - - constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) { - this.blockLen = blockLen; - this.outputLen = outputLen; - this.padOffset = padOffset; - this.isLE = isLE; - this.buffer = new Uint8Array(blockLen); - this.view = createView(this.buffer); - } - update(data: TArg): this { - aexists(this); - abytes(data); - const { view, buffer, blockLen } = this; - const len = data.length; - for (let pos = 0; pos < len; ) { - const take = Math.min(blockLen - this.pos, len - pos); - // Fast path only when there is no buffered partial block: `take === blockLen` implies - // `this.pos === 0`, so we can process full blocks directly from the input view. - if (take === blockLen) { - const dataView = createView(data); - for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); - continue; - } - buffer.set(data.subarray(pos, pos + take), this.pos); - this.pos += take; - pos += take; - if (this.pos === blockLen) { - this.process(view, 0); - this.pos = 0; - } - } - this.length += data.length; - this.roundClean(); - return this; - } - digestInto(out: TArg): void { - aexists(this); - aoutput(out, this); - this.finished = true; - // Padding - // We can avoid allocation of buffer for padding completely if it - // was previously not allocated here. But it won't change performance. - const { buffer, view, blockLen, isLE } = this; - let { pos } = this; - // append the bit '1' to the message - buffer[pos++] = 0b10000000; - clean(this.buffer.subarray(pos)); - // we have less than padOffset left in buffer, so we cannot put length in - // current block, need process it and pad again - if (this.padOffset > blockLen - pos) { - this.process(view, 0); - pos = 0; - } - // Pad until full block byte with zeros - for (let i = pos; i < blockLen; i++) buffer[i] = 0; - // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from - // the padding fill above, and JS will overflow before user input can make that half non-zero. - // So we only need to write the low 64 bits here. - view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE); - this.process(view, 0); - const oview = createView(out); - const len = this.outputLen; - // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT - if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit'); - const outLen = len / 4; - const state = this.get(); - if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state'); - for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE); - } - digest(): TRet { - const { buffer, outputLen } = this; - this.digestInto(buffer); - // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return - // fresh bytes to the caller. - const res = buffer.slice(0, outputLen); - this.destroy(); - return res as TRet; - } - _cloneInto(to?: T): T { - to ||= new (this.constructor as any)() as T; - to.set(...this.get()); - const { blockLen, buffer, length, finished, destroyed, pos } = this; - to.destroyed = destroyed; - to.finished = finished; - to.length = length; - to.pos = pos; - // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and - // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it. - if (length % blockLen) to.buffer.set(buffer); - return to as unknown as any; - } - clone(): T { - return this._cloneInto(); - } -} - -/** - * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53. - * Check out `test/misc/sha2-gen-iv.js` for recomputation guide. - */ - -/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the - * square roots of the first eight prime numbers. Exported as a shared table; callers must treat - * it as read-only because constructors copy words from it by index. */ -export const SHA256_IV: TRet = /* @__PURE__ */ Uint32Array.from([ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, -]); - -/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must - * treat it as read-only because constructors copy words from it by index. */ -export const SHA224_IV: TRet = /* @__PURE__ */ Uint32Array.from([ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, -]); - -/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen - * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth - * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only - * because constructors copy halves from it by index. */ -export const SHA384_IV: TRet = /* @__PURE__ */ Uint32Array.from([ - 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939, - 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4, -]); - -/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen - * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first - * eight prime numbers. Exported as a shared table; callers must treat it as read-only because - * constructors copy halves from it by index. */ -export const SHA512_IV: TRet = /* @__PURE__ */ Uint32Array.from([ - 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1, - 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179, -]); diff --git a/packages/noble-hashes/src/_u64.ts b/packages/noble-hashes/src/_u64.ts deleted file mode 100644 index 4289c24f..00000000 --- a/packages/noble-hashes/src/_u64.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Internal helpers for u64. - * BigUint64Array is too slow as per 2026, so we implement it using - * Uint32Array. - * @privateRemarks TODO: re-check {@link https://issues.chromium.org/issues/42212588} - * @module - */ -import type { TRet } from './utils'; - -const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); -const _32n = /* @__PURE__ */ BigInt(32); - -// Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high -// }` to match little-endian word order rather than the property names. -function fromBig( - n: bigint, - le = false -): { - h: number; - l: number; -} { - if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) }; - return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; -} - -// Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array -// holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`. -function split(lst: bigint[], le = false): TRet { - const len = lst.length; - let Ah = new Uint32Array(len); - let Al = new Uint32Array(len); - for (let i = 0; i < len; i++) { - const { h, l } = fromBig(lst[i], le); - [Ah[i], Al[i]] = [h, l]; - } - return [Ah, Al] as TRet; -} - -// Combine explicit `(high, low)` 32-bit halves into a bigint; `>>> 0` normalizes signed JS -// bitwise results back to uint32 first, and little-endian callers must swap. -const toBig = (h: number, l: number): bigint => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0); -// High 32-bit half of a 64-bit logical right shift for `s` in `0..31`. -const shrSH = (h: number, _l: number, s: number): number => h >>> s; -// Low 32-bit half of a 64-bit logical right shift, valid for `s` in `1..31`. -const shrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s); -// High 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`. -const rotrSH = (h: number, l: number, s: number): number => (h >>> s) | (l << (32 - s)); -// Low 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`. -const rotrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s); -// High 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. -const rotrBH = (h: number, l: number, s: number): number => (h << (64 - s)) | (l >>> (s - 32)); -// Low 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. -const rotrBL = (h: number, l: number, s: number): number => (h >>> (s - 32)) | (l << (64 - s)); -// High 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped low half. -const rotr32H = (_h: number, l: number): number => l; -// Low 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped high half. -const rotr32L = (h: number, _l: number): number => h; -// High 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`. -const rotlSH = (h: number, l: number, s: number): number => (h << s) | (l >>> (32 - s)); -// Low 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`. -const rotlSL = (h: number, l: number, s: number): number => (l << s) | (h >>> (32 - s)); -// High 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. -const rotlBH = (h: number, l: number, s: number): number => (l << (s - 32)) | (h >>> (64 - s)); -// Low 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`. -const rotlBL = (h: number, l: number, s: number): number => (h << (s - 32)) | (l >>> (64 - s)); - -// Add two split 64-bit words and return the split `{ h, l }` sum. -// JS uses 32-bit signed integers for bitwise operations, so we cannot simply shift the carry out -// of the low sum and instead use division. -function add( - Ah: number, - Al: number, - Bh: number, - Bl: number -): { - h: number; - l: number; -} { - const l = (Al >>> 0) + (Bl >>> 0); - return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 }; -} -// Addition with more than 2 elements -// Unmasked low-word accumulator for 3-way addition; pass the raw result into `add3H(...)`. -const add3L = (Al: number, Bl: number, Cl: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); -// High-word finalize step for 3-way addition; `low` must be the untruncated output of `add3L(...)`. -const add3H = (low: number, Ah: number, Bh: number, Ch: number): number => - (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0; -// Unmasked low-word accumulator for 4-way addition; pass the raw result into `add4H(...)`. -const add4L = (Al: number, Bl: number, Cl: number, Dl: number): number => - (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); -// High-word finalize step for 4-way addition; `low` must be the untruncated output of `add4L(...)`. -const add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number): number => - (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0; -// Unmasked low-word accumulator for 5-way addition; pass the raw result into `add5H(...)`. -const add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number): number => - (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); -// High-word finalize step for 5-way addition; `low` must be the untruncated output of `add5L(...)`. -const add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number): number => - (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0; - -// prettier-ignore -export { - add, add3H, add3L, add4H, add4L, add5H, add5L, fromBig, rotlBH, rotlBL, rotlSH, rotlSL, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL, shrSH, shrSL, split, toBig -}; -// Canonical grouped namespace for callers that prefer one object. -// Named exports stay for direct imports. -// prettier-ignore -const u64: { fromBig: typeof fromBig; split: typeof split; toBig: (h: number, l: number) => bigint; shrSH: (h: number, _l: number, s: number) => number; shrSL: (h: number, l: number, s: number) => number; rotrSH: (h: number, l: number, s: number) => number; rotrSL: (h: number, l: number, s: number) => number; rotrBH: (h: number, l: number, s: number) => number; rotrBL: (h: number, l: number, s: number) => number; rotr32H: (_h: number, l: number) => number; rotr32L: (h: number, _l: number) => number; rotlSH: (h: number, l: number, s: number) => number; rotlSL: (h: number, l: number, s: number) => number; rotlBH: (h: number, l: number, s: number) => number; rotlBL: (h: number, l: number, s: number) => number; add: typeof add; add3L: (Al: number, Bl: number, Cl: number) => number; add3H: (low: number, Ah: number, Bh: number, Ch: number) => number; add4L: (Al: number, Bl: number, Cl: number, Dl: number) => number; add4H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number) => number; add5H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) => number; add5L: (Al: number, Bl: number, Cl: number, Dl: number, El: number) => number; } = { - fromBig, split, toBig, - shrSH, shrSL, - rotrSH, rotrSL, rotrBH, rotrBL, - rotr32H, rotr32L, - rotlSH, rotlSL, rotlBH, rotlBL, - add, add3L, add3H, add4L, add4H, add5H, add5L, -}; -// Default export mirrors named `u64` for compatibility with object-style imports. -export default u64; diff --git a/packages/noble-hashes/src/argon2.ts b/packages/noble-hashes/src/argon2.ts deleted file mode 100644 index 88b8d7fa..00000000 --- a/packages/noble-hashes/src/argon2.ts +++ /dev/null @@ -1,667 +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 { add3H, add3L, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL } from './_u64'; -import { blake2b } from './blake2'; -import { - anumber, - clean, - type KDFInput, - kdfInputToBytes, - nextTick, - swap8IfBE, - swap32IfBE, - type TArg, - type TRet, - u8, - u32, -} from './utils'; - -// 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 = { Argond2d: 0, Argon2i: 1, Argon2id: 2 } as const; -type Types = (typeof AT)[keyof typeof AT]; - -// 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?: TArg, errorTitle = ''): TRet => { - if (buf === undefined) return Uint8Array.of(); - return kdfInputToBytes(buf, errorTitle); -}; - -// Unsigned `u32 * u32 = { h, l }`, returned as split 64-bit halves. -function mul(a: number, b: number) { - // Split into 16-bit limbs so each partial product stays exact under `Math.imul`. - const aL = a & 0xffff; - const aH = a >>> 16; - const bL = b & 0xffff; - const bH = b >>> 16; - const ll = Math.imul(aL, bL); - const hl = Math.imul(aH, bL); - const lh = Math.imul(aL, bH); - const hh = Math.imul(aH, bH); - const carry = (ll >>> 16) + (hl & 0xffff) + lh; - const high = (hh + (hl >>> 16) + (carry >>> 16)) | 0; - const low = (carry << 16) | (ll & 0xffff); - return { h: high, l: low }; -} - -function mul2(a: number, b: number) { - // Double the split 64-bit product; carry from `l` is folded back into `h` via `l >>> 31`. - const { h, l } = mul(a, b); - return { h: ((h << 1) | (l >>> 31)) & 0xffff_ffff, l: (l << 1) & 0xffff_ffff }; -} - -// BlaMka permutation for Argon2 -// `A + B + 2 * trunc(A) * trunc(B)`, where `trunc(...)` means the low 32-bit halves. -function blamka(Ah: number, Al: number, Bh: number, Bl: number) { - const { h: Ch, l: Cl } = mul2(Al, Bl); - // A + B + (2 * A * B) - const Rll = add3L(Al, Bl, Cl); - return { h: add3H(Rll, Ah, Bh, Ch), l: Rll | 0 }; -} - -// 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. -function G(a: number, b: number, c: number, d: number) { - 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 - - // RFC 9106 Figure 19 GB rotates by 32, 24, 16, and 63 bits after each XOR step. - ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl)); - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: rotr32H(Dh, Dl), Dl: rotr32L(Dh, Dl) }); - - ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl)); - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: rotrSH(Bh, Bl, 24), Bl: rotrSL(Bh, Bl, 24) }); - - ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl)); - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: rotrSH(Dh, Dl, 16), Dl: rotrSL(Dh, Dl, 16) }); - - ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl)); - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: rotrBH(Bh, Bl, 63), Bl: rotrBL(Bh, Bl, 63) }); - - ((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: number, v01: number, v02: number, v03: number, v04: number, v05: number, v06: number, v07: number, - v08: number, v09: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, -) { - // 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: TArg, xPos: number, yPos: number, outPos: number, needXor: boolean) { - for (let i = 0; i < 256; i++) A2_BUF[i] = x[xPos + i] ^ x[yPos + i]; - // 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). - if (needXor) for (let i = 0; i < 256; i++) x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i]; - else for (let i = 0; i < 256; i++) x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i]; - clean(A2_BUF); -} - -// Variable-Length Hash Function H' -// Returns bytes, not words; 1024-byte block callers explicitly reinterpret with `u32(...)`. -function Hp(A: TArg, dkLen: number): TRet { - 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 as TRet; -} - -// Used only inside process block! -function indexAlpha( - r: number, - s: number, - laneLen: number, - segmentLen: number, - index: number, - randL: number, - sameLane: boolean = false -) { - // RFC 9106 §3.4.2 Figures 12-13: map `J1` / `J2` into the current lane's reference area `W`. - let area: number; - 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; - // RFC 9106 Figure 13: `mul(randL, randL).h` is `floor(J_1^2 / 2^32)`, and the outer high-half - // multiply computes `floor(|W| * x / 2^32)` without floating-point math. - const rel = area - 1 - mul(area, mul(randL, randL).h).h; - return (startPos + rel) % laneLen; -} - -/** Argon2 cost, output, and optional secret/personalization inputs. */ -export type ArgonOpts = { - /** Time cost measured in iterations. */ - t: number; - /** Memory cost in kibibytes. */ - m: number; - /** Parallelization parameter. */ - p: number; - /** Argon2 version number. Defaults to `0x13`. */ - version?: number; - /** Optional secret key mixed into initialization. */ - key?: KDFInput; - /** Optional personalization string or bytes. */ - personalization?: KDFInput; - /** Desired output length in bytes. RFC 9106 §3.1 requires `T` in the 4..(2^32 - 1) range. */ - dkLen?: number; - /** Max scheduler block time in milliseconds for the async variants. */ - asyncTick?: number; - /** Maximum temporary memory budget in bytes. */ - maxmem?: number; - /** - * Optional progress callback invoked during long-running derivations. - * param progress - completion fraction in the `0..1` range - */ - onProgress?: (progress: number) => void; -}; - -// Exclusive `2^32` sentinel used by `isU32(...)`, not the inclusive maximum u32 value. -const maxUint32 = Math.pow(2, 32); -// Validate safe JS integers in `[0, 2^32 - 1]`. -function isU32(num: number) { - return Number.isSafeInteger(num) && num >= 0 && num < maxUint32; -} - -function argon2Opts(opts: TArg) { - const merged: any = { - version: 0x13, - dkLen: 32, - maxmem: maxUint32 - 1, - 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('"progressCb" 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 argon2Init( - password: TArg, - salt: TArg, - type: Types, - opts: TArg -) { - password = kdfInputToBytes(password, 'password'); - salt = kdfInputToBytes(salt, 'salt'); - if (!isU32(password.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(salt.length) || salt.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 - key = abytesOrZero(key, 'key'); - personalization = abytesOrZero(personalization, '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) - const h = blake2b.create(); - const BUF = new Uint32Array(1); - const BUF8 = u8(BUF); - 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 [password, salt, 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. - const H0 = new Uint32Array(18); - const H0_8 = u8(H0); - h.digestInto(H0_8); - // 256 u32 = 1024 (BLOCK_SIZE), fills A2_BUF on processing - - // 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); - }; - } - clean(BUF, H0); - return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick }; -} - -function argon2Output( - B: TArg, - p: number, - laneLen: number, - dkLen: number -): TRet { - 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; -} - -function processBlock( - B: TArg, - address: TArg, - l: number, - r: number, - s: number, - index: number, - laneLen: number, - segmentLen: number, - lanes: number, - offset: number, - prev: number, - dataIndependent: boolean, - needXor: boolean -) { - if (offset % laneLen) prev = offset - 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); -} - -function argon2( - type: Types, - password: TArg, - salt: TArg, - opts: TArg -): TRet { - const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock } = argon2Init( - password, - salt, - type, - opts - ); - // Pre-loop setup - // [address, input, zero_block] format so we can pass single U32 to block function - const address = new Uint32Array(3 * 256); - 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 postion - let offset = l * laneLen + s * segmentLen + startPos; - // previous block position - let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1; - for (let index = startPos; index < segmentLen; index++, offset++, prev++) { - perBlock(); - processBlock( - B, - address, - l, - r, - s, - index, - laneLen, - segmentLen, - lanes, - offset, - prev, - dataIndependent, - needXor - ); - } - } - } - } - clean(address); - return argon2Output(B, p, laneLen, 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 }); - * ``` - */ -export const argon2d = ( - password: TArg, - salt: TArg, - opts: TArg -): TRet => argon2(AT.Argond2d, 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: TArg, - salt: TArg, - opts: TArg -): TRet => 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: TArg, - salt: TArg, - opts: TArg -): TRet => argon2(AT.Argon2id, password, salt, opts); - -async function argon2Async( - type: Types, - password: TArg, - salt: TArg, - opts: TArg -): Promise> { - const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick } = - argon2Init(password, salt, type, opts); - // Pre-loop setup - // [address, input, zero_block] format so we can pass single U32 to block function - const address = new Uint32Array(3 * 256); - address[256 + 6] = mP; - address[256 + 8] = t; - address[256 + 10] = type; - let ts = Date.now(); - 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 postion - let offset = l * laneLen + s * segmentLen + startPos; - // previous block position - let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1; - for (let index = startPos; index < segmentLen; index++, offset++, prev++) { - perBlock(); - processBlock( - B, - address, - l, - r, - s, - index, - laneLen, - segmentLen, - lanes, - offset, - prev, - dataIndependent, - needXor - ); - // Date.now() is not monotonic. If the clock goes backwards, - // still yield control. - const diff = Date.now() - ts; - if (!(diff >= 0 && diff < asyncTick)) { - await nextTick(); - ts += diff; - } - } - } - } - } - clean(address); - return argon2Output(B, p, laneLen, 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 }); - * ``` - */ -export const argon2dAsync = ( - password: TArg, - salt: TArg, - opts: TArg -): Promise> => argon2Async(AT.Argond2d, 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: TArg, - salt: TArg, - opts: TArg -): Promise> => 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: TArg, - salt: TArg, - opts: TArg -): Promise> => argon2Async(AT.Argon2id, password, salt, opts); diff --git a/packages/noble-hashes/src/blake1.ts b/packages/noble-hashes/src/blake1.ts deleted file mode 100644 index 7e5c4c57..00000000 --- a/packages/noble-hashes/src/blake1.ts +++ /dev/null @@ -1,614 +0,0 @@ -/** - * Blake1 legacy hash function, one of SHA3 proposals. - * Rarely used. Check out blake2 or blake3 instead. - * {@link https://www.aumasson.jp/blake/blake.pdf} - * - * In the best case, there are 0 allocations. - * - * Differences from blake2: - * - * - BE instead of LE - * - Paddings, similar to MD5, RIPEMD, SHA1, SHA2, but: - * - length flag is located before actual length - * - padding block is compressed differently (no lengths) - * Instead of msg[sigma[k]], we have `msg[sigma[k]] ^ constants[sigma[k-1]]` - * (-1 for g1, g2 without -1) - * - Salt is XOR-ed into constants instead of state - * - Salt is XOR-ed with output in `compress` - * - Additional rows (+64 bytes) in SIGMA for new rounds - * - Different round count: - * - 14 / 10 rounds in blake256 / blake2s - * - 16 / 12 rounds in blake512 / blake2b - * - blake512: G1b: rotr 24 -> 25, G2b: rotr 63 -> 11 - * @module - */ -import { BSIGMA, G1s, G2s } from './_blake'; -import { SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md'; -import * as u64 from './_u64'; -// prettier-ignore -import { - abytes, aexists, aoutput, - type CHash, - clean, createHasher, - createView, - type Hash, - type TArg, - type TRet -} from './utils'; - -/** Blake1 options. Basically just `salt`. */ -export type BlakeOpts = { - /** Optional salt mixed into initialization. */ - salt?: Uint8Array; -}; - -// Shared unsalted sentinel, sized for the 64-bit path and reused by the 32-bit path via prefix. -const EMPTY_SALT = /* @__PURE__ */ new Uint32Array(8); - -// Base destroy logic only clears salt-derived state; the partial message buffer and length/position -// bookkeeping remain until the instance or backing buffer is reused. -abstract class BLAKE1> implements Hash { - readonly canXOF = false; - protected finished = false; - protected length = 0; - protected pos = 0; - protected destroyed = false; - // For partial updates less than block size - protected buffer: Uint8Array; - protected view: DataView; - protected salt: Uint32Array; - abstract compress(view: DataView, offset: number, withLength?: boolean): void; - protected abstract get(): number[]; - protected abstract set(...args: number[]): void; - - readonly blockLen: number; - readonly outputLen: number; - private lengthFlag: number; - private counterLen: number; - protected constants: Uint32Array; - - constructor( - blockLen: number, - outputLen: number, - lengthFlag: number, - counterLen: number, - saltLen: number, - constants: Uint32Array, - opts: BlakeOpts = {} - ) { - const { salt } = opts; - this.blockLen = blockLen; - this.outputLen = outputLen; - this.lengthFlag = lengthFlag; - this.counterLen = counterLen; - this.buffer = new Uint8Array(blockLen); - this.view = createView(this.buffer); - if (salt !== undefined) { - let slt = salt; - abytes(slt, 4 * saltLen, 'salt'); - // if (slt.length !== 4 * saltLen) throw new Error('wrong salt length'); - const salt32 = (this.salt = new Uint32Array(saltLen)); - const sv = createView(slt); - this.constants = constants.slice(); - for (let i = 0, offset = 0; i < salt32.length; i++, offset += 4) { - salt32[i] = sv.getUint32(offset, false); - this.constants[i] ^= salt32[i]; - } - } else { - this.salt = EMPTY_SALT; - this.constants = constants; - } - } - update(data: TArg): this { - aexists(this); - abytes(data); - // From _md, but update length before each compress - const { view, buffer, blockLen } = this; - const len = data.length; - let dataView; - for (let pos = 0; pos < len; ) { - const take = Math.min(blockLen - this.pos, len - pos); - // Fast path only when there is no buffered partial block: `take === blockLen` implies - // `this.pos === 0`, so we can process full blocks directly from the input view. - if (take === blockLen) { - if (!dataView) dataView = createView(data); - for (; blockLen <= len - pos; pos += blockLen) { - this.length += blockLen; - this.compress(dataView, pos); - } - continue; - } - buffer.set(data.subarray(pos, pos + take), this.pos); - this.pos += take; - pos += take; - if (this.pos === blockLen) { - this.length += blockLen; - this.compress(view, 0, true); - this.pos = 0; - } - } - return this; - } - destroy(): void { - this.destroyed = true; - if (this.salt !== EMPTY_SALT) { - clean(this.salt, this.constants); - } - } - _cloneInto(to?: T): T { - to ||= new (this.constructor as any)() as T; - to.set(...this.get()); - const { buffer, length, finished, destroyed, constants, salt, pos } = this; - to.buffer.set(buffer); - // Clone salt-derived arrays by value so destroying the clone cannot wipe the source instance. - to.constants = constants.slice(); - to.destroyed = destroyed; - to.finished = finished; - to.length = length; - to.pos = pos; - to.salt = salt.slice(); - return to; - } - clone(): T { - return this._cloneInto(); - } - digestInto(out: TArg): void { - aexists(this); - aoutput(out, this); - this.finished = true; - // Padding - const { buffer, blockLen, counterLen, lengthFlag, view } = this; - clean(buffer.subarray(this.pos)); // clean buf - const counter = BigInt((this.length + this.pos) * 8); - const counterPos = blockLen - counterLen - 1; - buffer[this.pos] |= 0b1000_0000; // End block flag - this.length += this.pos; // add unwritten length - // Not enough in buffer for length: write what we have. - if (this.pos > counterPos) { - this.compress(view, 0); - clean(buffer); - this.pos = 0; - } - // Difference with md: here we have lengthFlag! - buffer[counterPos] |= lengthFlag; // Length flag - // We always set 8 byte length flag. Because length will overflow significantly sooner. - view.setBigUint64(blockLen - 8, counter, false); - // Blake1 omits the counter from the extra all-padding block; only the block that still carries - // message bytes mixes in the final bit length. - this.compress(view, 0, this.pos !== 0); - // Write output - clean(buffer); - const v = createView(out); - const state = this.get(); - for (let i = 0; i < this.outputLen / 4; ++i) v.setUint32(i * 4, state[i]); - } - digest(): TRet { - const { buffer, outputLen } = this; - this.digestInto(buffer); - // Return a copy so callers do not alias the instance scratch buffer used during finalization. - const res = buffer.slice(0, outputLen); - this.destroy(); - return res as TRet; - } -} - -// Blake1-512 / Blake1-384 constant table `C512`. -// Stored as sixteen 64-bit constants split into `[high32, low32]` halves so -// the Blake1-64 path can reuse one layout for both `v8..v15` initialization -// and the permuted constant lookups. -const B64C = /* @__PURE__ */ Uint32Array.from([ - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b, 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, - 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, -]); -// Blake1-256 / Blake1-224 constant table `C256`, derived as the first half of `C512`. -const B32C = /* @__PURE__ */ B64C.slice(0, 16); - -// Blake1-256 IV cloned from SHA-256. -const B256_IV = /* @__PURE__ */ SHA256_IV.slice(); -// Blake1-224 IV cloned from SHA-224. -const B224_IV = /* @__PURE__ */ SHA224_IV.slice(); -// Blake1-384 IV cloned from the SHA-384 high-then-low 32-bit halves. -const B384_IV = /* @__PURE__ */ SHA384_IV.slice(); -// Blake1-512 IV cloned from the SHA-512 high-then-low 32-bit halves. -const B512_IV = /* @__PURE__ */ SHA512_IV.slice(); - -// Precompute the odd/even companion constants used by all 14 Blake1-32 rounds. -// Each pair stores `u[sigma[2i + 1]]` then `u[sigma[2i]]`, matching the `G1s` / `G2s` xor order. -function generateTBL256() { - const TBL = []; - for (let i = 0, j = 0; i < 14; i++, j += 16) { - for (let offset = 1; offset < 16; offset += 2) { - TBL.push(B32C[BSIGMA[j + offset]]); - TBL.push(B32C[BSIGMA[j + offset - 1]]); - } - } - return new Uint32Array(TBL); -} -// Full 14-round companion-constant table for Blake1-32. -const TBL256 = /* @__PURE__ */ generateTBL256(); - -// Shared synchronous message-word scratch for the 32-bit Blake1 path. -const BLAKE256_W = /* @__PURE__ */ new Uint32Array(16); - -class BLAKE1_32B extends BLAKE1 { - private v0: number; - private v1: number; - private v2: number; - private v3: number; - private v4: number; - private v5: number; - private v6: number; - private v7: number; - constructor(outputLen: number, IV: Uint32Array, lengthFlag: number, opts: BlakeOpts = {}) { - super(64, outputLen, lengthFlag, 8, 4, B32C, opts); - this.v0 = IV[0] | 0; - this.v1 = IV[1] | 0; - this.v2 = IV[2] | 0; - this.v3 = IV[3] | 0; - this.v4 = IV[4] | 0; - this.v5 = IV[5] | 0; - this.v6 = IV[6] | 0; - this.v7 = IV[7] | 0; - } - protected get(): [number, number, number, number, number, number, number, number] { - const { v0, v1, v2, v3, v4, v5, v6, v7 } = this; - return [v0, v1, v2, v3, v4, v5, v6, v7]; - } - // prettier-ignore - protected set( - v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number - ): void { - this.v0 = v0 | 0; - this.v1 = v1 | 0; - this.v2 = v2 | 0; - this.v3 = v3 | 0; - this.v4 = v4 | 0; - this.v5 = v5 | 0; - this.v6 = v6 | 0; - this.v7 = v7 | 0; - } - destroy(): void { - super.destroy(); - this.set(0, 0, 0, 0, 0, 0, 0, 0); - } - compress(view: DataView, offset: number, withLength = true): void { - for (let i = 0; i < 16; i++, offset += 4) BLAKE256_W[i] = view.getUint32(offset, false); - // Cannot reuse blake2s compress: Blake1 mixes each message word with the companion constants - // precomputed in `TBL256`, rather than using the raw schedule words directly. - let v00 = this.v0 | 0; - let v01 = this.v1 | 0; - let v02 = this.v2 | 0; - let v03 = this.v3 | 0; - let v04 = this.v4 | 0; - let v05 = this.v5 | 0; - let v06 = this.v6 | 0; - let v07 = this.v7 | 0; - let v08 = this.constants[0] | 0; - let v09 = this.constants[1] | 0; - let v10 = this.constants[2] | 0; - let v11 = this.constants[3] | 0; - // Blake1-32 injects the 64-bit bit counter as `[t0, t0, t1, t1]` across `v12..v15`; the - // final all-padding block passes `withLength = false`, leaving these lanes as raw constants. - const { h, l } = u64.fromBig(BigInt(withLength ? this.length * 8 : 0)); - let v12 = (this.constants[4] ^ l) >>> 0; - let v13 = (this.constants[5] ^ l) >>> 0; - let v14 = (this.constants[6] ^ h) >>> 0; - let v15 = (this.constants[7] ^ h) >>> 0; - // prettier-ignore - for (let i = 0, k = 0, j = 0; i < 14; i++) { - ({ a: v00, b: v04, c: v08, d: v12 } = G1s(v00, v04, v08, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v00, b: v04, c: v08, d: v12 } = G2s(v00, v04, v08, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v01, b: v05, c: v09, d: v13 } = G1s(v01, v05, v09, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v01, b: v05, c: v09, d: v13 } = G2s(v01, v05, v09, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v02, b: v06, c: v10, d: v14 } = G1s(v02, v06, v10, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v02, b: v06, c: v10, d: v14 } = G2s(v02, v06, v10, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v03, b: v07, c: v11, d: v15 } = G1s(v03, v07, v11, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v03, b: v07, c: v11, d: v15 } = G2s(v03, v07, v11, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v00, b: v05, c: v10, d: v15 } = G1s(v00, v05, v10, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v00, b: v05, c: v10, d: v15 } = G2s(v00, v05, v10, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v01, b: v06, c: v11, d: v12 } = G1s(v01, v06, v11, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v01, b: v06, c: v11, d: v12 } = G2s(v01, v06, v11, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v02, b: v07, c: v08, d: v13 } = G1s(v02, v07, v08, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v02, b: v07, c: v08, d: v13 } = G2s(v02, v07, v08, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v03, b: v04, c: v09, d: v14 } = G1s(v03, v04, v09, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - ({ a: v03, b: v04, c: v09, d: v14 } = G2s(v03, v04, v09, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++])); - } - this.v0 = (this.v0 ^ v00 ^ v08 ^ this.salt[0]) >>> 0; - this.v1 = (this.v1 ^ v01 ^ v09 ^ this.salt[1]) >>> 0; - this.v2 = (this.v2 ^ v02 ^ v10 ^ this.salt[2]) >>> 0; - this.v3 = (this.v3 ^ v03 ^ v11 ^ this.salt[3]) >>> 0; - this.v4 = (this.v4 ^ v04 ^ v12 ^ this.salt[0]) >>> 0; - this.v5 = (this.v5 ^ v05 ^ v13 ^ this.salt[1]) >>> 0; - this.v6 = (this.v6 ^ v06 ^ v14 ^ this.salt[2]) >>> 0; - this.v7 = (this.v7 ^ v07 ^ v15 ^ this.salt[3]) >>> 0; - clean(BLAKE256_W); - } -} - -// Shared Blake1-64 work vector storing 16 working words as adjacent high/low 32-bit halves. -const BBUF = /* @__PURE__ */ new Uint32Array(32); -// Shared synchronous message-word scratch for the 64-bit Blake1 path. -const BLAKE512_W = /* @__PURE__ */ new Uint32Array(32); - -// Precompute the high/low companion constants used by all 16 Blake1-64 rounds. -// Each quartet stores `u[sigma[2i + 1]]` high/low halves, then `u[sigma[2i]]` high/low halves. -function generateTBL512() { - const TBL = []; - for (let r = 0, k = 0; r < 16; r++, k += 16) { - for (let offset = 1; offset < 16; offset += 2) { - TBL.push(B64C[BSIGMA[k + offset] * 2 + 0]); - TBL.push(B64C[BSIGMA[k + offset] * 2 + 1]); - TBL.push(B64C[BSIGMA[k + offset - 1] * 2 + 0]); - TBL.push(B64C[BSIGMA[k + offset - 1] * 2 + 1]); - } - } - return new Uint32Array(TBL); -} -// Full 16-round companion-constant table as high/low halves. -const TBL512 = /* @__PURE__ */ generateTBL512(); - -// Blake1-64 first half-round with rotations `32` and `25`; `k` is the half-call schedule index. -function G1b(a: number, b: number, c: number, d: number, msg: TArg, k: number) { - const Xpos = 2 * BSIGMA[k]; - const Xl = msg[Xpos + 1] ^ TBL512[k * 2 + 1], Xh = msg[Xpos] ^ TBL512[k * 2]; // prettier-ignore - let Al = BBUF[2 * a + 1], Ah = BBUF[2 * a]; // prettier-ignore - let Bl = BBUF[2 * b + 1], Bh = BBUF[2 * b]; // prettier-ignore - let Cl = BBUF[2 * c + 1], Ch = BBUF[2 * c]; // prettier-ignore - let Dl = BBUF[2 * d + 1], Dh = BBUF[2 * d]; // prettier-ignore - // v[a] = (v[a] + v[b] + x) | 0; - let ll = u64.add3L(Al, Bl, Xl); - Ah = u64.add3H(ll, Ah, Bh, Xh) >>> 0; - Al = (ll | 0) >>> 0; - // v[d] = rotr(v[d] ^ v[a], 32) - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) }); - // v[c] = (v[c] + v[d]) | 0; - ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); - // v[b] = rotr(v[b] ^ v[c], 25) - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 25), Bl: u64.rotrSL(Bh, Bl, 25) }); - ((BBUF[2 * a + 1] = Al), (BBUF[2 * a] = Ah)); - ((BBUF[2 * b + 1] = Bl), (BBUF[2 * b] = Bh)); - ((BBUF[2 * c + 1] = Cl), (BBUF[2 * c] = Ch)); - ((BBUF[2 * d + 1] = Dl), (BBUF[2 * d] = Dh)); -} - -// Blake1-64 second half-round with rotations `16` and `11`; `k` is the half-call schedule index. -function G2b(a: number, b: number, c: number, d: number, msg: TArg, k: number) { - const Xpos = 2 * BSIGMA[k]; - const Xl = msg[Xpos + 1] ^ TBL512[k * 2 + 1], Xh = msg[Xpos] ^ TBL512[k * 2]; // prettier-ignore - let Al = BBUF[2 * a + 1], Ah = BBUF[2 * a]; // prettier-ignore - let Bl = BBUF[2 * b + 1], Bh = BBUF[2 * b]; // prettier-ignore - let Cl = BBUF[2 * c + 1], Ch = BBUF[2 * c]; // prettier-ignore - let Dl = BBUF[2 * d + 1], Dh = BBUF[2 * d]; // prettier-ignore - // v[a] = (v[a] + v[b] + x) | 0; - let ll = u64.add3L(Al, Bl, Xl); - Ah = u64.add3H(ll, Ah, Bh, Xh); - Al = ll | 0; - // v[d] = rotr(v[d] ^ v[a], 16) - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) }); - // v[c] = (v[c] + v[d]) | 0; - ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); - // v[b] = rotr(v[b] ^ v[c], 11) - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 11), Bl: u64.rotrSL(Bh, Bl, 11) }); - ((BBUF[2 * a + 1] = Al), (BBUF[2 * a] = Ah)); - ((BBUF[2 * b + 1] = Bl), (BBUF[2 * b] = Bh)); - ((BBUF[2 * c + 1] = Cl), (BBUF[2 * c] = Ch)); - ((BBUF[2 * d + 1] = Dl), (BBUF[2 * d] = Dh)); -} - -// Legacy field names keep the local `l/h` spelling, but array/state order stays `[high, low]` to -// match the IV tables and `BBUF` layout. -class BLAKE1_64B extends BLAKE1 { - private v0l: number; - private v0h: number; - private v1l: number; - private v1h: number; - private v2l: number; - private v2h: number; - private v3l: number; - private v3h: number; - private v4l: number; - private v4h: number; - private v5l: number; - private v5h: number; - private v6l: number; - private v6h: number; - private v7l: number; - private v7h: number; - constructor(outputLen: number, IV: Uint32Array, lengthFlag: number, opts: BlakeOpts = {}) { - super(128, outputLen, lengthFlag, 16, 8, B64C, opts); - this.v0l = IV[0] | 0; - this.v0h = IV[1] | 0; - this.v1l = IV[2] | 0; - this.v1h = IV[3] | 0; - this.v2l = IV[4] | 0; - this.v2h = IV[5] | 0; - this.v3l = IV[6] | 0; - this.v3h = IV[7] | 0; - this.v4l = IV[8] | 0; - this.v4h = IV[9] | 0; - this.v5l = IV[10] | 0; - this.v5h = IV[11] | 0; - this.v6l = IV[12] | 0; - this.v6h = IV[13] | 0; - this.v7l = IV[14] | 0; - this.v7h = IV[15] | 0; - } - // prettier-ignore - protected get(): [ - number, number, number, number, number, number, number, number, - number, number, number, number, number, number, number, number - ] { - let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this; - return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h]; - } - // prettier-ignore - protected set( - v0l: number, v0h: number, v1l: number, v1h: number, - v2l: number, v2h: number, v3l: number, v3h: number, - v4l: number, v4h: number, v5l: number, v5h: number, - v6l: number, v6h: number, v7l: number, v7h: number - ): void { - this.v0l = v0l | 0; - this.v0h = v0h | 0; - this.v1l = v1l | 0; - this.v1h = v1h | 0; - this.v2l = v2l | 0; - this.v2h = v2h | 0; - this.v3l = v3l | 0; - this.v3h = v3h | 0; - this.v4l = v4l | 0; - this.v4h = v4h | 0; - this.v5l = v5l | 0; - this.v5h = v5h | 0; - this.v6l = v6l | 0; - this.v6h = v6h | 0; - this.v7l = v7l | 0; - this.v7h = v7h | 0; - } - destroy(): void { - super.destroy(); - this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } - compress(view: DataView, offset: number, withLength = true): void { - for (let i = 0; i < 32; i++, offset += 4) BLAKE512_W[i] = view.getUint32(offset, false); - - this.get().forEach((v, i) => (BBUF[i] = v)); // First half from state. - BBUF.set(this.constants.subarray(0, 16), 16); - if (withLength) { - // Blake1-64 injects the 64-bit bit counter into `v12` and `v13`; the final all-padding - // block passes `withLength = false`, leaving the trailing constant lanes untouched. - const { h, l } = u64.fromBig(BigInt(this.length * 8)); - BBUF[24] = (BBUF[24] ^ h) >>> 0; - BBUF[25] = (BBUF[25] ^ l) >>> 0; - BBUF[26] = (BBUF[26] ^ h) >>> 0; - BBUF[27] = (BBUF[27] ^ l) >>> 0; - } - for (let i = 0, k = 0; i < 16; i++) { - G1b(0, 4, 8, 12, BLAKE512_W, k++); - G2b(0, 4, 8, 12, BLAKE512_W, k++); - G1b(1, 5, 9, 13, BLAKE512_W, k++); - G2b(1, 5, 9, 13, BLAKE512_W, k++); - G1b(2, 6, 10, 14, BLAKE512_W, k++); - G2b(2, 6, 10, 14, BLAKE512_W, k++); - G1b(3, 7, 11, 15, BLAKE512_W, k++); - G2b(3, 7, 11, 15, BLAKE512_W, k++); - - G1b(0, 5, 10, 15, BLAKE512_W, k++); - G2b(0, 5, 10, 15, BLAKE512_W, k++); - G1b(1, 6, 11, 12, BLAKE512_W, k++); - G2b(1, 6, 11, 12, BLAKE512_W, k++); - G1b(2, 7, 8, 13, BLAKE512_W, k++); - G2b(2, 7, 8, 13, BLAKE512_W, k++); - G1b(3, 4, 9, 14, BLAKE512_W, k++); - G2b(3, 4, 9, 14, BLAKE512_W, k++); - } - this.v0l ^= BBUF[0] ^ BBUF[16] ^ this.salt[0]; - this.v0h ^= BBUF[1] ^ BBUF[17] ^ this.salt[1]; - this.v1l ^= BBUF[2] ^ BBUF[18] ^ this.salt[2]; - this.v1h ^= BBUF[3] ^ BBUF[19] ^ this.salt[3]; - this.v2l ^= BBUF[4] ^ BBUF[20] ^ this.salt[4]; - this.v2h ^= BBUF[5] ^ BBUF[21] ^ this.salt[5]; - this.v3l ^= BBUF[6] ^ BBUF[22] ^ this.salt[6]; - this.v3h ^= BBUF[7] ^ BBUF[23] ^ this.salt[7]; - this.v4l ^= BBUF[8] ^ BBUF[24] ^ this.salt[0]; - this.v4h ^= BBUF[9] ^ BBUF[25] ^ this.salt[1]; - this.v5l ^= BBUF[10] ^ BBUF[26] ^ this.salt[2]; - this.v5h ^= BBUF[11] ^ BBUF[27] ^ this.salt[3]; - this.v6l ^= BBUF[12] ^ BBUF[28] ^ this.salt[4]; - this.v6h ^= BBUF[13] ^ BBUF[29] ^ this.salt[5]; - this.v7l ^= BBUF[14] ^ BBUF[30] ^ this.salt[6]; - this.v7h ^= BBUF[15] ^ BBUF[31] ^ this.salt[7]; - clean(BBUF, BLAKE512_W); - } -} - -/** Internal blake1-224 hash class. */ -export class _BLAKE224 extends BLAKE1_32B { - constructor(opts: BlakeOpts = {}) { - super(28, B224_IV, 0b0000_0000, opts); - } -} -/** Internal blake1-256 hash class. */ -export class _BLAKE256 extends BLAKE1_32B { - constructor(opts: BlakeOpts = {}) { - super(32, B256_IV, 0b0000_0001, opts); - } -} -/** Internal blake1-384 hash class. */ -export class _BLAKE384 extends BLAKE1_64B { - constructor(opts: BlakeOpts = {}) { - super(48, B384_IV, 0b0000_0000, opts); - } -} -/** Internal blake1-512 hash class. */ -export class _BLAKE512 extends BLAKE1_64B { - constructor(opts: BlakeOpts = {}) { - super(64, B512_IV, 0b0000_0001, opts); - } -} -/** - * Blake1-224 hash function. - * @param msg - message bytes to hash - * @param opts - Optional Blake1 settings. See {@link BlakeOpts}. If set, - * `opts.salt` must be exactly 16 bytes. - * @returns Digest bytes. - * @example - * Hash a message with Blake1-224. - * ```ts - * blake224(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake224: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE224(opts) -); -/** - * Blake1-256 hash function. - * @param msg - message bytes to hash - * @param opts - Optional Blake1 settings. See {@link BlakeOpts}. If set, - * `opts.salt` must be exactly 16 bytes. - * @returns Digest bytes. - * @example - * Hash a message with Blake1-256. - * ```ts - * blake256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake256: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE256(opts) -); -/** - * Blake1-384 hash function. - * @param msg - message bytes to hash - * @param opts - Optional Blake1 settings. See {@link BlakeOpts}. If set, - * `opts.salt` must be exactly 32 bytes. - * @returns Digest bytes. - * @example - * Hash a message with Blake1-384. - * ```ts - * blake384(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake384: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE384(opts) -); -/** - * Blake1-512 hash function. - * @param msg - message bytes to hash - * @param opts - Optional Blake1 settings. See {@link BlakeOpts}. If set, - * `opts.salt` must be exactly 32 bytes. - * @returns Digest bytes. - * @example - * Hash a message with Blake1-512. - * ```ts - * blake512(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake512: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE512(opts) -); diff --git a/packages/noble-hashes/src/blake2.ts b/packages/noble-hashes/src/blake2.ts deleted file mode 100644 index 05da5e16..00000000 --- a/packages/noble-hashes/src/blake2.ts +++ /dev/null @@ -1,581 +0,0 @@ -/** - * blake2b (64-bit) & blake2s (8 to 32-bit) hash functions. - * b could have been faster, but there is no fast u64 in js, so s is 1.5x faster. - * @module - */ -import { BSIGMA, G1s, G2s } from './_blake'; -import { SHA256_IV } from './_md'; -import * as u64 from './_u64'; -// prettier-ignore -import { - abytes, aexists, anumber, aoutput, - type CHash, - clean, createHasher, - type Hash, - swap8IfBE, - swap32IfBE, type TArg, - type TRet, - u32} from './utils'; - -/** - * Blake hash options. - * `dkLen` is output length. `key` is used in MAC mode. `salt` is used in - * KDF mode. - */ -export type Blake2Opts = { - /** Desired digest length in bytes. RFC 7693 uses 1..64 for blake2b and 1..32 for blake2s. */ - dkLen?: number; - /** Optional MAC key. */ - key?: Uint8Array; - /** Optional salt mixed into initialization. */ - salt?: Uint8Array; - /** Optional personalization bytes. */ - personalization?: Uint8Array; -}; - -// Same IV words as `SHA512_IV`, but endian-swapped into LE u32 low/high halves -// for the BLAKE2b u64 helpers below. -const B2B_IV = /* @__PURE__ */ Uint32Array.from([ - 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a, - 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19, -]); -// Shared synchronous BLAKE2b work vector as LE u32 low/high halves. -const BBUF = /* @__PURE__ */ new Uint32Array(32); - -// BLAKE2b G mix split into two half-rounds over LE u32 low/high limbs. -function G1b(a: number, b: number, c: number, d: number, msg: TArg, x: number) { - // NOTE: V is LE here - const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore - let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore - let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore - let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore - let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore - // v[a] = (v[a] + v[b] + x) | 0; - let ll = u64.add3L(Al, Bl, Xl); - Ah = u64.add3H(ll, Ah, Bh, Xh); - Al = ll | 0; - // v[d] = rotr(v[d] ^ v[a], 32) - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) }); - // v[c] = (v[c] + v[d]) | 0; - ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); - // v[b] = rotr(v[b] ^ v[c], 24) - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) }); - ((BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah)); - ((BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh)); - ((BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch)); - ((BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh)); -} - -// Second half-round of the same LE-limb BLAKE2b G mix; `x` is the message word offset. -function G2b(a: number, b: number, c: number, d: number, msg: TArg, x: number) { - // NOTE: V is LE here - const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore - let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore - let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore - let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore - let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore - // v[a] = (v[a] + v[b] + x) | 0; - let ll = u64.add3L(Al, Bl, Xl); - Ah = u64.add3H(ll, Ah, Bh, Xh); - Al = ll | 0; - // v[d] = rotr(v[d] ^ v[a], 16) - ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); - ({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) }); - // v[c] = (v[c] + v[d]) | 0; - ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); - // v[b] = rotr(v[b] ^ v[c], 63) - ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); - ({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) }); - ((BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah)); - ((BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh)); - ((BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch)); - ((BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh)); -} - -function checkBlake2Opts( - outputLen: number, - opts: TArg = {}, - keyLen: number, - saltLen: number, - persLen: number -) { - anumber(keyLen); - // RFC 7693 §2.1 requires digest length nn in 1..keyLen. - if (outputLen <= 0 || outputLen > keyLen) throw new Error('outputLen bigger than keyLen'); - const { key, salt, personalization } = opts; - // This API uses `undefined` for the RFC 7693 `kk = 0` case, so a provided key must be non-empty. - if (key !== undefined && (key.length < 1 || key.length > keyLen)) - throw new Error('"key" expected to be undefined or of length=1..' + keyLen); - if (salt !== undefined) abytes(salt, saltLen, 'salt'); - if (personalization !== undefined) abytes(personalization, persLen, 'personalization'); -} - -/** Internal base class for BLAKE2. */ -export abstract class _BLAKE2> implements Hash { - protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void; - protected abstract get(): number[]; - protected abstract set(...args: number[]): void; - abstract destroy(): void; - protected buffer: Uint8Array; - protected buffer32: Uint32Array; - protected finished = false; - protected destroyed = false; - protected length: number = 0; - protected pos: number = 0; - readonly blockLen: number; - readonly outputLen: number; - readonly canXOF: boolean = false; - - constructor(blockLen: number, outputLen: number) { - anumber(blockLen); - anumber(outputLen); - this.blockLen = blockLen; - this.outputLen = outputLen; - this.buffer = new Uint8Array(blockLen); - this.buffer32 = u32(this.buffer); - } - update(data: TArg): this { - aexists(this); - abytes(data); - // Main difference with other hashes: there is flag for last block, - // so we cannot process current block before we know that there - // is the next one. This significantly complicates logic and reduces ability - // to do zero-copy processing - const { blockLen, buffer, buffer32 } = this; - const len = data.length; - const offset = data.byteOffset; - const buf = data.buffer; - for (let pos = 0; pos < len; ) { - // If buffer is full and we still have input (don't process last block, same as blake2s) - if (this.pos === blockLen) { - swap32IfBE(buffer32); - this.compress(buffer32, 0, false); - swap32IfBE(buffer32); - this.pos = 0; - } - const take = Math.min(blockLen - this.pos, len - pos); - const dataOffset = offset + pos; - // Zero-copy only for full, 4-byte-aligned, non-final blocks. - if (take === blockLen && !(dataOffset % 4) && pos + take < len) { - const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4)); - swap32IfBE(data32); - for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) { - this.length += blockLen; - this.compress(data32, pos32, false); - } - swap32IfBE(data32); - continue; - } - buffer.set(data.subarray(pos, pos + take), this.pos); - this.pos += take; - this.length += take; - pos += take; - } - return this; - } - digestInto(out: TArg): void { - aexists(this); - aoutput(out, this); - const { pos, buffer32 } = this; - this.finished = true; - // Padding - clean(this.buffer.subarray(pos)); - swap32IfBE(buffer32); - this.compress(buffer32, 0, true); - swap32IfBE(buffer32); - // Reject unaligned views explicitly instead of hiding them behind a full scratch copy. - if (out.byteOffset & 3) - throw new RangeError( - '"digestInto() output" expected 4-byte aligned byteOffset, got ' + out.byteOffset - ); - const state = this.get(); - const out32 = u32(out); - const full = Math.floor(this.outputLen / 4); - for (let i = 0; i < full; i++) out32[i] = swap8IfBE(state[i]); - const tail = this.outputLen % 4; - if (!tail) return; - const off = full * 4; - const word = state[full]; - for (let i = 0; i < tail; i++) out[off + i] = word >>> (8 * i); - } - digest(): TRet { - const { buffer, outputLen } = this; - this.digestInto(buffer); - // Return a copy so callers do not alias the instance scratch buffer used during finalization. - const res = buffer.slice(0, outputLen); - this.destroy(); - return res as TRet; - } - _cloneInto(to?: T): T { - const { buffer, length, finished, destroyed, outputLen, pos } = this; - // Recreate only `dkLen`; key/salt/personalization are already absorbed into the copied state. - to ||= new (this.constructor as any)({ dkLen: outputLen }) as T; - to.set(...this.get()); - to.buffer.set(buffer); - to.destroyed = destroyed; - to.finished = finished; - to.length = length; - to.pos = pos; - // @ts-ignore - to.outputLen = outputLen; - return to; - } - clone(): T { - return this._cloneInto(); - } -} - -/** Internal blake2b hash class with state stored as LE u32 low/high halves. */ -export class _BLAKE2b extends _BLAKE2<_BLAKE2b> { - // Same IV words as SHA-512 / BLAKE2b, encoded as LE u32 low/high halves. - private v0l = B2B_IV[0] | 0; - private v0h = B2B_IV[1] | 0; - private v1l = B2B_IV[2] | 0; - private v1h = B2B_IV[3] | 0; - private v2l = B2B_IV[4] | 0; - private v2h = B2B_IV[5] | 0; - private v3l = B2B_IV[6] | 0; - private v3h = B2B_IV[7] | 0; - private v4l = B2B_IV[8] | 0; - private v4h = B2B_IV[9] | 0; - private v5l = B2B_IV[10] | 0; - private v5h = B2B_IV[11] | 0; - private v6l = B2B_IV[12] | 0; - private v6h = B2B_IV[13] | 0; - private v7l = B2B_IV[14] | 0; - private v7h = B2B_IV[15] | 0; - - constructor(opts: Blake2Opts = {}) { - const olen = opts.dkLen === undefined ? 64 : opts.dkLen; - super(128, olen); - checkBlake2Opts(olen, opts, 64, 16, 16); - let { key, personalization, salt } = opts; - let keyLength = 0; - if (key !== undefined) { - abytes(key, undefined, 'key'); - keyLength = key.length; - } - // RFC 7693 §2.5: xor `p[0] = 0x0101kknn` into the low 32 bits of `h[0]`; - // the high 32 bits stay at `IV[0]`. - this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24); - if (salt !== undefined) { - abytes(salt, undefined, 'salt'); - const slt = u32(salt); - this.v4l ^= swap8IfBE(slt[0]); - this.v4h ^= swap8IfBE(slt[1]); - this.v5l ^= swap8IfBE(slt[2]); - this.v5h ^= swap8IfBE(slt[3]); - } - if (personalization !== undefined) { - abytes(personalization, undefined, 'personalization'); - const pers = u32(personalization); - this.v6l ^= swap8IfBE(pers[0]); - this.v6h ^= swap8IfBE(pers[1]); - this.v7l ^= swap8IfBE(pers[2]); - this.v7h ^= swap8IfBE(pers[3]); - } - if (key !== undefined) { - // Pad to blockLen and update - const tmp = new Uint8Array(this.blockLen); - tmp.set(key); - this.update(tmp); - } - } - // prettier-ignore - protected get(): [ - number, number, number, number, number, number, number, number, - number, number, number, number, number, number, number, number - ] { - let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this; - return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h]; - } - // prettier-ignore - protected set( - v0l: number, v0h: number, v1l: number, v1h: number, - v2l: number, v2h: number, v3l: number, v3h: number, - v4l: number, v4h: number, v5l: number, v5h: number, - v6l: number, v6h: number, v7l: number, v7h: number - ): void { - this.v0l = v0l | 0; - this.v0h = v0h | 0; - this.v1l = v1l | 0; - this.v1h = v1h | 0; - this.v2l = v2l | 0; - this.v2h = v2h | 0; - this.v3l = v3l | 0; - this.v3h = v3h | 0; - this.v4l = v4l | 0; - this.v4h = v4h | 0; - this.v5l = v5l | 0; - this.v5h = v5h | 0; - this.v6l = v6l | 0; - this.v6h = v6h | 0; - this.v7l = v7l | 0; - this.v7h = v7h | 0; - } - protected compress(msg: Uint32Array, offset: number, isLast: boolean): void { - this.get().forEach((v, i) => (BBUF[i] = v)); // First half from state. - BBUF.set(B2B_IV, 16); // Second half from IV. - let { h, l } = u64.fromBig(BigInt(this.length)); - BBUF[24] = B2B_IV[8] ^ l; // Low word of the offset. - BBUF[25] = B2B_IV[9] ^ h; // High word. - // Invert all bits for last block - if (isLast) { - BBUF[28] = ~BBUF[28]; - BBUF[29] = ~BBUF[29]; - } - let j = 0; - const s = BSIGMA; - // SIGMA selects 64-bit message words; multiply by 2 because `msg` stores - // each word as [low32, high32]. - for (let i = 0; i < 12; i++) { - G1b(0, 4, 8, 12, msg, offset + 2 * s[j++]); - G2b(0, 4, 8, 12, msg, offset + 2 * s[j++]); - G1b(1, 5, 9, 13, msg, offset + 2 * s[j++]); - G2b(1, 5, 9, 13, msg, offset + 2 * s[j++]); - G1b(2, 6, 10, 14, msg, offset + 2 * s[j++]); - G2b(2, 6, 10, 14, msg, offset + 2 * s[j++]); - G1b(3, 7, 11, 15, msg, offset + 2 * s[j++]); - G2b(3, 7, 11, 15, msg, offset + 2 * s[j++]); - - G1b(0, 5, 10, 15, msg, offset + 2 * s[j++]); - G2b(0, 5, 10, 15, msg, offset + 2 * s[j++]); - G1b(1, 6, 11, 12, msg, offset + 2 * s[j++]); - G2b(1, 6, 11, 12, msg, offset + 2 * s[j++]); - G1b(2, 7, 8, 13, msg, offset + 2 * s[j++]); - G2b(2, 7, 8, 13, msg, offset + 2 * s[j++]); - G1b(3, 4, 9, 14, msg, offset + 2 * s[j++]); - G2b(3, 4, 9, 14, msg, offset + 2 * s[j++]); - } - this.v0l ^= BBUF[0] ^ BBUF[16]; - this.v0h ^= BBUF[1] ^ BBUF[17]; - this.v1l ^= BBUF[2] ^ BBUF[18]; - this.v1h ^= BBUF[3] ^ BBUF[19]; - this.v2l ^= BBUF[4] ^ BBUF[20]; - this.v2h ^= BBUF[5] ^ BBUF[21]; - this.v3l ^= BBUF[6] ^ BBUF[22]; - this.v3h ^= BBUF[7] ^ BBUF[23]; - this.v4l ^= BBUF[8] ^ BBUF[24]; - this.v4h ^= BBUF[9] ^ BBUF[25]; - this.v5l ^= BBUF[10] ^ BBUF[26]; - this.v5h ^= BBUF[11] ^ BBUF[27]; - this.v6l ^= BBUF[12] ^ BBUF[28]; - this.v6h ^= BBUF[13] ^ BBUF[29]; - this.v7l ^= BBUF[14] ^ BBUF[30]; - this.v7h ^= BBUF[15] ^ BBUF[31]; - clean(BBUF); - } - destroy(): void { - this.destroyed = true; - clean(this.buffer32); - this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } -} - -/** - * Blake2b hash function. 64-bit. 1.5x slower than blake2s in JS. - * @param msg - message that would be hashed - * @param opts - Optional output, MAC, salt, and personalization settings. - * `dkLen` must be 1..64 bytes; `salt` and `personalization`, if present, - * must be 16 bytes each. See {@link Blake2Opts}. - * @returns Digest bytes. - * @example - * Hash a message with Blake2b. - * ```ts - * blake2b(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake2b: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE2b(opts) -); - -// ================= -// Blake2S -// ================= - -/** Internal type, 16 numbers. */ -// prettier-ignore -export type _Num16 = { - v0: number; v1: number; v2: number; v3: number; - v4: number; v5: number; v6: number; v7: number; - v8: number; v9: number; v10: number; v11: number; - v12: number; v13: number; v14: number; v15: number; -}; - -/** - * BLAKE2-compress core method. - * Runs only the round function over a caller-supplied local vector; callers initialize `v0..v15` - * and apply the final `h[i] ^= v[i] ^ v[i + 8]` fold themselves. - * @param s - flattened sigma schedule bytes - * @param offset - starting word offset inside `msg`, not a byte offset - * @param msg - message words - * @param rounds - round count to execute - * @param v0 - state word 0 - * @param v1 - state word 1 - * @param v2 - state word 2 - * @param v3 - state word 3 - * @param v4 - state word 4 - * @param v5 - state word 5 - * @param v6 - state word 6 - * @param v7 - state word 7 - * @param v8 - state word 8 - * @param v9 - state word 9 - * @param v10 - state word 10 - * @param v11 - state word 11 - * @param v12 - state word 12 - * @param v13 - state word 13 - * @param v14 - state word 14 - * @param v15 - state word 15 - * @returns Updated compression state words. - * @example - * Run the BLAKE2 compression core on zeroed state and message words. - * ```ts - * import { compress } from '@noble/hashes/blake2.js'; - * const state = compress( - * new Uint8Array(16), - * 0, - * new Uint32Array(16), - * 1, - * 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 0, 0, 0, 0, 0, 0 - * ); - * state.v0; - * ``` - */ -// prettier-ignore -export function compress(s: TArg, offset: number, msg: TArg, rounds: number, - v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, - v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, -): _Num16 { - let j = 0; - for (let i = 0; i < rounds; i++) { - ({ a: v0, b: v4, c: v8, d: v12 } = G1s(v0, v4, v8, v12, msg[offset + s[j++]])); - ({ a: v0, b: v4, c: v8, d: v12 } = G2s(v0, v4, v8, v12, msg[offset + s[j++]])); - ({ a: v1, b: v5, c: v9, d: v13 } = G1s(v1, v5, v9, v13, msg[offset + s[j++]])); - ({ a: v1, b: v5, c: v9, d: v13 } = G2s(v1, v5, v9, v13, msg[offset + s[j++]])); - ({ a: v2, b: v6, c: v10, d: v14 } = G1s(v2, v6, v10, v14, msg[offset + s[j++]])); - ({ a: v2, b: v6, c: v10, d: v14 } = G2s(v2, v6, v10, v14, msg[offset + s[j++]])); - ({ a: v3, b: v7, c: v11, d: v15 } = G1s(v3, v7, v11, v15, msg[offset + s[j++]])); - ({ a: v3, b: v7, c: v11, d: v15 } = G2s(v3, v7, v11, v15, msg[offset + s[j++]])); - - ({ a: v0, b: v5, c: v10, d: v15 } = G1s(v0, v5, v10, v15, msg[offset + s[j++]])); - ({ a: v0, b: v5, c: v10, d: v15 } = G2s(v0, v5, v10, v15, msg[offset + s[j++]])); - ({ a: v1, b: v6, c: v11, d: v12 } = G1s(v1, v6, v11, v12, msg[offset + s[j++]])); - ({ a: v1, b: v6, c: v11, d: v12 } = G2s(v1, v6, v11, v12, msg[offset + s[j++]])); - ({ a: v2, b: v7, c: v8, d: v13 } = G1s(v2, v7, v8, v13, msg[offset + s[j++]])); - ({ a: v2, b: v7, c: v8, d: v13 } = G2s(v2, v7, v8, v13, msg[offset + s[j++]])); - ({ a: v3, b: v4, c: v9, d: v14 } = G1s(v3, v4, v9, v14, msg[offset + s[j++]])); - ({ a: v3, b: v4, c: v9, d: v14 } = G2s(v3, v4, v9, v14, msg[offset + s[j++]])); - } - return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 }; -} - -// Blake2s reuses the SHA-256 IV words as-is. -const B2S_IV = /* @__PURE__ */ SHA256_IV.slice(); - -/** Internal blake2s hash class. */ -export class _BLAKE2s extends _BLAKE2<_BLAKE2s> { - // Internal state, same as SHA-256 - private v0 = B2S_IV[0] | 0; - private v1 = B2S_IV[1] | 0; - private v2 = B2S_IV[2] | 0; - private v3 = B2S_IV[3] | 0; - private v4 = B2S_IV[4] | 0; - private v5 = B2S_IV[5] | 0; - private v6 = B2S_IV[6] | 0; - private v7 = B2S_IV[7] | 0; - - constructor(opts: Blake2Opts = {}) { - const olen = opts.dkLen === undefined ? 32 : opts.dkLen; - super(64, olen); - checkBlake2Opts(olen, opts, 32, 8, 8); - let { key, personalization, salt } = opts; - let keyLength = 0; - if (key !== undefined) { - abytes(key, undefined, 'key'); - keyLength = key.length; - } - // RFC 7693 §2.5: xor `p[0] = 0x0101kknn` directly into `h[0]`, since - // BLAKE2s stores each state word as one `u32`. - this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24); - if (salt !== undefined) { - abytes(salt, undefined, 'salt'); - const slt = u32(salt as Uint8Array); - this.v4 ^= swap8IfBE(slt[0]); - this.v5 ^= swap8IfBE(slt[1]); - } - if (personalization !== undefined) { - abytes(personalization, undefined, 'personalization'); - const pers = u32(personalization as Uint8Array); - this.v6 ^= swap8IfBE(pers[0]); - this.v7 ^= swap8IfBE(pers[1]); - } - if (key !== undefined) { - // Pad to blockLen and update - const tmp = new Uint8Array(this.blockLen); - tmp.set(key); - this.update(tmp); - } - } - protected get(): [number, number, number, number, number, number, number, number] { - const { v0, v1, v2, v3, v4, v5, v6, v7 } = this; - return [v0, v1, v2, v3, v4, v5, v6, v7]; - } - // prettier-ignore - protected set( - v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number - ): void { - this.v0 = v0 | 0; - this.v1 = v1 | 0; - this.v2 = v2 | 0; - this.v3 = v3 | 0; - this.v4 = v4 | 0; - this.v5 = v5 | 0; - this.v6 = v6 | 0; - this.v7 = v7 | 0; - } - protected compress(msg: Uint32Array, offset: number, isLast: boolean): void { - const { h, l } = u64.fromBig(BigInt(this.length)); - // Seed v8..v15 from the IV, xor the low/high 32-bit byte counter into - // v12/v13, and invert v14 on the final block. - // prettier-ignore - const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = - compress( - BSIGMA, offset, msg, 10, - this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7, - B2S_IV[0], B2S_IV[1], B2S_IV[2], B2S_IV[3], l ^ B2S_IV[4], h ^ B2S_IV[5], isLast ? ~B2S_IV[6] : B2S_IV[6], B2S_IV[7] - ); - this.v0 ^= v0 ^ v8; - this.v1 ^= v1 ^ v9; - this.v2 ^= v2 ^ v10; - this.v3 ^= v3 ^ v11; - this.v4 ^= v4 ^ v12; - this.v5 ^= v5 ^ v13; - this.v6 ^= v6 ^ v14; - this.v7 ^= v7 ^ v15; - } - destroy(): void { - this.destroyed = true; - clean(this.buffer32); - this.set(0, 0, 0, 0, 0, 0, 0, 0); - } -} - -/** - * Blake2s hash function. Focuses on 8-bit to 32-bit platforms. 1.5x faster than blake2b in JS. - * @param msg - message that would be hashed - * @param opts - Optional output, MAC, salt, and personalization settings. - * `dkLen` must be 1..32 bytes; `salt` and `personalization`, if present, - * must be 8 bytes each. See {@link Blake2Opts}. - * @returns Digest bytes. - * @example - * Hash a message with Blake2s. - * ```ts - * blake2s(new Uint8Array([97, 98, 99])); - * ``` - */ -export const blake2s: TRet> = /* @__PURE__ */ createHasher( - (opts) => new _BLAKE2s(opts) -); diff --git a/packages/noble-hashes/src/blake3.ts b/packages/noble-hashes/src/blake3.ts deleted file mode 100644 index 15e983d6..00000000 --- a/packages/noble-hashes/src/blake3.ts +++ /dev/null @@ -1,317 +0,0 @@ -/** - * Blake3 fast hash is Blake2 with reduced security (round count). Can also be used as MAC & KDF. - * - * It is advertised as "the fastest cryptographic hash". However, it isn't true in JS. - * Why is this so slow? While it must be 6x faster than blake2b, perf diff is only 20%: - * - * * There is only 30% reduction in number of rounds from blake2s - * * Speed-up comes from tree structure, which is parallelized using SIMD & threading. - * These features are not present in JS, so we only get overhead from trees. - * * Parallelization only happens on 1024-byte chunks: there is no benefit for small inputs. - * * It is still possible to make it faster using: a) loop unrolling b) web workers c) wasm - * @module - */ -import { SHA256_IV } from './_md'; -import { fromBig } from './_u64'; -import { _BLAKE2, compress } from './blake2'; -// prettier-ignore -import { - abytes, aexists, anumber, aoutput, - type CHashXOF, - clean, - copyBytes, - createHasher, type HashXOF, - swap32IfBE, - type TArg, - type TRet, - u8, - u32} from './utils'; - -// Constructor-time mode flags (`KEYED_HASH`, `DERIVE_*`) plus per-node tree -// flags (`CHUNK_*`, `PARENT`, `ROOT`). -const B3_Flags = { - CHUNK_START: 0b1, - CHUNK_END: 0b10, - PARENT: 0b100, - ROOT: 0b1000, - KEYED_HASH: 0b10000, - DERIVE_KEY_CONTEXT: 0b100000, - DERIVE_KEY_MATERIAL: 0b1000000, -} as const; - -// Default BLAKE3 IV, cloned from the shared BLAKE2s / SHA-256 IV basis. -const B3_IV = /* @__PURE__ */ SHA256_IV.slice(); - -// Seven 16-word rounds of BLAKE3 message schedule, generated by repeatedly -// permuting the identity row. -const B3_SIGMA: TRet = /* @__PURE__ */ (() => { - const Id = Array.from({ length: 16 }, (_, i) => i); - const permute = (arr: number[]) => - [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]); - const res: number[] = []; - for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v); - return Uint8Array.from(res); -})(); - -/** - * Ensure to use EITHER `key` OR `context`, not both. - * - * * `key`: 32-byte MAC key. - * * `context`: caller-encoded KDF context bytes. If your protocol defines a - * string context, encode it before passing it here. - * A good default format for the original context string is - * "[application] [commit timestamp] [purpose]". - */ -export type Blake3Opts = { - /** Desired digest length in bytes. The BLAKE3 spec allows 0..2^64-1 bytes of output. */ - dkLen?: number; - /** Optional 32-byte MAC key. */ - key?: Uint8Array; - /** Optional KDF context bytes. */ - context?: Uint8Array; -}; - -/** Blake3 hash. Can be used as MAC and KDF with caller-encoded context bytes. */ -export class _BLAKE3 extends _BLAKE2<_BLAKE3> implements HashXOF<_BLAKE3> { - readonly canXOF = true; - private chunkPos = 0; // Position of current block in chunk - // How many chunks we already have; exact while this stays within - // JS's safe-integer range. - private chunksDone = 0; - private flags = 0 | 0; - private IV: Uint32Array; - private state: Uint32Array; - private stack: Uint32Array[] = []; - // Output - private posOut = 0; - private bufferOut32 = new Uint32Array(16); - private bufferOut: Uint8Array; - // Index of output chunk; exact while this stays within JS's - // safe-integer range. - private chunkOut = 0; - private enableXOF = true; - - constructor(opts: Blake3Opts = {}, flags = 0) { - super(64, opts.dkLen === undefined ? 32 : opts.dkLen); - const { key, context } = opts; - const hasContext = context !== undefined; - if (key !== undefined) { - if (hasContext) throw new Error('Only "key" or "context" can be specified at same time'); - abytes(key, 32, 'key'); - const k = copyBytes(key); - this.IV = u32(k); - swap32IfBE(this.IV); - this.flags = flags | B3_Flags.KEYED_HASH; - } else if (hasContext) { - abytes(context, undefined, 'context'); - const ctx = context; - const contextKey = new _BLAKE3({ dkLen: 32 }, B3_Flags.DERIVE_KEY_CONTEXT) - .update(ctx) - .digest(); - this.IV = u32(contextKey); - swap32IfBE(this.IV); - this.flags = flags | B3_Flags.DERIVE_KEY_MATERIAL; - } else { - this.IV = B3_IV.slice(); - this.flags = flags; - } - this.state = this.IV.slice(); - this.bufferOut = u8(this.bufferOut32); - } - // _BLAKE2's scalar-state hooks are unused here: BLAKE3 keeps its tree/XOF state in arrays and - // copies it directly in _cloneInto(). - protected get(): [] { - return []; - } - protected set(): void {} - // Truncated chunk/parent compression: seed v8..v15 as IV[0..3], t0, t1, - // block length, and flags, then keep only the first 8 output words. - private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) { - const { state: s, pos } = this; - const { h, l } = fromBig(BigInt(counter), true); - // prettier-ignore - const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = - compress( - B3_SIGMA, bufPos, buf, 7, - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], - B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], h, l, pos, flags - ); - s[0] = v0 ^ v8; - s[1] = v1 ^ v9; - s[2] = v2 ^ v10; - s[3] = v3 ^ v11; - s[4] = v4 ^ v12; - s[5] = v5 ^ v13; - s[6] = v6 ^ v14; - s[7] = v7 ^ v15; - } - protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false): void { - // Compress last block - let flags = this.flags; - if (!this.chunkPos) flags |= B3_Flags.CHUNK_START; - if (this.chunkPos === 15 || isLast) flags |= B3_Flags.CHUNK_END; - if (!isLast) this.pos = this.blockLen; - this.b2Compress(this.chunksDone, flags, buf, bufPos); - this.chunkPos += 1; - // If current block is last in chunk (16 blocks), then compress chunks - if (this.chunkPos === 16 || isLast) { - let chunk = this.state; - this.state = this.IV.slice(); - // If not the last one, compress only when there are trailing zeros in chunk counter - // Chunks are used as a binary tree where the current stack is the path. - // Zero means the current leaf is finished and can be compressed. - // 1 (001) - leaf not finished (just push current chunk to stack) - // 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back) - // 3 (011) - last leaf not finished - // 4 (100) - leafs finished at depth=1 and depth=2 - for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) { - if (!(last = this.stack.pop())) break; - this.buffer32.set(last, 0); - this.buffer32.set(chunk, 8); - this.pos = this.blockLen; - this.b2Compress(0, this.flags | B3_Flags.PARENT, this.buffer32, 0); - chunk = this.state; - this.state = this.IV.slice(); - } - this.chunksDone++; - this.chunkPos = 0; - this.stack.push(chunk); - } - this.pos = 0; - } - _cloneInto(to?: _BLAKE3): _BLAKE3 { - to = super._cloneInto(to) as _BLAKE3; - const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this; - to.state.set(state.slice()); - // Clone each CV stack entry by value so extending or destroying the clone - // cannot alias the source tree state. - to.stack = stack.map((i) => Uint32Array.from(i)); - to.IV.set(IV); - to.flags = flags; - to.chunkPos = chunkPos; - to.chunksDone = chunksDone; - to.posOut = posOut; - to.chunkOut = chunkOut; - to.enableXOF = this.enableXOF; - to.bufferOut32.set(this.bufferOut32); - return to; - } - destroy(): void { - this.destroyed = true; - clean(this.state, this.buffer32, this.IV, this.bufferOut32); - clean(...this.stack); - } - // Root/XOF compression: rerun the same ROOT inputs with incrementing output - // counter `t` and materialize all 16 output words. - // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8) - private b2CompressOut() { - const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this; - const { h, l } = fromBig(BigInt(this.chunkOut++)); - swap32IfBE(buffer32); - // prettier-ignore - const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = - compress( - B3_SIGMA, 0, buffer32, 7, - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], - B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], l, h, pos, flags - ); - out32[0] = v0 ^ v8; - out32[1] = v1 ^ v9; - out32[2] = v2 ^ v10; - out32[3] = v3 ^ v11; - out32[4] = v4 ^ v12; - out32[5] = v5 ^ v13; - out32[6] = v6 ^ v14; - out32[7] = v7 ^ v15; - out32[8] = s[0] ^ v8; - out32[9] = s[1] ^ v9; - out32[10] = s[2] ^ v10; - out32[11] = s[3] ^ v11; - out32[12] = s[4] ^ v12; - out32[13] = s[5] ^ v13; - out32[14] = s[6] ^ v14; - out32[15] = s[7] ^ v15; - swap32IfBE(buffer32); - swap32IfBE(out32); - this.posOut = 0; - } - protected finish(): void { - if (this.finished) return; - this.finished = true; - // Padding - clean(this.buffer.subarray(this.pos)); - // Process last chunk - let flags = this.flags | B3_Flags.ROOT; - if (this.stack.length) { - // Finalize the current chunk first, then rerun the last parent - // compression as ROOT with t = 0 and b = 64. - flags |= B3_Flags.PARENT; - swap32IfBE(this.buffer32); - this.compress(this.buffer32, 0, true); - swap32IfBE(this.buffer32); - this.chunksDone = 0; - this.pos = this.blockLen; - } else { - flags |= (!this.chunkPos ? B3_Flags.CHUNK_START : 0) | B3_Flags.CHUNK_END; - } - this.flags = flags; - this.b2CompressOut(); - } - private writeInto(out: TArg): TRet { - aexists(this, false); - abytes(out); - this.finish(); - const { blockLen, bufferOut } = this; - for (let pos = 0, len = out.length; pos < len; ) { - if (this.posOut >= blockLen) this.b2CompressOut(); - const take = Math.min(blockLen - this.posOut, len - pos); - out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); - this.posOut += take; - pos += take; - } - return out as TRet; - } - xofInto(out: TArg): TRet { - if (!this.enableXOF) throw new Error('XOF is not possible after digest call'); - return this.writeInto(out); - } - xof(bytes: number): TRet { - anumber(bytes); - return this.xofInto(new Uint8Array(bytes)); - } - digestInto(out: TArg): void { - aoutput(out, this); - if (this.finished) throw new Error('digest() was already called'); - this.enableXOF = false; - // `aoutput(...)` allows oversized buffers; digestInto() must fill only the configured digest. - this.writeInto(out.subarray(0, this.outputLen)); - this.destroy(); - } - digest(): TRet { - const out = new Uint8Array(this.outputLen); - this.digestInto(out); - return out as TRet; - } -} - -/** - * BLAKE3 hash function. Can be used as MAC and KDF. - * @param msg - message that would be hashed - * @param opts - Optional output, MAC, or KDF configuration. `key` must be - * exactly 32 bytes, `context` is caller-encoded bytes, and `dkLen` can be - * 0..2^64-1 via the XOF-backed output path. See {@link Blake3Opts}. - * @returns Digest bytes. - * @example - * Hash, MAC, or derive key material with BLAKE3. - * ```ts - * import { blake3 } from '@noble/hashes/blake3.js'; - * import { utf8ToBytes } from '@noble/hashes/utils.js'; - * const data = new Uint8Array(32); - * const hash = blake3(data); - * const mac = blake3(data, { key: new Uint8Array(32) }); - * const kdf = blake3(data, { context: utf8ToBytes('application name') }); - * ``` - */ -export const blake3: TRet> = /* @__PURE__ */ createHasher( - (opts = {}) => new _BLAKE3(opts) -); diff --git a/packages/noble-hashes/src/eskdf.ts b/packages/noble-hashes/src/eskdf.ts deleted file mode 100644 index b80edecb..00000000 --- a/packages/noble-hashes/src/eskdf.ts +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Experimental KDF for AES. - * @module - */ -import { hkdf } from './hkdf'; -import { pbkdf2 as _pbkdf2 } from './pbkdf2'; -import { scrypt as _scrypt } from './scrypt'; -import { sha256 } from './sha2'; -import { - abytes, - bytesToHex, - clean, - createView, - hexToBytes, - kdfInputToBytes, - type TArg, - type TRet, -} from './utils'; - -// A tiny KDF for various applications like AES key-gen. -// Uses HKDF in a non-standard way, so it's not "KDF-secure", only "PRF-secure". -// Which is good enough: assume sha2-256 retained preimage resistance. - -// Fixed ESKDF scrypt work factor: interactive-latency target with about 512 MiB RAM per derivation. -const SCRYPT_FACTOR = /* @__PURE__ */ (() => 2 ** 19)(); -// Fixed ESKDF PBKDF2 work factor: CPU-only companion branch in the same rough -// interactive-latency range. -const PBKDF2_FACTOR = /* @__PURE__ */ (() => 2 ** 17)(); - -/** - * Scrypt KDF with the fixed ESKDF policy tuple `{ N: 2^19, r: 8, p: 1, dkLen: 32 }`. - * @param password - user password string, UTF-8 encoded before entering RFC 7914 - * @param salt - unique salt string, UTF-8 encoded before entering RFC 7914 - * @returns Derived 32-byte key. - * @example - * Derive the 32-byte scrypt key used by ESKDF. - * ```ts - * scrypt('password123', 'user@example.com'); - * ``` - */ -export function scrypt(password: string, salt: string): TRet { - return _scrypt(password, salt, { N: SCRYPT_FACTOR, r: 8, p: 1, dkLen: 32 }); -} - -/** - * PBKDF2-HMAC-SHA256 with the fixed ESKDF policy tuple `{ sha256, c: 2^17, dkLen: 32 }`. - * @param password - user password string, UTF-8 encoded before entering PBKDF2-HMAC-SHA-256 - * @param salt - unique salt string, UTF-8 encoded before entering PBKDF2-HMAC-SHA-256 - * @returns Derived 32-byte key. - * @example - * Derive the 32-byte PBKDF2 key used by ESKDF. - * ```ts - * pbkdf2('password123', 'user@example.com'); - * ``` - */ -export function pbkdf2(password: string, salt: string): TRet { - return _pbkdf2(sha256, password, salt, { c: PBKDF2_FACTOR, dkLen: 32 }); -} - -// Combines two 32-byte byte arrays into a fresh 32-byte result without aliasing either input. -function xor32(a: TArg, b: TArg): TRet { - abytes(a, 32); - abytes(b, 32); - const arr = new Uint8Array(32); - for (let i = 0; i < 32; i++) { - arr[i] = a[i] ^ b[i]; - } - return arr as TRet; -} - -// All local string length checks are in JS UTF-16 code units, not UTF-8 bytes. -function strHasLength(str: string, min: number, max: number): boolean { - return typeof str === 'string' && str.length >= min && str.length <= max; -} - -/** - * Derives main seed. Takes a lot of time; prefer the higher-level `eskdf(...)` - * flow unless you specifically need the raw main seed. - * Derives the main seed by xor'ing two branches: - * the scrypt branch uses a `0x01` separator byte on username/password, - * and the PBKDF2 branch uses `0x02`. - * Username and password strings are encoded by the underlying KDFs after the - * local separator bytes are appended. - * @param username - account identifier used as public salt - * @param password - user password string - * @returns Main 32-byte seed for the account. - * @throws If the username or password length is invalid. {@link Error} - * @example - * Derive the main ESKDF seed from username and password. - * ```ts - * deriveMainSeed('example-user', 'example-password'); - * ``` - */ -export function deriveMainSeed(username: string, password: string): TRet { - if (!strHasLength(username, 8, 255)) throw new Error('invalid username'); - if (!strHasLength(password, 8, 255)) throw new Error('invalid password'); - // Keep the protocol separators as the literal bytes 0x01 / 0x02 even after minification. - // Embedding them as non-printable characters directly can be awkward across - // JS tooling and environments. - const codes = { _1: 1, _2: 2 }; - const sep = { s: String.fromCharCode(codes._1), p: String.fromCharCode(codes._2) }; - const scr = scrypt(password + sep.s, username + sep.s); - const pbk = pbkdf2(password + sep.p, username + sep.p); - const res = xor32(scr, pbk); - clean(scr, pbk); - return res; -} - -type AccountID = number | string; - -/** - * Converts protocol & accountId pair to HKDF params: - * `info` is UTF-8 protocol bytes, numeric ids become 4-byte BE `salt`, - * and string ids become UTF-8 `salt` bytes. - */ -function getSaltInfo(protocol: string, accountId: AccountID = 0) { - // Note that length here also repeats two lines below - // We do an additional length check here to reduce the scope of DoS attacks - if (!(strHasLength(protocol, 3, 15) && /^[a-z0-9]{3,15}$/.test(protocol))) { - throw new Error('invalid protocol'); - } - - // Exact-match only: substring matches like `assh` / `mentor` must not widen the public whitelist. - const allowsStr = /^(password\d{0,3}|ssh|tor|file)$/.test(protocol); - let salt: Uint8Array; // Assigned below: either 4-byte BE account bytes or UTF-8 account bytes. - if (typeof accountId === 'string') { - if (!allowsStr) throw new Error('accountId must be a number'); - if (!strHasLength(accountId, 1, 255)) - throw new Error('accountId must be string of length 1..255'); - salt = kdfInputToBytes(accountId); - } else if (Number.isSafeInteger(accountId)) { - if (accountId < 0 || accountId > Math.pow(2, 32) - 1) throw new Error('invalid accountId'); - // Convert to Big Endian Uint32 - salt = new Uint8Array(4); - createView(salt).setUint32(0, accountId, false); - } else { - throw new Error('accountId must be a number' + (allowsStr ? ' or string' : '')); - } - const info = kdfInputToBytes(protocol); - return { salt, info }; -} - -type OptsLength = { keyLength: number }; -type OptsMod = { modulus: bigint }; -type KeyOpts = undefined | OptsLength | OptsMod; - -// Local modulus-size helper, not a general bigint-byte-length primitive: -// `<= 128n` is rejected by ESKDF policy. -function countBytes(num: bigint): number { - if (typeof num !== 'bigint' || num <= BigInt(128)) throw new Error('invalid number'); - return Math.ceil(num.toString(2).length / 8); -} - -/** - * Parses keyLength and modulus options to extract length of result key. - * If modulus is used, adds 64 bits to it per the FIPS 186-5 Appendix A.3.1 / - * A.4.1 extra-bits guidance. - */ -function getKeyLength(options: KeyOpts): number { - if (!options || typeof options !== 'object') return 32; - const hasLen = 'keyLength' in options; - const hasMod = 'modulus' in options; - if (hasLen && hasMod) throw new Error('cannot combine keyLength and modulus options'); - if (!hasLen && !hasMod) throw new Error('must have either keyLength or modulus option'); - // FIPS 186-5 Appendix A.3.1 / A.4.1 calls for at least 64 extra bits. - const l = hasMod ? countBytes(options.modulus) + 8 : options.keyLength; - if (!(typeof l === 'number' && l >= 16 && l <= 8192)) throw new Error('invalid keyLength'); - return l; -} - -/** - * Converts key to bigint and divides it by modulus. Big Endian. - * Adapts FIPS 186-5 Appendix A.4.1: `getKeyLength()` already requested the - * extra 64-bit margin, and this step maps the result into `1..modulus-1`. - */ -function modReduceKey(key: TArg, modulus: bigint): TRet { - const _1 = BigInt(1); - const num = BigInt('0x' + bytesToHex(key)); // check for ui8a, then bytesToNumber() - const res = (num % (modulus - _1)) + _1; // Remove 0 from output - if (res < _1) throw new Error('expected positive number'); // Guard against bad values - // Strip the extra 64-bit margin that `getKeyLength()` requested - // for bias reduction. - const len = key.length - 8; - const hex = res.toString(16).padStart(len * 2, '0'); // numberToHex() - const bytes = hexToBytes(hex); - if (bytes.length !== len) throw new Error('invalid length of result key'); - return bytes; -} - -/** Not using classes because constructor cannot be async. */ -export interface ESKDF { - /** - * Derives a child key. Child key will not be associated with any - * other child key because of properties of underlying KDF. - * - * @param protocol - 3-15 character protocol name - * @param accountId - numeric account identifier, or a string id for - * `password\d{0,3}`, `ssh`, `tor`, or `file` - * @param options - Optional child-key shaping parameters. See {@link KeyOpts}. - * @returns Derived child key bytes. - */ - deriveChildKey: (protocol: string, accountId: AccountID, options?: KeyOpts) => TRet; - /** Deletes the main seed from the ESKDF instance. */ - expire: () => void; - /** - * Human-readable fingerprint: first 6 bytes of - * `deriveChildKey('fingerprint', 0)`, formatted as uppercase - * colon-separated hex. - */ - fingerprint: string; -} - -/** - * ESKDF - * @param username - username, email, or identifier, min: 8 characters, should have enough entropy - * @param password - password, min: 8 characters, should have enough entropy - * @returns Frozen API that derives child keys and exposes the account fingerprint. - * @throws If the username or password length is invalid. {@link Error} - * @example - * Derive account-specific child keys from the main ESKDF seed. - * ```ts - * const kdf = await eskdf('example-university', 'beginning-new-example'); - * const key = kdf.deriveChildKey('aes', 0); - * const fingerprint = kdf.fingerprint; - * kdf.expire(); - * ``` - */ -export async function eskdf(username: string, password: string): Promise> { - // We are using closure + object instead of class because - // we want to make `seed` non-accessible for any external function. - let seed: Uint8Array | undefined = deriveMainSeed(username, password); - - function deriveCK( - protocol: string, - accountId: AccountID = 0, - options?: KeyOpts - ): TRet { - // Reject expired instances before deriving any HKDF inputs from the closure-held seed. - abytes(seed!, 32); - const { salt, info } = getSaltInfo(protocol, accountId); // validate protocol & accountId - // Validate option shape and coarse length bounds; - // `hkdf()` still rejects non-integer lengths. - const keyLength = getKeyLength(options); - const key = hkdf(sha256, seed!, salt, info, keyLength); - // Modulus has already been validated - return options && 'modulus' in options ? modReduceKey(key, options.modulus) : key; - } - function expire() { - // Overwrite the closure-held seed before dropping the reference. - if (seed) seed.fill(1); - seed = undefined; - } - // prettier-ignore - const fingerprint = Array.from(deriveCK('fingerprint', 0)) - .slice(0, 6) - .map((char) => char.toString(16).padStart(2, '0').toUpperCase()) - .join(':'); - return Object.freeze({ deriveChildKey: deriveCK, expire, fingerprint }); -} diff --git a/packages/noble-hashes/src/hkdf.ts b/packages/noble-hashes/src/hkdf.ts deleted file mode 100644 index 5b43d2e7..00000000 --- a/packages/noble-hashes/src/hkdf.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * HKDF (RFC 5869): extract + expand in one step. - * See {@link https://soatok.blog/2021/11/17/understanding-hkdf/}. - * @module - */ -import { hmac } from './hmac'; -import { abytes, ahash, anumber, type CHash, clean, type TArg, type TRet } from './utils'; - -/** - * HKDF-extract from spec. Less important part. `HKDF-Extract(IKM, salt) -> PRK` - * Arguments position differs from spec (IKM is first one, since it is not optional) - * Local validation only checks `hash`; `ikm` / `salt` byte validation is delegated to `hmac()`. - * @param hash - hash function that would be used (e.g. sha256) - * @param ikm - input keying material, the initial key - * @param salt - optional salt value (a non-secret random value) - * @returns Pseudorandom key derived from input keying material. - * @example - * Run the HKDF extract step. - * ```ts - * import { extract } from '@noble/hashes/hkdf.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * extract(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])); - * ``` - */ -export function extract( - hash: TArg, - ikm: TArg, - salt?: TArg -): TRet { - ahash(hash); - // NOTE: some libraries treat zero-length array as 'not provided'; - // we don't, since we have undefined as 'not provided' - // https://github.com/RustCrypto/KDFs/issues/15 - if (salt === undefined) salt = new Uint8Array(hash.outputLen); - return hmac(hash, salt, ikm); -} - -// Shared mutable scratch byte for the RFC 5869 block counter `N`. -// Safe to reuse because `expand()` is synchronous and resets it with `clean(...)` before returning. -const HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0); -// Shared RFC 5869 empty string for both `info === undefined` and the first-block `T(0)` input. -const EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of(); - -/** - * HKDF-expand from the spec. The most important part. `HKDF-Expand(PRK, info, L) -> OKM` - * @param hash - hash function that would be used (e.g. sha256) - * @param prk - a pseudorandom key of at least HashLen octets - * (usually, the output from the extract step) - * @param info - optional context and application specific information (can be a zero-length string) - * @param length - length of output keying material in bytes. - * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM. - * @returns Output keying material with the requested length. - * @throws If the requested output length exceeds the HKDF limit - * for the selected hash. {@link Error} - * @example - * Run the HKDF expand step. - * ```ts - * import { expand } from '@noble/hashes/hkdf.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * expand(sha256, new Uint8Array(32), new Uint8Array([1, 2, 3]), 16); - * ``` - */ -export function expand( - hash: TArg, - prk: TArg, - info?: TArg, - length: number = 32 -): TRet { - ahash(hash); - anumber(length, 'length'); - abytes(prk, undefined, 'prk'); - const olen = hash.outputLen; - // RFC 5869 §2.3: PRK is "a pseudorandom key of at least HashLen octets". - if (prk.length < olen) throw new Error('"prk" must be at least HashLen octets'); - // RFC 5869 §2.3 only bounds `L` by `<= 255*HashLen`; `L=0` is valid and yields empty OKM. - if (length > 255 * olen) throw new Error('Length must be <= 255*HashLen'); - const blocks = Math.ceil(length / olen); - if (info === undefined) info = EMPTY_BUFFER; - else abytes(info, undefined, 'info'); - // first L(ength) octets of T - const okm = new Uint8Array(blocks * olen); - // Re-use HMAC instance between blocks - const HMAC = hmac.create(hash, prk); - const HMACTmp = HMAC._cloneInto(); - const T = new Uint8Array(HMAC.outputLen); - for (let counter = 0; counter < blocks; counter++) { - HKDF_COUNTER[0] = counter + 1; - // T(0) = empty string (zero length) - // T(N) = HMAC-Hash(PRK, T(N-1) | info | N) - HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T) - .update(info) - .update(HKDF_COUNTER) - .digestInto(T); - okm.set(T, olen * counter); - HMAC._cloneInto(HMACTmp); - } - HMAC.destroy(); - HMACTmp.destroy(); - clean(T, HKDF_COUNTER); - return okm.slice(0, length) as TRet; -} - -/** - * HKDF (RFC 5869): derive keys from an initial input. - * Combines hkdf_extract + hkdf_expand in one step - * @param hash - hash function that would be used (e.g. sha256) - * @param ikm - input keying material, the initial key - * @param salt - optional salt value (a non-secret random value) - * @param info - optional context and application specific information bytes - * @param length - length of output keying material in bytes. - * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM. - * @returns Output keying material derived from the input key. - * @throws If the requested output length exceeds the HKDF limit - * for the selected hash. {@link Error} - * @example - * HKDF (RFC 5869): derive keys from an initial input. - * ```ts - * import { hkdf } from '@noble/hashes/hkdf.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * import { randomBytes, utf8ToBytes } from '@noble/hashes/utils.js'; - * const inputKey = randomBytes(32); - * const salt = randomBytes(32); - * const info = utf8ToBytes('application-key'); - * const okm = hkdf(sha256, inputKey, salt, info, 32); - * ``` - */ -export const hkdf = ( - hash: TArg, - ikm: TArg, - salt: TArg, - info: TArg, - length: number -): TRet => expand(hash, extract(hash, ikm, salt), info, length); diff --git a/packages/noble-hashes/src/hmac.ts b/packages/noble-hashes/src/hmac.ts deleted file mode 100644 index 7c23186c..00000000 --- a/packages/noble-hashes/src/hmac.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * HMAC: RFC2104 message authentication code. - * @module - */ -import { - abytes, - aexists, - ahash, - aoutput, - type CHash, - clean, - type Hash, - type TArg, - type TRet, -} from './utils'; - -/** - * Internal class for HMAC. - * Accepts any byte key, although RFC 2104 §3 recommends keys at least - * `HashLen` bytes long. - */ -export class _HMAC> implements Hash<_HMAC> { - oHash: T; - iHash: T; - blockLen: number; - outputLen: number; - canXOF = false; - private finished = false; - private destroyed = false; - - constructor(hash: TArg, key: TArg) { - ahash(hash); - abytes(key, undefined, 'key'); - this.iHash = hash.create() as T; - if (typeof this.iHash.update !== 'function') - throw new Error('Expected instance of class which extends utils.Hash'); - this.blockLen = this.iHash.blockLen; - this.outputLen = this.iHash.outputLen; - const blockLen = this.blockLen; - const pad = new Uint8Array(blockLen); - // blockLen can be bigger than outputLen - pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); - for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36; - this.iHash.update(pad); - // By doing update (processing of the first block) of the outer hash here, - // we can re-use it between multiple calls via clone. - this.oHash = hash.create() as T; - // Undo internal XOR && apply outer XOR - for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c; - this.oHash.update(pad); - clean(pad); - } - update(buf: TArg): this { - aexists(this); - this.iHash.update(buf); - return this; - } - digestInto(out: TArg): void { - aexists(this); - aoutput(out, this); - this.finished = true; - const buf = out.subarray(0, this.outputLen); - // Reuse the first outputLen bytes for the inner digest; the outer hash consumes them before - // overwriting that same prefix with the final tag, leaving any oversized tail untouched. - this.iHash.digestInto(buf); - this.oHash.update(buf); - this.oHash.digestInto(buf); - this.destroy(); - } - digest(): TRet { - const out = new Uint8Array(this.oHash.outputLen); - this.digestInto(out); - return out as TRet; - } - _cloneInto(to?: _HMAC): _HMAC { - // Create new instance without calling constructor since the key - // is already in state and we don't know it. - to ||= Object.create(Object.getPrototypeOf(this), {}); - const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; - to = to as this; - to.finished = finished; - to.destroyed = destroyed; - to.blockLen = blockLen; - to.outputLen = outputLen; - to.oHash = oHash._cloneInto(to.oHash); - to.iHash = iHash._cloneInto(to.iHash); - return to; - } - clone(): _HMAC { - return this._cloneInto(); - } - destroy(): void { - this.destroyed = true; - this.oHash.destroy(); - this.iHash.destroy(); - } -} - -/** - * HMAC: RFC2104 message authentication code. - * @param hash - function that would be used e.g. sha256 - * @param key - authentication key bytes - * @param message - message bytes to authenticate - * @returns Authentication tag bytes. - * @example - * Compute an RFC 2104 HMAC. - * ```ts - * import { hmac } from '@noble/hashes/hmac.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const mac = hmac(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])); - * ``` - */ -type HmacFn = { - (hash: TArg, key: TArg, message: TArg): TRet; - create(hash: TArg, key: TArg): TRet<_HMAC>; -}; -export const hmac: TRet = /* @__PURE__ */ (() => { - const hmac_ = (( - hash: TArg, - key: TArg, - message: TArg - ): TRet => new _HMAC(hash, key).update(message).digest()) as TRet; - hmac_.create = (hash: TArg, key: TArg): TRet<_HMAC> => - new _HMAC(hash, key) as TRet<_HMAC>; - return hmac_; -})(); diff --git a/packages/noble-hashes/src/index.ts b/packages/noble-hashes/src/index.ts deleted file mode 100644 index c6a9c23a..00000000 --- a/packages/noble-hashes/src/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Audited & minimal JS implementation of hash functions, MACs, and KDFs. - * Check out individual modules. - * @module - * @example -```js -import { - sha256, sha384, sha512, sha224, sha512_224, sha512_256 -} from '@noble/hashes/sha2.js'; -import { - sha3_224, sha3_256, sha3_384, sha3_512, - keccak_224, keccak_256, keccak_384, keccak_512, - shake128, shake256 -} from '@noble/hashes/sha3.js'; -import { - cshake128, cshake256, - turboshake128, turboshake256, - kt128, kt256, - kmac128, kmac256, - tuplehash256, parallelhash256, - keccakprg -} from '@noble/hashes/sha3-addons.js'; -import { blake3 } from '@noble/hashes/blake3.js'; -import { blake2b, blake2s } from '@noble/hashes/blake2.js'; -import { hmac } from '@noble/hashes/hmac.js'; -import { hkdf } from '@noble/hashes/hkdf.js'; -import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2.js'; -import { scrypt, scryptAsync } from '@noble/hashes/scrypt.js'; -import { md5, ripemd160, sha1 } from '@noble/hashes/legacy.js'; -import * as utils from '@noble/hashes/utils.js'; -``` - */ -throw new Error('root module cannot be imported: import submodules instead. Check out README'); diff --git a/packages/noble-hashes/src/legacy.ts b/packages/noble-hashes/src/legacy.ts deleted file mode 100644 index 60c0372c..00000000 --- a/packages/noble-hashes/src/legacy.ts +++ /dev/null @@ -1,339 +0,0 @@ -/** - -SHA1 (RFC 3174), MD5 (RFC 1321), and RIPEMD160 legacy, weak hash functions. -RFC 2286 only covers HMAC-RIPEMD160 wrapper material and test vectors, -not the base RIPEMD-160 compression spec. -Don't use them in a new protocol. What "weak" means: - -- Collisions can be made with 2^18 effort in MD5, 2^60 in SHA1, 2^80 in RIPEMD160. -- No practical pre-image attacks (only theoretical, 2^123.4) -- HMAC seems kinda ok: https://www.rfc-editor.org/rfc/rfc6151 - * @module - */ -import { Chi, HashMD, Maj } from './_md'; -import { type CHash, clean, createHasher, rotl, type TRet } from './utils'; - -/** Initial SHA-1 state from RFC 3174 §6.1. */ -const SHA1_IV = /* @__PURE__ */ Uint32Array.from([ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0, -]); - -// Reusable 80-word SHA-1 message schedule buffer. -const SHA1_W = /* @__PURE__ */ new Uint32Array(80); - -/** Internal SHA1 legacy hash class. */ -export class _SHA1 extends HashMD<_SHA1> { - private A = SHA1_IV[0] | 0; - private B = SHA1_IV[1] | 0; - private C = SHA1_IV[2] | 0; - private D = SHA1_IV[3] | 0; - private E = SHA1_IV[4] | 0; - - constructor() { - super(64, 20, 8, false); - } - protected get(): [number, number, number, number, number] { - const { A, B, C, D, E } = this; - return [A, B, C, D, E]; - } - protected set(A: number, B: number, C: number, D: number, E: number): void { - this.A = A | 0; - this.B = B | 0; - this.C = C | 0; - this.D = D | 0; - this.E = E | 0; - } - protected process(view: DataView, offset: number): void { - for (let i = 0; i < 16; i++, offset += 4) SHA1_W[i] = view.getUint32(offset, false); - for (let i = 16; i < 80; i++) - SHA1_W[i] = rotl(SHA1_W[i - 3] ^ SHA1_W[i - 8] ^ SHA1_W[i - 14] ^ SHA1_W[i - 16], 1); - // Compression function main loop, 80 rounds - let { A, B, C, D, E } = this; - for (let i = 0; i < 80; i++) { - let F, K; - if (i < 20) { - F = Chi(B, C, D); - K = 0x5a827999; - } else if (i < 40) { - F = B ^ C ^ D; - K = 0x6ed9eba1; - } else if (i < 60) { - F = Maj(B, C, D); - K = 0x8f1bbcdc; - } else { - F = B ^ C ^ D; - K = 0xca62c1d6; - } - const T = (rotl(A, 5) + F + E + K + SHA1_W[i]) | 0; - E = D; - D = C; - C = rotl(B, 30); - B = A; - A = T; - } - // Add the compressed chunk to the current hash value - A = (A + this.A) | 0; - B = (B + this.B) | 0; - C = (C + this.C) | 0; - D = (D + this.D) | 0; - E = (E + this.E) | 0; - this.set(A, B, C, D, E); - } - protected roundClean(): void { - clean(SHA1_W); - } - destroy(): void { - // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves - // update()/digest() callable on reused instances. - this.destroyed = true; - this.set(0, 0, 0, 0, 0); - clean(this.buffer); - } -} - -/** - * SHA1 (RFC 3174) legacy hash function. It was cryptographically broken. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA1. - * ```ts - * sha1(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha1: TRet = /* @__PURE__ */ createHasher(() => new _SHA1()); - -/** RFC 1321 `T[i]` uses `floor(2^32 * abs(sin(i)))`; this is the shared `2^32` scale factor. */ -const p32 = /* @__PURE__ */ Math.pow(2, 32); -/** RFC 1321 `T[1..64]` table. */ -const K = /* @__PURE__ */ Array.from({ length: 64 }, (_, i) => - Math.floor(p32 * Math.abs(Math.sin(i + 1))) -); - -/** MD5 initial state from RFC 1321, stored as 4 u32 words. */ -const MD5_IV = /* @__PURE__ */ SHA1_IV.slice(0, 4); - -// Reusable 16-word MD5 message block buffer. -const MD5_W = /* @__PURE__ */ new Uint32Array(16); -/** Internal MD5 legacy hash class. */ -export class _MD5 extends HashMD<_MD5> { - private A = MD5_IV[0] | 0; - private B = MD5_IV[1] | 0; - private C = MD5_IV[2] | 0; - private D = MD5_IV[3] | 0; - - constructor() { - super(64, 16, 8, true); - } - protected get(): [number, number, number, number] { - const { A, B, C, D } = this; - return [A, B, C, D]; - } - protected set(A: number, B: number, C: number, D: number): void { - this.A = A | 0; - this.B = B | 0; - this.C = C | 0; - this.D = D | 0; - } - protected process(view: DataView, offset: number): void { - for (let i = 0; i < 16; i++, offset += 4) MD5_W[i] = view.getUint32(offset, true); - // Compression function main loop, 64 rounds - let { A, B, C, D } = this; - for (let i = 0; i < 64; i++) { - let F, g, s; - if (i < 16) { - F = Chi(B, C, D); - g = i; - s = [7, 12, 17, 22]; - } else if (i < 32) { - // RFC 1321 round 2 uses G(B,C,D) = (B & D) | (C & ~D), which is `Chi(D, B, C)`. - F = Chi(D, B, C); - g = (5 * i + 1) % 16; - s = [5, 9, 14, 20]; - } else if (i < 48) { - F = B ^ C ^ D; - g = (3 * i + 5) % 16; - s = [4, 11, 16, 23]; - } else { - F = C ^ (B | ~D); - g = (7 * i) % 16; - s = [6, 10, 15, 21]; - } - F = F + A + K[i] + MD5_W[g]; - A = D; - D = C; - C = B; - B = B + rotl(F, s[i % 4]); - } - // 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; - this.set(A, B, C, D); - } - protected roundClean(): void { - clean(MD5_W); - } - destroy(): void { - // 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); - clean(this.buffer); - } -} - -/** - * MD5 (RFC 1321) legacy hash function. It was cryptographically broken. - * MD5 architecture is similar to SHA1, with some differences: - * - Reduced output length: 16 bytes (128 bit) instead of 20 - * - 64 rounds, instead of 80 - * - Little-endian: could be faster, but will require more code - * - Non-linear index selection: huge speed-up for unroll - * - Per round constants: more memory accesses, additional speed-up for unroll - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with MD5. - * ```ts - * md5(new Uint8Array([97, 98, 99])); - * ``` - */ -export const md5: TRet = /* @__PURE__ */ createHasher(() => new _MD5()); - -// RIPEMD-160 - -// Permutation repeatedly applied to derive the later RIPEMD-160 message-order tables. -const Rho160 = /* @__PURE__ */ Uint8Array.from([ - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, -]); -const Id160 = /* @__PURE__ */ (() => Uint8Array.from(new Array(16).fill(0).map((_, i) => i)))(); -const Pi160 = /* @__PURE__ */ (() => Id160.map((i) => (9 * i + 5) % 16))(); -// Five left/right message-word orderings for the RIPEMD-160 dual-lane rounds. -const idxLR = /* @__PURE__ */ (() => { - const L = [Id160]; - const R = [Pi160]; - const res = [L, R]; - for (let i = 0; i < 4; i++) for (let j of res) j.push(j[i].map((k) => Rho160[k])); - return res; -})(); -const idxL = /* @__PURE__ */ (() => idxLR[0])(); -const idxR = /* @__PURE__ */ (() => idxLR[1])(); -// const [idxL, idxR] = idxLR; - -// Base per-group shift table before the left/right message-order permutations are applied. -const shifts160 = /* @__PURE__ */ [ - [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], - [12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7], - [13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9], - [14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6], - [15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5], -].map((i) => Uint8Array.from(i)); -const shiftsL160 = /* @__PURE__ */ idxL.map((idx, i) => idx.map((j) => shifts160[i][j])); -const shiftsR160 = /* @__PURE__ */ idxR.map((idx, i) => idx.map((j) => shifts160[i][j])); -// Five left-lane additive constants for RIPEMD-160. -const Kl160 = /* @__PURE__ */ Uint32Array.from([ - 0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e, -]); -// Five right-lane additive constants for RIPEMD-160. -const Kr160 = /* @__PURE__ */ Uint32Array.from([ - 0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000, -]); -// Called `f()` in the spec; valid `group` values are 0..4, and out-of-range -// inputs currently fall through to the group-4 branch. -function ripemd_f(group: number, x: number, y: number, z: number): number { - if (group === 0) return x ^ y ^ z; - if (group === 1) return (x & y) | (~x & z); - if (group === 2) return (x | ~y) ^ z; - if (group === 3) return (x & z) | (y & ~z); - return x ^ (y | ~z); -} -// Reusable 16-word RIPEMD-160 message block buffer. -const BUF_160 = /* @__PURE__ */ new Uint32Array(16); -/** - * Internal RIPEMD-160 legacy hash class. - * RFC 2286 only adds HMAC-RIPEMD160 material, not the core hash specification. - */ -export class _RIPEMD160 extends HashMD<_RIPEMD160> { - private h0 = 0x67452301 | 0; - private h1 = 0xefcdab89 | 0; - private h2 = 0x98badcfe | 0; - private h3 = 0x10325476 | 0; - private h4 = 0xc3d2e1f0 | 0; - - constructor() { - super(64, 20, 8, true); - } - protected get(): [number, number, number, number, number] { - const { h0, h1, h2, h3, h4 } = this; - return [h0, h1, h2, h3, h4]; - } - protected set(h0: number, h1: number, h2: number, h3: number, h4: number): void { - this.h0 = h0 | 0; - this.h1 = h1 | 0; - this.h2 = h2 | 0; - this.h3 = h3 | 0; - this.h4 = h4 | 0; - } - protected process(view: DataView, offset: number): void { - for (let i = 0; i < 16; i++, offset += 4) BUF_160[i] = view.getUint32(offset, true); - // prettier-ignore - let al = this.h0 | 0, ar = al, - bl = this.h1 | 0, br = bl, - cl = this.h2 | 0, cr = cl, - dl = this.h3 | 0, dr = dl, - el = this.h4 | 0, er = el; - - // Instead of iterating 0 to 80, we split it into 5 groups - // And use the groups in constants, functions, etc. Much simpler - for (let group = 0; group < 5; group++) { - const rGroup = 4 - group; - const hbl = Kl160[group], hbr = Kr160[group]; // prettier-ignore - const rl = idxL[group], rr = idxR[group]; // prettier-ignore - const sl = shiftsL160[group], sr = shiftsR160[group]; // prettier-ignore - for (let i = 0; i < 16; i++) { - const tl = (rotl(al + ripemd_f(group, bl, cl, dl) + BUF_160[rl[i]] + hbl, sl[i]) + el) | 0; - al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore - } - // 2 loops are 10% faster - for (let i = 0; i < 16; i++) { - const tr = (rotl(ar + ripemd_f(rGroup, br, cr, dr) + BUF_160[rr[i]] + hbr, sr[i]) + er) | 0; - ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore - } - } - // Add the compressed chunk to the current hash value - // Final recombination cross-adds the left/right lane accumulators into the next h0..h4 order. - this.set( - (this.h1 + cl + dr) | 0, - (this.h2 + dl + er) | 0, - (this.h3 + el + ar) | 0, - (this.h4 + al + br) | 0, - (this.h0 + bl + cr) | 0 - ); - } - protected roundClean(): void { - clean(BUF_160); - } - destroy(): void { - this.destroyed = true; - clean(this.buffer); - this.set(0, 0, 0, 0, 0); - } -} - -/** - * RIPEMD-160 - a legacy hash function from 1990s. - * RFC 2286 only covers HMAC-RIPEMD160 test material; the links below point - * at the base RIPEMD-160 references. - * * {@link https://homes.esat.kuleuven.be/~bosselae/ripemd160.html} - * * {@link https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf} - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with RIPEMD-160. - * ```ts - * ripemd160(new Uint8Array([97, 98, 99])); - * ``` - */ -export const ripemd160: TRet = /* @__PURE__ */ createHasher(() => new _RIPEMD160()); diff --git a/packages/noble-hashes/src/pbkdf2.ts b/packages/noble-hashes/src/pbkdf2.ts deleted file mode 100644 index cc8bafd8..00000000 --- a/packages/noble-hashes/src/pbkdf2.ts +++ /dev/null @@ -1,175 +0,0 @@ -/** - * PBKDF (RFC 2898). Can be used to create a key from password and salt. - * @module - */ -import { hmac } from './hmac'; -// prettier-ignore -import { - ahash, anumber, - asyncLoop, type CHash, - checkOpts, clean, createView, type Hash, - type KDFInput, - kdfInputToBytes, - type TArg, - type TRet -} from './utils'; - -/** - * PBKDF2 options: - * * c: iterations, should probably be higher than 100_000 - * * dkLen: desired length of derived key in bytes, must be `>= 1` per RFC 8018 §5.2 - * * asyncTick: max time in ms for which async function can block execution - */ -export type Pbkdf2Opt = { - /** Iteration count. Higher values increase CPU cost. */ - c: number; - /** Desired derived key length in bytes, must be `>= 1` per RFC 8018 §5.2. */ - dkLen?: number; - /** Max scheduler block time in milliseconds for the async variant. */ - asyncTick?: number; -}; -// Common start and end for sync/async functions -function pbkdf2Init( - hash: TArg, - _password: TArg, - _salt: TArg, - _opts: TArg -) { - ahash(hash); - const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts); - const { c, dkLen, asyncTick } = opts; - anumber(c, 'c'); - anumber(dkLen, 'dkLen'); - anumber(asyncTick, 'asyncTick'); - if (c < 1) throw new Error('iterations (c) must be >= 1'); - // RFC 8018 §5.2 defines `dkLen` as "a positive integer". - if (dkLen < 1) throw new Error('"dkLen" must be >= 1'); - // RFC 8018 §5.2 step 1 requires rejecting oversize `dkLen` - // before allocating the destination buffer. - if (dkLen > (2 ** 32 - 1) * hash.outputLen) throw new Error('derived key too long'); - const password = kdfInputToBytes(_password, 'password'); - const salt = kdfInputToBytes(_salt, 'salt'); - // DK = PBKDF2(PRF, Password, Salt, c, dkLen); - const DK = new Uint8Array(dkLen); - // U1 = PRF(Password, Salt + INT_32_BE(i)) - const PRF = hmac.create(hash, password); - // Cache PRF(P, S || ...) prefix state so each block only appends INT_32_BE(i). - const PRFSalt = PRF._cloneInto().update(salt); - return { c, dkLen, asyncTick, DK, PRF, PRFSalt }; -} - -function pbkdf2Output>( - PRF: TArg>, - PRFSalt: TArg>, - DK: TArg, - prfW: TArg | undefined>, - u: TArg -): TRet { - // Shared sync/async cleanup point: wipe transient PRF state - // while preserving the derived key buffer. - PRF.destroy(); - PRFSalt.destroy(); - if (prfW) prfW.destroy(); - clean(u); - return DK as TRet; -} - -/** - * PBKDF2-HMAC: RFC 8018 key derivation function. - * @param hash - hash function that would be used e.g. sha256 - * @param password - password from which a derived key is generated; - * JS string inputs are UTF-8 encoded first - * @param salt - cryptographic salt; JS string inputs are UTF-8 encoded first - * @param opts - PBKDF2 work factor and output settings. `dkLen`, if provided, - * must be `>= 1` per RFC 8018 §5.2. See {@link Pbkdf2Opt}. - * @returns Derived key bytes. - * @throws If the PBKDF2 iteration count or derived-key settings are invalid. {@link Error} - * @example - * PBKDF2-HMAC: RFC 2898 key derivation function. - * ```ts - * import { pbkdf2 } from '@noble/hashes/pbkdf2.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const key = pbkdf2(sha256, 'password', 'salt', { dkLen: 32, c: Math.pow(2, 18) }); - * ``` - */ -export function pbkdf2( - hash: TArg, - password: TArg, - salt: TArg, - opts: TArg -): TRet { - const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts); - let prfW: any; // Working copy - const arr = new Uint8Array(4); - const view = createView(arr); - const u = new Uint8Array(PRF.outputLen); - // DK = T1 + T2 + ⋯ + Tdklen/hlen - for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { - // Ti = F(Password, Salt, c, i) - // The last Ti view can be shorter than hLen, which applies - // RFC 8018 §5.2 step 4's T_l<0..r-1> truncation without extra copies. - const Ti = DK.subarray(pos, pos + PRF.outputLen); - view.setInt32(0, ti, false); - // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc - // U1 = PRF(Password, Salt + INT_32_BE(i)) - (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u); - Ti.set(u.subarray(0, Ti.length)); - for (let ui = 1; ui < c; ui++) { - // Uc = PRF(Password, Uc−1) - PRF._cloneInto(prfW).update(u).digestInto(u); - for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i]; - } - } - return pbkdf2Output(PRF, PRFSalt, DK, prfW, u); -} - -/** - * PBKDF2-HMAC: RFC 8018 key derivation function. Async version. - * @param hash - hash function that would be used e.g. sha256 - * @param password - password from which a derived key is generated; - * JS string inputs are UTF-8 encoded first - * @param salt - cryptographic salt; JS string inputs are UTF-8 encoded first - * @param opts - PBKDF2 work factor and output settings. `dkLen`, if provided, - * must be `>= 1` per RFC 8018 §5.2. `asyncTick` is only a local - * scheduler-yield knob for this JS wrapper, not part of RFC 8018. - * See {@link Pbkdf2Opt}. - * @returns Promise resolving to derived key bytes. - * @throws If the PBKDF2 iteration count or derived-key settings are invalid. {@link Error} - * @example - * PBKDF2-HMAC: RFC 2898 key derivation function. - * ```ts - * import { pbkdf2Async } from '@noble/hashes/pbkdf2.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const key = await pbkdf2Async(sha256, 'password', 'salt', { dkLen: 32, c: 500_000 }); - * ``` - */ -export async function pbkdf2Async( - hash: TArg, - password: TArg, - salt: TArg, - opts: TArg -): Promise> { - const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts); - let prfW: any; // Working copy - const arr = new Uint8Array(4); - const view = createView(arr); - const u = new Uint8Array(PRF.outputLen); - // DK = T1 + T2 + ⋯ + Tdklen/hlen - for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { - // Ti = F(Password, Salt, c, i) - // The last Ti view can be shorter than hLen, which applies - // RFC 8018 §5.2 step 4's T_l<0..r-1> truncation without extra copies. - const Ti = DK.subarray(pos, pos + PRF.outputLen); - view.setInt32(0, ti, false); - // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc - // U1 = PRF(Password, Salt + INT_32_BE(i)) - (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u); - Ti.set(u.subarray(0, Ti.length)); - await asyncLoop(c - 1, asyncTick, () => { - // Uc = PRF(Password, Uc−1) - PRF._cloneInto(prfW).update(u).digestInto(u); - for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i]; - }); - } - return pbkdf2Output(PRF, PRFSalt, DK, prfW, u); -} diff --git a/packages/noble-hashes/src/scrypt.ts b/packages/noble-hashes/src/scrypt.ts deleted file mode 100644 index 211ce672..00000000 --- a/packages/noble-hashes/src/scrypt.ts +++ /dev/null @@ -1,314 +0,0 @@ -/** - * RFC 7914 Scrypt KDF. Can be used to create a key from password and salt. - * @module - */ -import { pbkdf2 } from './pbkdf2'; -import { sha256 } from './sha2'; -// prettier-ignore -import { - anumber, asyncLoop, - checkOpts, clean, - type KDFInput, - rotl, - swap32IfBE, - type TArg, - type TRet, - u32} from './utils'; - -// The main Scrypt loop: uses Salsa extensively. -// Six versions of the function were tried, this is the fastest one. -// RFC 7914 §3 / §4 step 2 applies Salsa20/8 to one 16-word (64-byte) block -// after xor'ing two such blocks. -// The local `y*` snapshot keeps the xor input stable even when `out` aliases `prev` or `input`. -// prettier-ignore -function XorAndSalsa( - prev: TArg, - pi: number, - input: TArg, - ii: number, - out: TArg, - oi: number -) { - // Based on https://cr.yp.to/salsa20.html and RFC 7914's Salsa20/8 core. - // Xor blocks - let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++]; - let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++]; - let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++]; - let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++]; - let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++]; - let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++]; - let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++]; - let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++]; - // Save state to temporary variables (salsa) - 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; - // Main loop (salsa) - for (let i = 0; i < 8; i += 2) { - x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9); - x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18); - x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9); - x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18); - x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9); - x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18); - x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9); - x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18); - x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9); - x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18); - x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9); - x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18); - x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9); - x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18); - x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9); - x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18); - } - // Write output (salsa) - 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; -} - -function BlockMix( - input: TArg, - ii: number, - out: TArg, - oi: number, - r: number -) { - // The block B is `r` 128-byte chunks, i.e. `2r` 16-word (64-byte) Salsa blocks. - let head = oi + 0; - let tail = oi + 16 * r; - for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1] - for (let i = 0; i < r; i++, head += 16, ii += 16) { - // RFC 7914 §4 step 3 outputs `Y[0], Y[2], ...` first, then `Y[1], Y[3], ...`; - // `head` and `tail` lay out those even/odd halves in place. - XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1]) - if (i > 0) tail += 16; // First iteration overwrites tmp value in tail - // tail[i] = Salsa(blockIn[2*i+1] ^ head[i]) - XorAndSalsa(out, head, input, (ii += 16), out, tail); - } -} - -/** - * Scrypt options: - * - `N` is cpu/mem work factor (power of 2 e.g. `2**18`) - * - `r` is block size (8 is common), fine-tunes sequential memory read size and performance - * - `p` is parallelization factor (1 is common) - * - `dkLen` is output key length in bytes e.g. 32, and must be `>= 1` per RFC 7914 §2. - * - `asyncTick` - (default: 10) max time in ms for which async function can block execution - * - `maxmem` - (default: `1024 ** 3 + 1024` aka 1GB+1KB). A limit that the app could use for scrypt - * - `onProgress` - callback function that would be executed for progress report - */ -export type ScryptOpts = { - /** CPU and memory work factor. Must be a power of two. */ - N: number; - /** Block size parameter. */ - r: number; - /** Parallelization factor. */ - p: number; - /** Desired derived key length in bytes, must be `>= 1` per RFC 7914 §2. */ - dkLen?: number; - /** Max scheduler block time in milliseconds for the async variant. */ - asyncTick?: number; - /** Maximum temporary memory budget in bytes. */ - maxmem?: number; - /** - * Optional progress callback invoked during long-running derivations. - * param progress - completion fraction in the `0..1` range - */ - onProgress?: (progress: number) => void; -}; - -// Common prologue and epilogue for sync/async functions -function scryptInit(password: TArg, salt: TArg, _opts?: TArg) { - // Maxmem - 1GB+1KB by default - const opts = checkOpts( - { - dkLen: 32, - asyncTick: 10, - maxmem: 1024 ** 3 + 1024, - }, - _opts - ); - const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts; - anumber(N, 'N'); - anumber(r, 'r'); - anumber(p, 'p'); - anumber(dkLen, 'dkLen'); - anumber(asyncTick, 'asyncTick'); - anumber(maxmem, 'maxmem'); - if (onProgress !== undefined && typeof onProgress !== 'function') - throw new Error('progressCb must be a function'); - const blockSize = 128 * r; - const blockSize32 = blockSize / 4; - - // Max N is 2^32 (Integrify is 32-bit). - // Real limit can be 2^22: some JS engines limit Uint8Array to 4GB. - // Spec check `N >= 2^(blockSize / 8)` is not done for compat with popular libs, - // which used incorrect r: 1, p: 8. Also, the check seems to be a spec error: - // https://www.rfc-editor.org/errata_search.php?rfc=7914 - const pow32 = Math.pow(2, 32); - if (N <= 1 || (N & (N - 1)) !== 0 || N > pow32) - throw new Error('"N" expected a power of 2, and 2^1 <= N <= 2^32'); - if (p < 1 || p > ((pow32 - 1) * 32) / blockSize) - throw new Error('"p" expected integer 1..((2^32 - 1) * 32) / (128 * r)'); - // RFC 7914 §2 defines `dkLen` as a positive integer. - if (dkLen < 1 || dkLen > (pow32 - 1) * 32) - throw new Error('"dkLen" expected integer 1..(2^32 - 1) * 32'); - // Include the shared `tmp` scratch block so `maxmem` matches noble's actual temporary allocation. - // Node requires more headroom here, so this accounting is intentionally noble-specific. - const memUsed = blockSize * (N + p + 1); - if (memUsed > maxmem) - throw new Error( - '"maxmem" limit was hit: memUsed(128*r*(N+p+1))=' + memUsed + ', maxmem=' + maxmem - ); - // [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor) - // Since it has only one iteration there is no reason to use async variant - const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p }); - const B32 = u32(B); - // Re-used between parallel iterations. Array(iterations) of B - const V = u32(new Uint8Array(blockSize * N)); - const tmp = u32(new Uint8Array(blockSize)); - let blockMixCb = () => {}; - if (onProgress) { - const totalBlockMix = 2 * N * 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(totalBlockMix / 10000), 1); - let blockMixCnt = 0; - blockMixCb = () => { - blockMixCnt++; - if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix)) - onProgress(blockMixCnt / totalBlockMix); - }; - } - return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick }; -} - -function scryptOutput( - password: TArg, - dkLen: number, - B: TArg, - V: TArg, - tmp: TArg -): TRet { - // Shared final PBKDF2-and-cleanup step: keep the derived key, wipe the scrypt workspace. - const res = pbkdf2(sha256, password, B, { c: 1, dkLen }); - clean(B, V, tmp); - return res; -} - -/** - * Scrypt KDF from RFC 7914. See {@link ScryptOpts}. - * @param password - password or key material to derive from; - * JS string inputs are UTF-8 encoded first - * @param salt - unique salt bytes or string; JS string inputs are UTF-8 encoded first - * @param opts - Scrypt cost and memory parameters. `dkLen`, if provided, - * must be `>= 1` per RFC 7914 §2. See {@link ScryptOpts}. - * @returns Derived key bytes. - * @throws If the Scrypt cost, memory, or callback options are invalid. {@link Error} - * @example - * Derive a key with scrypt. - * ```ts - * scrypt('password', 'salt', { N: 2**18, r: 8, p: 1, dkLen: 32 }); - * ``` - */ -export function scrypt( - password: TArg, - salt: TArg, - opts: TArg -): TRet { - const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit( - password, - salt, - opts - ); - swap32IfBE(B32); - for (let pi = 0; pi < p; pi++) { - const Pi = blockSize32 * pi; - for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i] - for (let i = 0, pos = 0; i < N - 1; i++) { - BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]); - blockMixCb(); - } - BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element - blockMixCb(); - for (let i = 0; i < N; i++) { - // First u32 of the last 64-byte block (u32 is LE) - // RFC 7914 Integerify(X) uses the whole last 64-byte block, but mod N - // only depends on the low word here because N is a power of two and - // this implementation caps N at 2^32. - // & (N - 1) is % N as N is a power of 2, N & (N - 1) = 0 is checked - // above; >>> 0 for unsigned, input fits in u32. - const j = (B32[Pi + blockSize32 - 16] & (N - 1)) >>> 0; // j = Integrify(X) % iterations - // tmp = B ^ V[j] - for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; - BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j]) - blockMixCb(); - } - } - swap32IfBE(B32); - return scryptOutput(password, dkLen, B, V, tmp); -} - -/** - * Scrypt KDF from RFC 7914. Async version. See {@link ScryptOpts}. - * @param password - password or key material to derive from; - * JS string inputs are UTF-8 encoded first - * @param salt - unique salt bytes or string; JS string inputs are UTF-8 encoded first - * @param opts - Scrypt cost and memory parameters. `dkLen`, if provided, - * must be `>= 1` per RFC 7914 §2. `asyncTick` is only a local - * scheduler-yield control for this JS wrapper, not part of RFC 7914. - * See {@link ScryptOpts}. - * @returns Promise resolving to derived key bytes. - * @throws If the Scrypt cost, memory, or callback options are invalid. {@link Error} - * @example - * Derive a key with scrypt asynchronously. - * ```ts - * await scryptAsync('password', 'salt', { N: 2**18, r: 8, p: 1, dkLen: 32 }); - * ``` - */ -export async function scryptAsync( - password: TArg, - salt: TArg, - opts: TArg -): Promise> { - const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit( - password, - salt, - opts - ); - swap32IfBE(B32); - for (let pi = 0; pi < p; pi++) { - const Pi = blockSize32 * pi; - for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i] - let pos = 0; - await asyncLoop(N - 1, asyncTick, () => { - BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]); - blockMixCb(); - }); - BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element - blockMixCb(); - await asyncLoop(N, asyncTick, () => { - // First u32 of the last 64-byte block (u32 is LE) - // RFC 7914 Integerify(X) uses the whole last 64-byte block, but mod N - // only depends on the low word here because N is a power of two and - // this implementation caps N at 2^32. - // & (N - 1) is % N as N is a power of 2, N & (N - 1) = 0 is checked - // above; >>> 0 for unsigned, input fits in u32. - const j = (B32[Pi + blockSize32 - 16] & (N - 1)) >>> 0; // j = Integrify(X) % iterations - // tmp = B ^ V[j] - for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; - BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j]) - blockMixCb(); - }); - } - swap32IfBE(B32); - return scryptOutput(password, dkLen, B, V, tmp); -} diff --git a/packages/noble-hashes/src/sha2.ts b/packages/noble-hashes/src/sha2.ts deleted file mode 100644 index 73139608..00000000 --- a/packages/noble-hashes/src/sha2.ts +++ /dev/null @@ -1,530 +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'; -import * as u64 from './_u64'; -import { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils'; - -/** - * 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. */ -abstract class SHA2_32B> extends HashMD { - // We cannot use array here since array allows indexing by variable - // which means optimizer/compiler cannot use registers. - protected abstract A: number; - protected abstract B: number; - protected abstract C: number; - protected abstract D: number; - protected abstract E: number; - protected abstract F: number; - protected abstract G: number; - protected abstract H: number; - - constructor(outputLen: number) { - super(64, outputLen, 8, false); - } - protected get(): [number, number, number, number, number, number, number, number] { - const { A, B, C, D, E, F, G, H } = this; - return [A, B, C, D, E, F, G, H]; - } - // prettier-ignore - protected set( - A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number - ): void { - 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; - } - protected process(view: DataView, offset: number): void { - // 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); - } - protected roundClean(): void { - clean(SHA256_W); - } - destroy(): void { - // 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<_SHA256> { - // We cannot use array here since array allows indexing by variable - // which means optimizer/compiler cannot use registers. - protected A: number = SHA256_IV[0] | 0; - protected B: number = SHA256_IV[1] | 0; - protected C: number = SHA256_IV[2] | 0; - protected D: number = SHA256_IV[3] | 0; - protected E: number = SHA256_IV[4] | 0; - protected F: number = SHA256_IV[5] | 0; - protected G: number = SHA256_IV[6] | 0; - protected H: number = SHA256_IV[7] | 0; - constructor() { - super(32); - } -} - -/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */ -export class _SHA224 extends SHA2_32B<_SHA224> { - protected A: number = SHA224_IV[0] | 0; - protected B: number = SHA224_IV[1] | 0; - protected C: number = SHA224_IV[2] | 0; - protected D: number = SHA224_IV[3] | 0; - protected E: number = SHA224_IV[4] | 0; - protected F: number = SHA224_IV[5] | 0; - protected G: number = SHA224_IV[6] | 0; - protected H: number = SHA224_IV[7] | 0; - constructor() { - super(28); - } -} - -// 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. */ -abstract 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 - protected abstract Ah: number; - protected abstract Al: number; - protected abstract Bh: number; - protected abstract Bl: number; - protected abstract Ch: number; - protected abstract Cl: number; - protected abstract Dh: number; - protected abstract Dl: number; - protected abstract Eh: number; - protected abstract El: number; - protected abstract Fh: number; - protected abstract Fl: number; - protected abstract Gh: number; - protected abstract Gl: number; - protected abstract Hh: number; - protected abstract Hl: number; - - constructor(outputLen: number) { - super(128, outputLen, 16, false); - } - // prettier-ignore - protected get(): [ - number, number, number, number, number, number, number, number, - number, number, number, number, number, number, number, number - ] { - 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 - protected set( - Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number, - Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number - ): void { - 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; - } - protected process(view: DataView, offset: number): void { - // 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); - } - protected roundClean(): void { - clean(SHA512_W_H, SHA512_W_L); - } - destroy(): void { - // 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<_SHA512> { - protected Ah: number = SHA512_IV[0] | 0; - protected Al: number = SHA512_IV[1] | 0; - protected Bh: number = SHA512_IV[2] | 0; - protected Bl: number = SHA512_IV[3] | 0; - protected Ch: number = SHA512_IV[4] | 0; - protected Cl: number = SHA512_IV[5] | 0; - protected Dh: number = SHA512_IV[6] | 0; - protected Dl: number = SHA512_IV[7] | 0; - protected Eh: number = SHA512_IV[8] | 0; - protected El: number = SHA512_IV[9] | 0; - protected Fh: number = SHA512_IV[10] | 0; - protected Fl: number = SHA512_IV[11] | 0; - protected Gh: number = SHA512_IV[12] | 0; - protected Gl: number = SHA512_IV[13] | 0; - protected Hh: number = SHA512_IV[14] | 0; - protected Hl: number = SHA512_IV[15] | 0; - - constructor() { - super(64); - } -} - -/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */ -export class _SHA384 extends SHA2_64B<_SHA384> { - protected Ah: number = SHA384_IV[0] | 0; - protected Al: number = SHA384_IV[1] | 0; - protected Bh: number = SHA384_IV[2] | 0; - protected Bl: number = SHA384_IV[3] | 0; - protected Ch: number = SHA384_IV[4] | 0; - protected Cl: number = SHA384_IV[5] | 0; - protected Dh: number = SHA384_IV[6] | 0; - protected Dl: number = SHA384_IV[7] | 0; - protected Eh: number = SHA384_IV[8] | 0; - protected El: number = SHA384_IV[9] | 0; - protected Fh: number = SHA384_IV[10] | 0; - protected Fl: number = SHA384_IV[11] | 0; - protected Gh: number = SHA384_IV[12] | 0; - protected Gl: number = SHA384_IV[13] | 0; - protected Hh: number = SHA384_IV[14] | 0; - protected Hl: number = SHA384_IV[15] | 0; - - constructor() { - super(48); - } -} - -/** - * 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<_SHA512_224> { - protected Ah: number = T224_IV[0] | 0; - protected Al: number = T224_IV[1] | 0; - protected Bh: number = T224_IV[2] | 0; - protected Bl: number = T224_IV[3] | 0; - protected Ch: number = T224_IV[4] | 0; - protected Cl: number = T224_IV[5] | 0; - protected Dh: number = T224_IV[6] | 0; - protected Dl: number = T224_IV[7] | 0; - protected Eh: number = T224_IV[8] | 0; - protected El: number = T224_IV[9] | 0; - protected Fh: number = T224_IV[10] | 0; - protected Fl: number = T224_IV[11] | 0; - protected Gh: number = T224_IV[12] | 0; - protected Gl: number = T224_IV[13] | 0; - protected Hh: number = T224_IV[14] | 0; - protected Hl: number = T224_IV[15] | 0; - - constructor() { - super(28); - } -} - -/** 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<_SHA512_256> { - protected Ah: number = T256_IV[0] | 0; - protected Al: number = T256_IV[1] | 0; - protected Bh: number = T256_IV[2] | 0; - protected Bl: number = T256_IV[3] | 0; - protected Ch: number = T256_IV[4] | 0; - protected Cl: number = T256_IV[5] | 0; - protected Dh: number = T256_IV[6] | 0; - protected Dl: number = T256_IV[7] | 0; - protected Eh: number = T256_IV[8] | 0; - protected El: number = T256_IV[9] | 0; - protected Fh: number = T256_IV[10] | 0; - protected Fl: number = T256_IV[11] | 0; - protected Gh: number = T256_IV[12] | 0; - protected Gl: number = T256_IV[13] | 0; - protected Hh: number = T256_IV[14] | 0; - protected Hl: number = T256_IV[15] | 0; - - constructor() { - super(32); - } -} - -/** - * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info: - * - * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack. - * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025. - * - Each sha256 hash is executing 2^18 bit operations. - * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA2-256. - * ```ts - * sha256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha256: TRet> = /* @__PURE__ */ createHasher( - () => new _SHA256(), - /* @__PURE__ */ oidNist(0x01) -); -/** - * SHA2-224 hash function from RFC 4634. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA2-224. - * ```ts - * sha224(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha224: TRet> = /* @__PURE__ */ createHasher( - () => new _SHA224(), - /* @__PURE__ */ oidNist(0x04) -); - -/** - * SHA2-512 hash function from RFC 4634. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA2-512. - * ```ts - * sha512(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha512: TRet> = /* @__PURE__ */ createHasher( - () => new _SHA512(), - /* @__PURE__ */ oidNist(0x03) -); -/** - * SHA2-384 hash function from RFC 4634. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA2-384. - * ```ts - * sha384(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha384: TRet> = /* @__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 - * @returns Digest bytes. - * @example - * Hash a message with SHA2-512/256. - * ```ts - * sha512_256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha512_256: TRet> = /* @__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 - * @returns Digest bytes. - * @example - * Hash a message with SHA2-512/224. - * ```ts - * sha512_224(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha512_224: TRet> = /* @__PURE__ */ createHasher( - () => new _SHA512_224(), - /* @__PURE__ */ oidNist(0x05) -); diff --git a/packages/noble-hashes/src/sha3-addons.ts b/packages/noble-hashes/src/sha3-addons.ts deleted file mode 100644 index e7d348ac..00000000 --- a/packages/noble-hashes/src/sha3-addons.ts +++ /dev/null @@ -1,983 +0,0 @@ -/** - * SHA3 (keccak) addons. - * - * * cSHAKE, KMAC, TupleHash, ParallelHash + XOF variants from - * {@link https://csrc.nist.gov/pubs/sp/800/185/final | NIST SP 800-185} - * * KangarooTwelve 🦘 and TurboSHAKE - reduced-round keccak from - * {@link https://datatracker.ietf.org/doc/rfc9861/ | RFC 9861} - * * KeccakPRG: Pseudo-random generator based on Keccak - * ({@link https://keccak.team/files/CSF-0.1.pdf | pdf}) - * @module - */ -import { Keccak, type ShakeOpts } from './sha3'; -import { - abytes, - aexists, - anumber, - type CHash, - type CHashXOF, - clean, - copyBytes, - createHasher, - type Hash, - type HashXOF, - type KDFInput, - kdfInputToBytes, - type PRG, - type TArg, - type TRet, - u32, -} from './utils'; - -// cSHAKE && KMAC (NIST SP800-185) -const _8n = /* @__PURE__ */ BigInt(8); -const _ffn = /* @__PURE__ */ BigInt(0xff); - -// It is safe to use bigints here, since they used only for length encoding (not actual data). -// We use bigints in sha256 for lengths too. -// Callers are still expected to supply SP 800-185-valid lengths -// (`0 <= x < 2^2040`); this helper does not enforce that bound. -function leftEncode(n: number | bigint): TRet { - n = BigInt(n); - const res = [Number(n & _ffn)]; - n >>= _8n; - for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn)); - res.unshift(res.length); - return new Uint8Array(res) as TRet; -} - -// Same caller contract as `leftEncode(...)`: lengths must already satisfy SP 800-185 §2.3.1. -function rightEncode(n: number | bigint): TRet { - n = BigInt(n); - const res = [Number(n & _ffn)]; - n >>= _8n; - for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn)); - res.push(res.length); - return new Uint8Array(res) as TRet; -} - -// `dkLen` validation is deferred to the downstream Keccak constructor. -function chooseLen(opts: ShakeOpts, outputLen: number): number { - return opts.dkLen === undefined ? outputLen : opts.dkLen; -} - -const abytesOrZero = (buf?: TArg, title = '') => { - if (buf === undefined) return EMPTY_BUFFER; - abytes(buf, undefined, title); - return buf; -}; -// NOTE: second modulo is necessary since we don't need to add padding if the -// current element takes a whole block. -// Callers only pass the fixed positive Keccak rates here (`168` or `136`); -// `block <= 0` is not validated locally. -const getPadding = (len: number, block: number) => new Uint8Array((block - (len % block)) % block); -/** Options for cSHAKE and related SP 800-185 functions. */ -export type cShakeOpts = ShakeOpts & { - /** Optional personalization string mixed into domain separation. */ - personalization?: Uint8Array; - /** - * Optional NIST function-name string used for domain separation. - * SP 800-185 reserves this for standardized function names; applications - * should generally stick to `personalization`. - */ - NISTfn?: KDFInput; -}; - -// Personalization -function cshakePers(hash: TArg, opts: TArg = {}): TRet { - const h = hash as unknown as Keccak; - if (!opts || (opts.personalization === undefined && opts.NISTfn === undefined)) - return h as TRet; - // Encode and pad inplace to avoid unneccesary memory copies/slices so we - // don't need to zero them later. - // bytepad(encode_string(N) || encode_string(S), rate), where `rate` is the - // current cSHAKE/KMAC/TupleHash/ParallelHash block length. - const blockLenBytes = leftEncode(h.blockLen); - const fn = opts.NISTfn === undefined ? EMPTY_BUFFER : kdfInputToBytes(opts.NISTfn); - const fnLen = leftEncode(_8n * BigInt(fn.length)); // length in bits - const pers = abytesOrZero(opts.personalization, 'personalization'); - const persLen = leftEncode(_8n * BigInt(pers.length)); // length in bits - if (!fn.length && !pers.length) return h as TRet; - // SP 800-185 cSHAKE appends `00` instead of SHAKE's `1111`; in this Keccak implementation - // that changes the delimited suffix byte from `0x1f` to `0x04` once N or S is non-empty. - h.suffix = 0x04; - h.update(blockLenBytes).update(fnLen).update(fn).update(persLen).update(pers); - let totalLen = blockLenBytes.length + fnLen.length + fn.length + persLen.length + pers.length; - h.update(getPadding(totalLen, h.blockLen)); - return h as TRet; -} - -const gencShake = ( - suffix: number, - blockLen: number, - outputLen: number -): TRet> => - createHasher( - (opts: TArg = {}) => - cshakePers( - new Keccak(blockLen, suffix, chooseLen(opts, outputLen), true) as unknown as TArg, - opts - ) as Keccak - ); - -/** TupleHash callable interface. */ -export type ITupleHash = { - /** - * Hashes an ordered tuple of byte arrays. - * @param messages - Ordered byte-array tuple to hash. - * @param opts - TupleHash output and personalization options. See {@link cShakeOpts}. - * @returns Digest bytes. - */ - (messages: TArg, opts?: TArg): TRet; - /** - * Creates an incremental TupleHash state. - * @param opts - TupleHash output and personalization options. See {@link cShakeOpts}. - * @returns Stateful TupleHash instance. - */ - create(opts?: cShakeOpts): _TupleHash; -}; -/** - * 128-bit NIST cSHAKE XOF. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and NIST function-name - * settings. When both `NISTfn` and `personalization` are empty, - * SP 800-185 defines this as plain SHAKE128. Defaults to 16 output bytes - * when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with cSHAKE128. - * ```ts - * cshake128(new Uint8Array([1, 2, 3]), { dkLen: 32 }); - * ``` - */ -export const cshake128: TRet> = /* @__PURE__ */ gencShake( - 0x1f, - 168, - 16 -); -/** - * 256-bit NIST cSHAKE XOF. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and NIST function-name - * settings. When both `NISTfn` and `personalization` are empty, - * SP 800-185 defines this as plain SHAKE256. Defaults to 32 output bytes - * when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with cSHAKE256. - * ```ts - * cshake256(new Uint8Array([1, 2, 3]), { dkLen: 64 }); - * ``` - */ -export const cshake256: TRet> = /* @__PURE__ */ gencShake( - 0x1f, - 136, - 32 -); - -/** - * Internal KMAC class. - * SP 800-185 §8.4.1 still recommends keys at least as long as the target - * security strength. - */ -export class _KMAC extends Keccak implements HashXOF<_KMAC> { - constructor( - blockLen: number, - outputLen: number, - enableXOF: boolean, - key: TArg, - opts: TArg = {} - ) { - super(blockLen, 0x1f, outputLen, enableXOF); - // Preload T = bytepad(encode_string("KMAC") || encode_string(S), rate); later updates append - // newX = bytepad(encode_string(K), rate) || X and `finish()` appends right_encode(L or 0). - cshakePers(this as unknown as TArg, { - NISTfn: 'KMAC', - personalization: opts.personalization, - }); - abytes(key, undefined, 'key'); - // 1. newX = bytepad(encode_string(K), rate) || X || right_encode(L), - // with `rate = this.blockLen`. - const blockLenBytes = leftEncode(this.blockLen); - const keyLen = leftEncode(_8n * BigInt(key.length)); - this.update(blockLenBytes).update(keyLen).update(key); - const totalLen = blockLenBytes.length + keyLen.length + key.length; - this.update(getPadding(totalLen, this.blockLen)); - } - protected finish(): void { - // SP 800-185 uses right_encode(L) for fixed-length KMAC and right_encode(0) for KMACXOF. - // outputLen in bits - if (!this.finished) this.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); - super.finish(); - } - _cloneInto(to?: _KMAC): _KMAC { - // Create new instance without calling constructor since the key - // is already in state and we don't know it. - // Force "to" to be instance of KMAC instead of Sha3. - if (!to) { - to = Object.create(Object.getPrototypeOf(this), {}) as _KMAC; - to.state = this.state.slice(); - to.blockLen = this.blockLen; - to.state32 = u32(to.state); - } - return super._cloneInto(to) as _KMAC; - } - clone(): _KMAC { - return this._cloneInto(); - } -} - -function genKmac(blockLen: number, outputLen: number, xof = false): TRet { - // One-shot XOF wrappers still finalize via `.digest()` because `_KMAC` - // already bakes the requested output length into the state. - const kmac = ( - key: TArg, - message: TArg, - opts?: TArg - ): TRet => kmac.create(key, opts).update(message).digest(); - kmac.create = (key: TArg, opts: TArg = {}) => - new _KMAC(blockLen, chooseLen(opts, outputLen), xof, key, opts); - return kmac as TRet; -} - -/** KMAC callable interface. */ -export type IKMAC = { - /** - * Computes a keyed KMAC digest for one message. - * @param key - Secret key bytes. - * @param message - Message bytes to authenticate. - * @param opts - KMAC output and personalization options. See {@link KangarooOpts}. - * @returns Authentication tag bytes. - */ - (key: TArg, message: TArg, opts?: TArg): TRet; - /** - * Creates an incremental KMAC state. - * @param key - Secret key bytes. - * @param opts - KMAC output and personalization options. See {@link cShakeOpts}. - * @returns Stateful KMAC instance. - */ - create(key: TArg, opts?: TArg): _KMAC; -}; -/** - * 128-bit Keccak MAC. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param opts - Optional output and personalization settings. Defaults to - * 16 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Authentication tag bytes. - * @example - * Authenticate a message with KMAC128. - * ```ts - * kmac128(new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])); - * ``` - */ -export const kmac128: TRet = /* @__PURE__ */ genKmac(168, 16); -/** - * 256-bit Keccak MAC. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param opts - Optional output and personalization settings. Defaults to - * 32 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Authentication tag bytes. - * @example - * Authenticate a message with KMAC256. - * ```ts - * kmac256(new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])); - * ``` - */ -export const kmac256: TRet = /* @__PURE__ */ genKmac(136, 32); -/** - * 128-bit Keccak-MAC XOF. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param opts - Optional output and personalization settings. Defaults to - * 16 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Authentication tag bytes. - * @example - * Authenticate a message with KMAC128 XOF output. - * ```ts - * kmac128xof(new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), { dkLen: 32 }); - * ``` - */ -export const kmac128xof: TRet = /* @__PURE__ */ genKmac(168, 16, true); -/** - * 256-bit Keccak-MAC XOF. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param opts - Optional output and personalization settings. Defaults to - * 32 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Authentication tag bytes. - * @example - * Authenticate a message with KMAC256 XOF output. - * ```ts - * kmac256xof(new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), { dkLen: 64 }); - * ``` - */ -export const kmac256xof: TRet = /* @__PURE__ */ genKmac(136, 32, true); - -/** - * Internal TupleHash class for byte-array tuple elements. - * This implementation relies on SP 800-185's byte-oriented encoding form - * rather than arbitrary bit strings. - */ -export class _TupleHash extends Keccak implements HashXOF<_TupleHash> { - constructor( - blockLen: number, - outputLen: number, - enableXOF: boolean, - opts: TArg = {} - ) { - super(blockLen, 0x1f, outputLen, enableXOF); - cshakePers(this as unknown as TArg, { - NISTfn: 'TupleHash', - personalization: opts.personalization, - }); - // Change update after cshake processed - this.update = (data: TArg) => { - abytes(data); - // SP 800-185 encodes each tuple element as - // encode_string(X[i]) = left_encode(len(X[i])) || X[i]. - super.update(leftEncode(_8n * BigInt(data.length))); - super.update(data); - return this; - }; - } - protected finish(): void { - // SP 800-185 uses right_encode(L) for fixed-length TupleHash - // and right_encode(0) for TupleHashXOF. - if (!this.finished) - // outputLen in bits - super.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); - super.finish(); - } - _cloneInto(to?: _TupleHash): _TupleHash { - to ||= new _TupleHash(this.blockLen, this.outputLen, this.enableXOF); - return super._cloneInto(to) as _TupleHash; - } - clone(): _TupleHash { - return this._cloneInto(); - } -} - -function genTuple(blockLen: number, outputLen: number, xof = false): TRet { - // One-shot XOF wrappers still use `.digest()` because `_TupleHash` stores - // the requested output length in the state itself. - const tuple = (messages: TArg, opts?: TArg): TRet => { - const h = tuple.create(opts); - if (!Array.isArray(messages)) throw new Error('expected array of messages'); - for (const msg of messages) h.update(msg); - return h.digest(); - }; - tuple.create = (opts: TArg = {}) => - new _TupleHash(blockLen, chooseLen(opts, outputLen), xof, opts); - return tuple as TRet; -} - -/** - * 128-bit TupleHASH. `tuple(['ab', 'cd']) != tuple(['a', 'bcd'])`. - * @param messages - ordered byte-array tuple - * @param opts - Optional output and personalization settings. Defaults to - * 16 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a tuple of byte arrays with TupleHash128. - * ```ts - * tuplehash128([new Uint8Array([1]), new Uint8Array([2])]); - * ``` - */ -export const tuplehash128: TRet = /* @__PURE__ */ genTuple(168, 16); -/** - * 256-bit TupleHASH. `tuple(['ab', 'cd']) != tuple(['a', 'bcd'])`. - * @param messages - ordered byte-array tuple - * @param opts - Optional output and personalization settings. Defaults to - * 32 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a tuple of byte arrays with TupleHash256. - * ```ts - * tuplehash256([new Uint8Array([1]), new Uint8Array([2])]); - * ``` - */ -export const tuplehash256: TRet = /* @__PURE__ */ genTuple(136, 32); -/** - * 128-bit TupleHASH XOF. - * @param messages - ordered byte-array tuple - * @param opts - Optional output and personalization settings. Defaults to - * 16 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a tuple of byte arrays with TupleHash128 XOF output. - * ```ts - * tuplehash128xof([new Uint8Array([1]), new Uint8Array([2])], { dkLen: 32 }); - * ``` - */ -export const tuplehash128xof: TRet = /* @__PURE__ */ genTuple(168, 16, true); -/** - * 256-bit TupleHASH XOF. - * @param messages - ordered byte-array tuple - * @param opts - Optional output and personalization settings. Defaults to - * 32 output bytes when `dkLen` is omitted. See {@link cShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a tuple of byte arrays with TupleHash256 XOF output. - * ```ts - * tuplehash256xof([new Uint8Array([1]), new Uint8Array([2])], { dkLen: 64 }); - * ``` - */ -export const tuplehash256xof: TRet = /* @__PURE__ */ genTuple(136, 32, true); - -// Same as K12/M14, but without speedup for inputs less 8kb, -// reduced number of rounds and simpler. -type ParallelOpts = KangarooOpts & { blockLen?: number }; - -/** Internal Parallel Keccak Hash class. */ -export class _ParallelHash extends Keccak implements HashXOF<_ParallelHash> { - private leafHash?: Hash; - protected leafCons: () => Hash; - private chunkPos = 0; // Position of current block in chunk - private chunksDone = 0; // How many chunks we already have - private chunkLen: number; - constructor( - blockLen: number, - outputLen: number, - leafCons: () => Hash, - enableXOF: boolean, - opts: TArg = {} - ) { - super(blockLen, 0x1f, outputLen, enableXOF); - cshakePers(this as unknown as TArg, { - NISTfn: 'ParallelHash', - personalization: opts.personalization, - }); - this.leafCons = leafCons; - let { blockLen: B = 8 } = opts; - anumber(B); - // blockLen=0 makes take=0 in update(), so pos never advances and the hash hangs. - if (B < 1) throw new Error('"blockLen" must be >= 1, got ' + B); - this.chunkLen = B; - // SP 800-185 initializes z = left_encode(B); each completed chunk appends - // one fixed-size cSHAKE leaf digest before finish() adds right_encode(n) - // and right_encode(L or 0). - super.update(leftEncode(B)); - // Change update after cshake processed - this.update = (data: TArg) => { - abytes(data); - const { chunkLen, leafCons } = this; - for (let pos = 0, len = data.length; pos < len; ) { - if (this.chunkPos == chunkLen || !this.leafHash) { - if (this.leafHash) { - super.update(this.leafHash.digest()); - this.chunksDone++; - } - this.leafHash = leafCons(); - this.chunkPos = 0; - } - const take = Math.min(chunkLen - this.chunkPos, len - pos); - this.leafHash.update(data.subarray(pos, pos + take)); - this.chunkPos += take; - pos += take; - } - return this; - }; - } - protected finish(): void { - if (this.finished) return; - if (this.leafHash) { - super.update(this.leafHash.digest()); - this.chunksDone++; - } - // SP 800-185 finishes ParallelHash as - // z || right_encode(n) || right_encode(L); XOF mode replaces - // right_encode(L) with right_encode(0). - super.update(rightEncode(this.chunksDone)); - // outputLen in bits - super.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); - super.finish(); - } - _cloneInto(to?: _ParallelHash): _ParallelHash { - to ||= new _ParallelHash(this.blockLen, this.outputLen, this.leafCons, this.enableXOF); - to.leafCons = this.leafCons; - // Reused destinations can carry a stale partial leaf - // when the source is still on the root sponge. - if (this.leafHash) to.leafHash = this.leafHash._cloneInto(to.leafHash as Keccak); - else if (to.leafHash) { - to.leafHash.destroy(); - to.leafHash = undefined; - } - to.chunkPos = this.chunkPos; - to.chunkLen = this.chunkLen; - to.chunksDone = this.chunksDone; - return super._cloneInto(to) as _ParallelHash; - } - destroy(): void { - super.destroy.call(this); - if (this.leafHash) this.leafHash.destroy(); - } - clone(): _ParallelHash { - return this._cloneInto(); - } -} - -function genPrl( - blockLen: number, - outputLen: number, - leaf: ReturnType, - xof = false -): TRet> { - const parallel = (message: TArg, opts?: TArg): TRet => - parallel.create(opts).update(message).digest(); - parallel.create = (opts: TArg = {}) => - new _ParallelHash( - blockLen, - chooseLen(opts, outputLen), - // SP 800-185 fixes leaf digests at 256 bits for ParallelHash128 and - // 512 bits for ParallelHash256; only the final cSHAKE output uses the - // caller-selected dkLen. - () => leaf.create({ dkLen: 2 * outputLen }), - xof, - opts - ); - parallel.outputLen = outputLen; - parallel.blockLen = blockLen; - parallel.canXOF = xof; - return parallel as TRet>; -} - -/** - * 128-bit ParallelHash. In JS, it is not parallel. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and chunking settings. - * Defaults to 16 output bytes when `dkLen` is omitted. - * See {@link ParallelOpts}. - * @returns Digest bytes. - * @example - * Hash a message with ParallelHash128. - * ```ts - * parallelhash128(new Uint8Array([1, 2, 3])); - * ``` - */ -export const parallelhash128: TRet> = /* @__PURE__ */ genPrl( - 168, - 16, - cshake128 -); -/** - * 256-bit ParallelHash. In JS, it is not parallel. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and chunking settings. - * Defaults to 32 output bytes when `dkLen` is omitted. - * See {@link ParallelOpts}. - * @returns Digest bytes. - * @example - * Hash a message with ParallelHash256. - * ```ts - * parallelhash256(new Uint8Array([1, 2, 3])); - * ``` - */ -export const parallelhash256: TRet> = /* @__PURE__ */ genPrl( - 136, - 32, - cshake256 -); -/** - * 128-bit ParallelHash XOF. In JS, it is not parallel. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and chunking settings. - * Defaults to 16 output bytes when `dkLen` is omitted. - * See {@link ParallelOpts}. - * @returns Digest bytes. - * @example - * Hash a message with ParallelHash128 XOF output. - * ```ts - * parallelhash128xof(new Uint8Array([1, 2, 3]), { dkLen: 32 }); - * ``` - */ -export const parallelhash128xof: TRet> = /* @__PURE__ */ genPrl( - 168, - 16, - cshake128, - true -); -/** - * 256-bit ParallelHash XOF. In JS, it is not parallel. - * @param msg - message bytes to hash - * @param opts - Optional output, personalization, and chunking settings. - * Defaults to 32 output bytes when `dkLen` is omitted. - * See {@link ParallelOpts}. - * @returns Digest bytes. - * @example - * Hash a message with ParallelHash256 XOF output. - * ```ts - * parallelhash256xof(new Uint8Array([1, 2, 3]), { dkLen: 64 }); - * ``` - */ -export const parallelhash256xof: TRet> = /* @__PURE__ */ genPrl( - 136, - 32, - cshake256, - true -); - -/** - * TurboSHAKE options. - * `D` is the domain separation byte; RFC 9861 defines output length `L` - * as a positive integer. - */ -export type TurboshakeOpts = ShakeOpts & { - /** Optional domain separation byte in the `0x01..0x7f` range. */ - D?: number; -}; - -const genTurbo = (blockLen: number, outputLen: number) => - createHasher((opts: TArg = {}) => { - const D = opts.D === undefined ? 0x1f : opts.D; - // RFC 9861 §2.1 fixes the default `D = 0x1f`; §2.2 defines the 12-round - // TurboSHAKE family selected here. - if (!Number.isSafeInteger(D) || D < 0x01 || D > 0x7f) - throw new Error('"D" (domain separation byte) must be 0x01..0x7f, got: ' + D); - const dkLen = opts.dkLen === undefined ? outputLen : opts.dkLen; - // RFC 9861 §§2.1-2.2 define output length L as a positive integer. - if (dkLen < 1) throw new Error('"dkLen" must be >= 1'); - return new Keccak(blockLen, D, dkLen, true, 12); - }); - -/** - * TurboSHAKE 128-bit: reduced 12-round keccak. - * Should've been a simple "shake with 12 rounds", but we got a whole new - * spec about Turbo SHAKE Pro MAX. - * @param msg - message bytes to hash - * @param opts - Optional output-length and domain-separation settings. - * RFC 9861 §2.1 defaults `D` to `0x1f`. Defaults to 32 output bytes when - * `dkLen` is omitted. See {@link TurboshakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with TurboSHAKE128. - * ```ts - * turboshake128(new Uint8Array([1, 2, 3]), { dkLen: 32 }); - * ``` - */ -export const turboshake128: TRet> = /* @__PURE__ */ genTurbo( - 168, - 32 -); -/** - * TurboSHAKE 256-bit: reduced 12-round keccak. - * @param msg - message bytes to hash - * @param opts - Optional output-length and domain-separation settings. - * RFC 9861 §2.1 defaults `D` to `0x1f`. Defaults to 64 output bytes when - * `dkLen` is omitted. See {@link TurboshakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with TurboSHAKE256. - * ```ts - * turboshake256(new Uint8Array([1, 2, 3]), { dkLen: 64 }); - * ``` - */ -export const turboshake256: TRet> = /* @__PURE__ */ genTurbo( - 136, - 64 -); - -// Same as NIST rightEncode, but returns `[0]` for the zero string. -// Callers still need to keep `x < 256^255` per RFC 9861 §3.3. -function rightEncodeK12(n: number | bigint): TRet { - n = BigInt(n); - const res: number[] = []; - for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn)); - res.push(res.length); - return Uint8Array.from(res); -} - -/** K12 options. */ -export type KangarooOpts = { - /** - * Desired digest length in bytes. - * RFC 9861 §3 defines output length `L` as a positive integer. - */ - dkLen?: number; - /** - * Optional personalization string mixed into the sponge state. - * Stateful K12 instances keep an internal copy so caller buffers can be - * wiped independently. - */ - personalization?: Uint8Array; -}; -const EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of(); - -/** Internal K12 hash class. */ -export class _KangarooTwelve extends Keccak implements HashXOF<_KangarooTwelve> { - readonly chunkLen = 8192; - private leafHash?: Keccak; - protected leafLen: number; - private personalization: Uint8Array; - private chunkPos = 0; // Position of current block in chunk - private chunksDone = 0; // How many chunks we already have - constructor( - blockLen: number, - leafLen: number, - outputLen: number, - rounds: number, - opts: TArg - ) { - super(blockLen, 0x07, outputLen, true, rounds); - // RFC 9861 §3 defines output length L as a positive integer. - if (outputLen < 1) throw new Error('"dkLen" must be >= 1'); - this.leafLen = leafLen; - this.personalization = - opts.personalization === undefined - ? EMPTY_BUFFER - : copyBytes(abytes(opts.personalization, undefined, 'personalization')); - } - update(data: TArg): this { - abytes(data); - const { chunkLen, blockLen, leafLen, rounds } = this; - for (let pos = 0, len = data.length; pos < len; ) { - if (this.chunkPos == chunkLen) { - if (this.leafHash) super.update(this.leafHash.digest()); - else { - // RFC 9861 §3.2 switches from SingleNode (`07`) to FinalNode (`06`) - // once S exceeds 8192 bytes and prefixes S_0 with - // `03 00 00 00 00 00 00 00`. - this.suffix = 0x06; // Its safe to change suffix here since its used only in digest() - super.update(Uint8Array.from([3, 0, 0, 0, 0, 0, 0, 0])); - } - // Secondary chunks S_1..S_(n-1) become fixed-length - // CV_i = TurboSHAKE*(S_i, `0B`, 32|64) chaining values. - this.leafHash = new Keccak(blockLen, 0x0b, leafLen, false, rounds); - this.chunksDone++; - this.chunkPos = 0; - } - const take = Math.min(chunkLen - this.chunkPos, len - pos); - const chunk = data.subarray(pos, pos + take); - if (this.leafHash) this.leafHash.update(chunk); - else super.update(chunk); - this.chunkPos += take; - pos += take; - } - return this; - } - protected finish(): void { - if (this.finished) return; - const { personalization } = this; - // RFC 9861 §3.2 forms S = M || C || length_encode(|C|) before any tree hashing logic. - this.update(personalization).update(rightEncodeK12(personalization.length)); - // Leaf hash - if (this.leafHash) { - // Multi-chunk K12 appends - // CV_1..CV_(n-1) || length_encode(n-1) || `FF FF` - // before the final TurboSHAKE call. - super.update(this.leafHash.digest()); - super.update(rightEncodeK12(this.chunksDone)); - super.update(Uint8Array.from([0xff, 0xff])); - } - super.finish.call(this); - } - destroy(): void { - super.destroy.call(this); - if (this.leafHash) this.leafHash.destroy(); - // Personalization is copied on create/clone, so destroy can wipe it - // without touching caller input. - if (this.personalization !== EMPTY_BUFFER) clean(this.personalization); - this.personalization = EMPTY_BUFFER; - } - _cloneInto(to?: _KangarooTwelve): _KangarooTwelve { - const { blockLen, leafLen, leafHash, outputLen, rounds } = this; - const personalization = - this.personalization === EMPTY_BUFFER ? EMPTY_BUFFER : copyBytes(this.personalization); - // Personalization is absorbed only during finish(), so clones need the same pending value. - to ||= new _KangarooTwelve(blockLen, leafLen, outputLen, rounds, { - personalization, - }); - super._cloneInto(to); - // Reused destinations can carry a stale leaf from an older multi-chunk state. - if (leafHash) to.leafHash = leafHash._cloneInto(to.leafHash); - else if (to.leafHash) { - to.leafHash.destroy(); - to.leafHash = undefined; - } - // Snapshot the pending personalization so clone state does not alias caller-owned input. - to.personalization = personalization; - to.leafLen = this.leafLen; - to.chunkPos = this.chunkPos; - to.chunksDone = this.chunksDone; - return to; - } - clone(): _KangarooTwelve { - return this._cloneInto(); - } -} - -/** - * 128-bit KangarooTwelve (k12): reduced 12-round keccak. - * @param msg - message bytes to hash - * @param opts - Optional output and personalization settings. Defaults to - * 32 output bytes when `dkLen` is omitted. See {@link KangarooOpts}. - * @returns Digest bytes. - * @example - * Hash a message with KangarooTwelve-128. - * ```ts - * kt128(new Uint8Array([1, 2, 3])); - * ``` - */ -export const kt128: TRet> = /* @__PURE__ */ createHasher( - (opts: TArg = {}) => new _KangarooTwelve(168, 32, chooseLen(opts, 32), 12, opts) -); -/** - * 256-bit KangarooTwelve (k12): reduced 12-round keccak. - * @param msg - message bytes to hash - * @param opts - Optional output and personalization settings. Defaults to - * 64 output bytes when `dkLen` is omitted. See {@link KangarooOpts}. - * @returns Digest bytes. - * @example - * Hash a message with KangarooTwelve-256. - * ```ts - * kt256(new Uint8Array([1, 2, 3])); - * ``` - */ -export const kt256: TRet> = /* @__PURE__ */ createHasher( - (opts: TArg = {}) => new _KangarooTwelve(136, 64, chooseLen(opts, 64), 12, opts) -); - -// MarsupilamiFourteen (14-rounds) can be defined as: -// `new KangarooTwelve(136, 64, chooseLen(opts, 64), 14, opts)` - -/** KangarooTwelve-based MAC function type. */ -export type HopMAC = ( - key: TArg, - message: TArg, - personalization: TArg, - dkLen?: number -) => TRet; -const genHopMAC = - (hash: TArg>): TRet => - ( - key: TArg, - message: TArg, - personalization: TArg, - dkLen?: number - ) => { - const h = hash as unknown as CHash<_KangarooTwelve, KangarooOpts>; - return h(key, { personalization: h(message, { personalization }), dkLen }) as TRet; - }; - -/** - * 128-bit KangarooTwelve-based MAC. - * - * These untested (there is no test vectors or implementation available). Use at your own risk. - * HopMAC128(Key, M, C, L) = KT128(Key, KT128(M, C, 32), L) - * HopMAC256(Key, M, C, L) = KT256(Key, KT256(M, C, 64), L) - * The inner KangarooTwelve call always uses a fixed 32-byte digest here, - * regardless of the outer `dkLen`. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param personalization - personalization bytes mixed into the inner hash - * @param dkLen - optional output length in bytes - * @returns Authentication tag bytes. - * @example - * Authenticate a message with HopMAC128. - * ```ts - * HopMAC128(new Uint8Array([1]), new Uint8Array([2]), new Uint8Array([3]), 32); - * ``` - */ -export const HopMAC128: TRet = /* @__PURE__ */ genHopMAC(kt128); -/** - * 256-bit KangarooTwelve-based MAC. - * Like `HopMAC128`, there are no test vectors or known independent - * implementations available for cross-checking. - * @param key - MAC key bytes - * @param message - message bytes to authenticate - * @param personalization - personalization bytes mixed into the inner hash - * @param dkLen - optional output length in bytes. The inner KangarooTwelve - * call still uses a fixed 64-byte digest here, regardless of the outer - * `dkLen`. - * @returns Authentication tag bytes. - * @example - * Authenticate a message with HopMAC256. - * ```ts - * HopMAC256(new Uint8Array([1]), new Uint8Array([2]), new Uint8Array([3]), 64); - * ``` - */ -export const HopMAC256: TRet = /* @__PURE__ */ genHopMAC(kt256); - -/** - * More at - * {@link https://github.com/XKCP/XKCP/tree/master/lib/high/Keccak/PRG}. - * Accepted capacities must keep `rho = 1598 - capacity` byte-aligned, and - * `.clean()` later also requires `rate > 801`. - */ -export class _KeccakPRG extends Keccak implements PRG { - protected rate: number; - constructor(capacity: number) { - anumber(capacity); - const rate = 1600 - capacity; - const rho = rate - 2; - // Rho must be full bytes - if (capacity < 0 || capacity > 1600 - 10 || rho % 8) throw new Error('invalid capacity'); - // blockLen = rho in bytes - super(rho / 8, 0, 0, true); - this.rate = rate; - this.posOut = Math.floor((rate + 7) / 8); - } - protected keccak(): void { - // Duplex padding - this.state[this.pos] ^= 0x01; - this.state[this.blockLen] ^= 0x02; // Rho is full bytes - super.keccak(); - this.pos = 0; - this.posOut = 0; - } - update(data: TArg): this { - super.update(data); - this.posOut = this.blockLen; - return this; - } - protected finish(): void {} - digestInto(_out: TArg): void { - throw new Error('digest is not allowed, use .randomBytes() instead'); - } - addEntropy(seed: TArg): void { - this.update(seed); - } - randomBytes(length: number): TRet { - return this.xof(length); - } - clean(): void { - // clean() mutates live sponge state just like randomBytes(), - // so destroyed instances must reject it. - aexists(this, false); - if (this.rate < 1600 / 2 + 1) throw new Error('rate is too low to use .forget()'); - this.keccak(); - for (let i = 0; i < this.blockLen; i++) this.state[i] = 0; - this.pos = this.blockLen; - this.keccak(); - this.posOut = this.blockLen; - } - _cloneInto(to?: _KeccakPRG): _KeccakPRG { - const { rate } = this; - to ||= new _KeccakPRG(1600 - rate); - super._cloneInto(to); - to.rate = rate; - return to; - } - clone(): _KeccakPRG { - return this._cloneInto(); - } -} - -/** - * KeccakPRG: pseudo-random generator based on Keccak. - * See {@link https://keccak.team/files/CSF-0.1.pdf}. - * @param capacity - sponge capacity in bits. Accepted values are those that - * keep `rho = 1598 - capacity` byte-aligned; the default `254` is chosen - * because it satisfies that duplex layout while leaving a wide byte-aligned - * rate. - * @returns PRG instance backed by a Keccak sponge. - * @example - * Create a Keccak-based pseudorandom generator and read bytes from it. - * ```ts - * const prg = keccakprg(254); - * prg.randomBytes(8); - * ``` - */ -export const keccakprg = (capacity = 254): TRet<_KeccakPRG> => - new _KeccakPRG(capacity) as TRet<_KeccakPRG>; diff --git a/packages/noble-hashes/src/sha3.ts b/packages/noble-hashes/src/sha3.ts deleted file mode 100644 index 59290dd5..00000000 --- a/packages/noble-hashes/src/sha3.ts +++ /dev/null @@ -1,484 +0,0 @@ -/** - * SHA3 (keccak) hash function, based on a new "Sponge function" design. - * Different from older hashes, the internal state is bigger than output size. - * - * Check out - * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf | FIPS-202}, - * {@link https://keccak.team/keccak.html | Website}, and - * {@link https://crypto.stackexchange.com/q/15727 | the differences between - * SHA-3 and Keccak}. - * - * Check out `sha3-addons` module for cSHAKE, k12, and others. - * @module - */ -import { rotlBH, rotlBL, rotlSH, rotlSL, split } from './_u64'; -// prettier-ignore -import { - abytes, aexists, anumber, aoutput, - type CHash, type CHashXOF, - clean, createHasher, - type Hash, - type HashInfo, - type HashXOF, - oidNist, - swap32IfBE, - type TArg, - type TRet, - u32} from './utils'; - -// No __PURE__ annotations in sha3 header: -// EVERYTHING is in fact used on every export. -// Various per round constants calculations -const _0n = BigInt(0); -const _1n = BigInt(1); -const _2n = BigInt(2); -const _7n = BigInt(7); -const _256n = BigInt(256); -// FIPS 202 Algorithm 5 rc(): when the outgoing bit is 1, the 8-bit LFSR xors -// taps 0, 4, 5, and 6, which compresses to the feedback mask `0x71`. -const _0x71n = BigInt(0x71); -const SHA3_PI: number[] = []; -const SHA3_ROTL: number[] = []; -const _SHA3_IOTA: bigint[] = []; // no pure annotation: var is always used -for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) { - // Pi - [x, y] = [y, (2 * x + 3 * y) % 5]; - SHA3_PI.push(2 * (5 * y + x)); - // Rotational - SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64); - // Iota - let t = _0n; - for (let j = 0; j < 7; j++) { - R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n; - if (R & _2n) t ^= _1n << ((_1n << BigInt(j)) - _1n); - } - _SHA3_IOTA.push(t); -} -const IOTAS = split(_SHA3_IOTA, true); -// `split(..., true)` keeps the local little-endian lane-word layout used by -// `state32`, so these `H` / `L` tables follow the file's first-word / -// second-word lane slots rather than `_u64.ts`'s usual high/low naming. -const SHA3_IOTA_H = IOTAS[0]; -const SHA3_IOTA_L = IOTAS[1]; - -// Left rotation (without 0, 32, 64) -const rotlH = (h: number, l: number, s: number) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s)); -const rotlL = (h: number, l: number, s: number) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s)); - -/** - * `keccakf1600` internal permutation, additionally allows adjusting the round count. - * @param s - 5x5 Keccak state encoded as 25 lanes split into 50 uint32 words - * in this file's local little-endian lane-word order - * @param rounds - number of rounds to execute - * @throws If `rounds` is outside the supported `1..24` range. {@link Error} - * @example - * Permute a Keccak state with the default 24 rounds. - * ```ts - * keccakP(new Uint32Array(50)); - * ``` - */ -export function keccakP(s: TArg, rounds: number = 24): void { - anumber(rounds, 'rounds'); - // This implementation precomputes only the standard Keccak-f[1600] 24-round Iota table. - if (rounds < 1 || rounds > 24) throw new Error('"rounds" expected integer 1..24'); - const B = new Uint32Array(5 * 2); - // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js) - for (let round = 24 - rounds; round < 24; round++) { - // Theta θ - for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; - for (let x = 0; x < 10; x += 2) { - const idx1 = (x + 8) % 10; - const idx0 = (x + 2) % 10; - const B0 = B[idx0]; - const B1 = B[idx0 + 1]; - const Th = rotlH(B0, B1, 1) ^ B[idx1]; - const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1]; - for (let y = 0; y < 50; y += 10) { - s[x + y] ^= Th; - s[x + y + 1] ^= Tl; - } - } - // Rho (ρ) and Pi (π) - let curH = s[2]; - let curL = s[3]; - for (let t = 0; t < 24; t++) { - const shift = SHA3_ROTL[t]; - const Th = rotlH(curH, curL, shift); - const Tl = rotlL(curH, curL, shift); - const PI = SHA3_PI[t]; - curH = s[PI]; - curL = s[PI + 1]; - s[PI] = Th; - s[PI + 1] = Tl; - } - // Chi (χ) - // Same as: - // for (let x = 0; x < 10; x++) B[x] = s[y + x]; - // for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; - for (let y = 0; y < 50; y += 10) { - const b0 = s[y], - b1 = s[y + 1], - b2 = s[y + 2], - b3 = s[y + 3]; - s[y] ^= ~s[y + 2] & s[y + 4]; - s[y + 1] ^= ~s[y + 3] & s[y + 5]; - s[y + 2] ^= ~s[y + 4] & s[y + 6]; - s[y + 3] ^= ~s[y + 5] & s[y + 7]; - s[y + 4] ^= ~s[y + 6] & s[y + 8]; - s[y + 5] ^= ~s[y + 7] & s[y + 9]; - s[y + 6] ^= ~s[y + 8] & b0; - s[y + 7] ^= ~s[y + 9] & b1; - s[y + 8] ^= ~b0 & b2; - s[y + 9] ^= ~b1 & b3; - } - // Iota (ι) - s[0] ^= SHA3_IOTA_H[round]; - s[1] ^= SHA3_IOTA_L[round]; - } - clean(B); -} - -/** - * Keccak sponge function. - * @param blockLen - absorb/squeeze rate in bytes - * @param suffix - domain separation suffix byte - * @param outputLen - default digest length in bytes. This base sponge only - * requires a non-negative integer; wrappers that need positive output - * lengths must enforce that themselves. - * @param enableXOF - whether XOF output is allowed - * @param rounds - number of Keccak-f rounds - * @example - * Build a sponge state, absorb bytes, then finalize a digest. - * ```ts - * const hash = new Keccak(136, 0x06, 32); - * hash.update(new Uint8Array([1, 2, 3])); - * hash.digest(); - * ``` - */ -export class Keccak implements Hash, HashXOF { - protected state: Uint8Array; - protected pos = 0; - protected posOut = 0; - protected finished = false; - protected state32: Uint32Array; - protected destroyed = false; - - public blockLen: number; - public suffix: number; - public outputLen: number; - public canXOF: boolean; - protected enableXOF = false; - protected rounds: number; - - // NOTE: we accept arguments in bytes instead of bits here. - constructor( - blockLen: number, - suffix: number, - outputLen: number, - enableXOF = false, - rounds: number = 24 - ) { - this.blockLen = blockLen; - this.suffix = suffix; - this.outputLen = outputLen; - this.enableXOF = enableXOF; - this.canXOF = enableXOF; - this.rounds = rounds; - // Can be passed from user as dkLen - anumber(outputLen, 'outputLen'); - // 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes - // 0 < blockLen < 200 - if (!(0 < blockLen && blockLen < 200)) - throw new Error('only keccak-f1600 function is supported'); - this.state = new Uint8Array(200); - this.state32 = u32(this.state); - } - clone(): Keccak { - return this._cloneInto(); - } - protected keccak(): void { - swap32IfBE(this.state32); - keccakP(this.state32, this.rounds); - swap32IfBE(this.state32); - this.posOut = 0; - this.pos = 0; - } - update(data: TArg): this { - aexists(this); - abytes(data); - const { blockLen, state } = this; - const len = data.length; - for (let pos = 0; pos < len; ) { - const take = Math.min(blockLen - this.pos, len - pos); - for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++]; - if (this.pos === blockLen) this.keccak(); - } - return this; - } - protected finish(): void { - if (this.finished) return; - this.finished = true; - const { state, suffix, pos, blockLen } = this; - // FIPS 202 appends the SHA3/SHAKE domain-separation suffix before pad10*1. - // These byte values already include the first padding bit, while the - // final `0x80` below supplies the closing `1` bit in the last rate byte. - state[pos] ^= suffix; - // If that combined suffix lands in the last rate byte and already sets - // bit 7, absorb it first so the final pad10*1 bit can be xored into a - // fresh block. - if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak(); - state[blockLen - 1] ^= 0x80; - this.keccak(); - } - protected writeInto(out: TArg): TRet { - aexists(this, false); - abytes(out); - this.finish(); - const bufferOut = this.state; - const { blockLen } = this; - for (let pos = 0, len = out.length; pos < len; ) { - if (this.posOut >= blockLen) this.keccak(); - const take = Math.min(blockLen - this.posOut, len - pos); - out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); - this.posOut += take; - pos += take; - } - return out as TRet; - } - xofInto(out: TArg): TRet { - // Plain SHA3/Keccak usage with XOF is probably a mistake, but this base - // class is also reused by SHAKE/cSHAKE/KMAC/TupleHash/ParallelHash/ - // TurboSHAKE/KangarooTwelve wrappers that intentionally enable XOF. - if (!this.enableXOF) throw new Error('XOF is not possible for this instance'); - return this.writeInto(out); - } - xof(bytes: number): TRet { - anumber(bytes); - return this.xofInto(new Uint8Array(bytes)); - } - digestInto(out: TArg): void { - aoutput(out, this); - if (this.finished) throw new Error('digest() was already called'); - // `aoutput(...)` allows oversized buffers; digestInto() must fill only the advertised digest. - this.writeInto(out.subarray(0, this.outputLen)); - this.destroy(); - } - digest(): TRet { - const out = new Uint8Array(this.outputLen); - this.digestInto(out); - return out as TRet; - } - destroy(): void { - this.destroyed = true; - clean(this.state); - } - _cloneInto(to?: Keccak): Keccak { - const { blockLen, suffix, outputLen, rounds, enableXOF } = this; - to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds); - // Reused destinations can come from a different rate/capacity variant, so clone must rewrite - // the sponge geometry as well as the state words. - to.blockLen = blockLen; - to.state32.set(this.state32); - to.pos = this.pos; - to.posOut = this.posOut; - to.finished = this.finished; - to.rounds = rounds; - // Suffix can change in cSHAKE - to.suffix = suffix; - to.outputLen = outputLen; - to.enableXOF = enableXOF; - // Clones must preserve the public capability bit too; `_KMAC` reuses this path and deep clone - // tests compare instance fields directly, so leaving `canXOF` behind makes the clone lie. - to.canXOF = this.canXOF; - to.destroyed = this.destroyed; - return to; - } -} - -const genKeccak = ( - suffix: number, - blockLen: number, - outputLen: number, - info: TArg = {} -) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info); - -/** - * SHA3-224 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA3-224. - * ```ts - * sha3_224(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha3_224: TRet = /* @__PURE__ */ genKeccak( - 0x06, - 144, - 28, - /* @__PURE__ */ oidNist(0x07) -); -/** - * SHA3-256 hash function. Different from keccak-256. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA3-256. - * ```ts - * sha3_256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha3_256: TRet = /* @__PURE__ */ genKeccak( - 0x06, - 136, - 32, - /* @__PURE__ */ oidNist(0x08) -); -/** - * SHA3-384 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA3-384. - * ```ts - * sha3_384(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha3_384: TRet = /* @__PURE__ */ genKeccak( - 0x06, - 104, - 48, - /* @__PURE__ */ oidNist(0x09) -); -/** - * SHA3-512 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with SHA3-512. - * ```ts - * sha3_512(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha3_512: TRet = /* @__PURE__ */ genKeccak( - 0x06, - 72, - 64, - /* @__PURE__ */ oidNist(0x0a) -); - -/** - * Keccak-224 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with Keccak-224. - * ```ts - * keccak_224(new Uint8Array([97, 98, 99])); - * ``` - */ -export const keccak_224: TRet = /* @__PURE__ */ genKeccak(0x01, 144, 28); -/** - * Keccak-256 hash function. Different from SHA3-256. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with Keccak-256. - * ```ts - * keccak_256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const keccak_256: TRet = /* @__PURE__ */ genKeccak(0x01, 136, 32); -/** - * Keccak-384 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with Keccak-384. - * ```ts - * keccak_384(new Uint8Array([97, 98, 99])); - * ``` - */ -export const keccak_384: TRet = /* @__PURE__ */ genKeccak(0x01, 104, 48); -/** - * Keccak-512 hash function. - * @param msg - message bytes to hash - * @returns Digest bytes. - * @example - * Hash a message with Keccak-512. - * ```ts - * keccak_512(new Uint8Array([97, 98, 99])); - * ``` - */ -export const keccak_512: TRet = /* @__PURE__ */ genKeccak(0x01, 72, 64); - -/** Options for SHAKE XOF. */ -export type ShakeOpts = { - /** Desired number of output bytes. */ - dkLen?: number; -}; - -const genShake = (suffix: number, blockLen: number, outputLen: number, info: TArg = {}) => - createHasher( - (opts: ShakeOpts = {}) => - new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true), - info - ); - -/** - * SHAKE128 XOF with 128-bit security and a 16-byte default output. - * @param msg - message bytes to hash - * @param opts - Optional output-length override. See {@link ShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with SHAKE128. - * ```ts - * shake128(new Uint8Array([97, 98, 99]), { dkLen: 32 }); - * ``` - */ -export const shake128: TRet> = - /* @__PURE__ */ - genShake(0x1f, 168, 16, /* @__PURE__ */ oidNist(0x0b)); -/** - * SHAKE256 XOF with 256-bit security and a 32-byte default output. - * @param msg - message bytes to hash - * @param opts - Optional output-length override. See {@link ShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with SHAKE256. - * ```ts - * shake256(new Uint8Array([97, 98, 99]), { dkLen: 64 }); - * ``` - */ -export const shake256: TRet> = - /* @__PURE__ */ - genShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c)); - -/** - * SHAKE128 XOF with 256-bit output (NIST version). - * @param msg - message bytes to hash - * @param opts - Optional output-length override. See {@link ShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with SHAKE128 using a 32-byte default output. - * ```ts - * shake128_32(new Uint8Array([97, 98, 99]), { dkLen: 32 }); - * ``` - */ -export const shake128_32: TRet> = - /* @__PURE__ */ - genShake(0x1f, 168, 32, /* @__PURE__ */ oidNist(0x0b)); -/** - * SHAKE256 XOF with 512-bit output (NIST version). - * @param msg - message bytes to hash - * @param opts - Optional output-length override. See {@link ShakeOpts}. - * @returns Digest bytes. - * @example - * Hash a message with SHAKE256 using a 64-byte default output. - * ```ts - * shake256_64(new Uint8Array([97, 98, 99]), { dkLen: 64 }); - * ``` - */ -export const shake256_64: TRet> = - /* @__PURE__ */ - genShake(0x1f, 136, 64, /* @__PURE__ */ oidNist(0x0c)); diff --git a/packages/noble-hashes/src/utils.ts b/packages/noble-hashes/src/utils.ts deleted file mode 100644 index 961dab26..00000000 --- a/packages/noble-hashes/src/utils.ts +++ /dev/null @@ -1,848 +0,0 @@ -/** - * Utilities for hex, bytes, CSPRNG. - * @module - */ -/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ -/** - * Bytes API type helpers for old + new TypeScript. - * - * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array`. - * We can't use specific return type, because TS 5.6 will error. - * We can't use generic return type, because most TS 5.9 software will expect specific type. - * - * Maps typed-array input leaves to broad forms. - * These are compatibility adapters, not ownership guarantees. - * - * - `TArg` keeps byte inputs broad. - * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility. - */ -export type TypedArg = T extends BigInt64Array - ? BigInt64Array - : T extends BigUint64Array - ? BigUint64Array - : T extends Float32Array - ? Float32Array - : T extends Float64Array - ? Float64Array - : T extends Int16Array - ? Int16Array - : T extends Int32Array - ? Int32Array - : T extends Int8Array - ? Int8Array - : T extends Uint16Array - ? Uint16Array - : T extends Uint32Array - ? Uint32Array - : T extends Uint8ClampedArray - ? Uint8ClampedArray - : T extends Uint8Array - ? Uint8Array - : never; -/** Maps typed-array output leaves to narrow TS-compatible forms. */ -export type TypedRet = T extends BigInt64Array - ? ReturnType - : T extends BigUint64Array - ? ReturnType - : T extends Float32Array - ? ReturnType - : T extends Float64Array - ? ReturnType - : T extends Int16Array - ? ReturnType - : T extends Int32Array - ? ReturnType - : T extends Int8Array - ? ReturnType - : T extends Uint16Array - ? ReturnType - : T extends Uint32Array - ? ReturnType - : T extends Uint8ClampedArray - ? ReturnType - : T extends Uint8Array - ? ReturnType - : never; -/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */ -export type TArg = - | T - | ([TypedArg] extends [never] - ? T extends (...args: infer A) => infer R - ? ((...args: { [K in keyof A]: TRet }) => TArg) & { - [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg; - } - : T extends [infer A, ...infer R] - ? [TArg, ...{ [K in keyof R]: TArg }] - : T extends readonly [infer A, ...infer R] - ? readonly [TArg, ...{ [K in keyof R]: TArg }] - : T extends (infer A)[] - ? TArg[] - : T extends readonly (infer A)[] - ? readonly TArg[] - : T extends Promise - ? Promise> - : T extends object - ? { [K in keyof T]: TArg } - : T - : TypedArg); -/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */ -export type TRet = T extends unknown - ? T & - ([TypedRet] extends [never] - ? T extends (...args: infer A) => infer R - ? ((...args: { [K in keyof A]: TArg }) => TRet) & { - [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet; - } - : T extends [infer A, ...infer R] - ? [TRet, ...{ [K in keyof R]: TRet }] - : T extends readonly [infer A, ...infer R] - ? readonly [TRet, ...{ [K in keyof R]: TRet }] - : T extends (infer A)[] - ? TRet[] - : T extends readonly (infer A)[] - ? readonly TRet[] - : T extends Promise - ? Promise> - : T extends object - ? { [K in keyof T]: TRet } - : T - : TypedRet) - : never; -/** - * 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: unknown): a is Uint8Array { - // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases. - // The fallback still requires a real ArrayBuffer view, so plain - // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and - // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views. - return ( - a instanceof Uint8Array || - (ArrayBuffer.isView(a) && - a.constructor.name === 'Uint8Array' && - 'BYTES_PER_ELEMENT' in a && - a.BYTES_PER_ELEMENT === 1) - ); -} - -/** - * Asserts something is a non-negative integer. - * @param n - number to validate - * @param title - label included in thrown errors - * @throws On wrong argument types. {@link TypeError} - * @throws On wrong argument ranges or values. {@link RangeError} - * @example - * Validate a non-negative integer option. - * ```ts - * anumber(32, 'length'); - * ``` - */ -export function anumber(n: number, title: string = ''): void { - if (typeof n !== 'number') { - const prefix = title && `"${title}" `; - throw new TypeError(`${prefix}expected number, got ${typeof n}`); - } - if (!Number.isSafeInteger(n) || n < 0) { - const prefix = title && `"${title}" `; - throw new RangeError(`${prefix}expected integer >= 0, got ${n}`); - } -} - -/** - * Asserts something is Uint8Array. - * @param value - value to validate - * @param length - optional exact length constraint - * @param title - label included in thrown errors - * @returns The validated byte array. - * @throws On wrong argument types. {@link TypeError} - * @throws On wrong argument ranges or values. {@link RangeError} - * @example - * Validate that a value is a byte array. - * ```ts - * abytes(new Uint8Array([1, 2, 3])); - * ``` - */ -export function abytes( - value: TArg, - length?: number, - title: string = '' -): TRet { - const bytes = isBytes(value); - const len = value?.length; - const needsLen = length !== undefined; - if (!bytes || (needsLen && len !== length)) { - const prefix = title && `"${title}" `; - const ofLen = needsLen ? ` of length ${length}` : ''; - const got = bytes ? `length=${len}` : `type=${typeof value}`; - const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got; - if (!bytes) throw new TypeError(message); - throw new RangeError(message); - } - return value as TRet; -} - -/** - * 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: TArg): TRet { - // `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)) as TRet; -} - -/** - * 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: TArg): void { - if (typeof h !== 'function' || typeof h.create !== 'function') - throw new TypeError('Hash must wrapped by utils.createHasher'); - anumber(h.outputLen); - anumber(h.blockLen); - // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass - // validation and can produce empty outputs instead of failing fast. - if (h.outputLen < 1) throw new Error('"outputLen" must be >= 1'); - if (h.blockLen < 1) throw new Error('"blockLen" must be >= 1'); -} - -/** - * Asserts a hash instance has not been destroyed or finished. - * @param instance - hash instance to validate - * @param checkFinished - whether to reject finalized instances - * @throws If the hash instance has already been destroyed or finalized. {@link Error} - * @example - * Validate that a hash instance is still usable. - * ```ts - * import { aexists } from '@noble/hashes/utils.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const hash = sha256.create(); - * aexists(hash); - * ``` - */ -export function aexists(instance: any, checkFinished = true): void { - if (instance.destroyed) throw new Error('Hash instance has been destroyed'); - if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called'); -} - -/** - * Asserts output is a sufficiently-sized byte array. - * @param out - destination buffer - * @param instance - hash instance providing output length - * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes. - * @throws On wrong argument types. {@link TypeError} - * @throws On wrong argument ranges or values. {@link RangeError} - * @example - * Validate a caller-provided digest buffer. - * ```ts - * import { aoutput } from '@noble/hashes/utils.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const hash = sha256.create(); - * aoutput(new Uint8Array(hash.outputLen), hash); - * ``` - */ -export function aoutput(out: any, instance: any): void { - abytes(out, undefined, 'digestInto() output'); - const min = instance.outputLen; - if (out.length < min) { - throw new RangeError('"digestInto() output" expected to be of length >=' + min); - } -} - -/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */ -// prettier-ignore -export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array | - Uint16Array | Int16Array | Uint32Array | Int32Array; - -/** - * 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: TArg): TRet { - return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet; -} - -/** - * 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: TArg): TRet { - return new Uint32Array( - arr.buffer, - arr.byteOffset, - Math.floor(arr.byteLength / 4) - ) as TRet; -} - -/** - * 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: TArg): void { - 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: TArg): DataView { - 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: number, shift: number): number { - 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: number, shift: number): number { - return (word << shift) | ((word >>> (32 - shift)) >>> 0); -} - -/** Whether the current platform is little-endian. */ -export const isLE: boolean = /* @__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: number): number { - 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: (n: number) => number = isLE - ? (n: number) => n - : (n: number) => 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: TArg): TRet { - for (let i = 0; i < arr.length; i++) { - arr[i] = byteSwap(arr[i]); - } - return arr as TRet; -} - -/** - * 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: (u: TArg) => TRet = isLE - ? (u: TArg) => u as TRet - : byteSwap32; - -// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex -const hasHexBuiltin: boolean = /* @__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: TArg): string { - abytes(bytes); - // @ts-ignore - if (hasHexBuiltin) return bytes.toHex(); - // pre-caching improves the speed 6x - let hex = ''; - for (let i = 0; i < bytes.length; i++) { - hex += hexes[bytes[i]]; - } - return hex; -} - -// We use optimized technique to convert hex string to byte array -const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const; -function asciiToBase16(ch: number): number | undefined { - if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48 - if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10) - if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10) - return; -} - -/** - * Convert hex string to byte array. Uses built-in function, when available. - * @param hex - hexadecimal string to decode - * @returns Decoded bytes. - * @throws On wrong argument types. {@link TypeError} - * @throws On wrong argument ranges or values. {@link RangeError} - * @example - * Decode lowercase hexadecimal into bytes. - * ```ts - * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23]) - * ``` - */ -export function hexToBytes(hex: string): TRet { - if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex); - if (hasHexBuiltin) { - try { - return (Uint8Array as any).fromHex(hex); - } catch (error) { - if (error instanceof SyntaxError) throw new RangeError(error.message); - throw error; - } - } - const hl = hex.length; - const al = hl / 2; - if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl); - const array = new Uint8Array(al); - for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { - const n1 = asciiToBase16(hex.charCodeAt(hi)); - const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); - if (n1 === undefined || n2 === undefined) { - const char = hex[hi] + hex[hi + 1]; - throw new RangeError( - 'hex string expected, got non-hex character "' + char + '" at index ' + hi - ); - } - array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163 - } - return array; -} - -/** - * There is no setImmediate in browser and setTimeout is slow. - * This yields to the Promise/microtask scheduler queue, not to timers or the - * full macrotask event loop. - * @example - * Yield to the next scheduler tick. - * ```ts - * await nextTick(); - * ``` - */ -export const nextTick = async (): Promise => {}; - -/** - * Returns control to the Promise/microtask scheduler every `tick` - * milliseconds to avoid blocking long loops. - * @param iters - number of loop iterations to run - * @param tick - maximum time slice in milliseconds - * @param cb - callback executed on each iteration - * @example - * Run a loop that periodically yields back to the event loop. - * ```ts - * await asyncLoop(2, 0, () => {}); - * ``` - */ -export async function asyncLoop( - iters: number, - tick: number, - cb: (i: number) => void -): Promise { - let ts = Date.now(); - for (let i = 0; i < iters; i++) { - cb(i); - // Date.now() is not monotonic, so in case if clock goes backwards we return return control too - const diff = Date.now() - ts; - if (diff >= 0 && diff < tick) continue; - await nextTick(); - ts += diff; - } -} - -// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535 -declare const TextEncoder: any; - -/** - * 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: string): TRet { - if (typeof str !== 'string') throw new TypeError('string expected'); - return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809 -} - -/** KDFs can accept string or Uint8Array for user convenience. */ -export type KDFInput = string | Uint8Array; - -/** - * 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: TArg, errorTitle = ''): TRet { - 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: TArg): TRet { - 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; -} - -type EmptyObj = {}; -/** - * Merges default options and passed options. - * @param defaults - base option object - * @param opts - user overrides - * @returns Merged option object. The merge mutates `defaults` in place. - * @throws On wrong argument types. {@link TypeError} - * @example - * Merge user overrides onto default options. - * ```ts - * checkOpts({ dkLen: 32 }, { asyncTick: 10 }); - * ``` - */ -export function checkOpts( - defaults: T1, - opts?: T2 -): T1 & T2 { - if (opts !== undefined && {}.toString.call(opts) !== '[object Object]') - throw new TypeError('options must be object or undefined'); - const merged = Object.assign(defaults, opts); - return merged as T1 & T2; -} - -/** Common interface for all hash instances. */ -export interface Hash { - /** Bytes processed per compression block. */ - blockLen: number; - /** Bytes produced by `digest()`. */ - outputLen: number; - /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */ - canXOF: boolean; - /** - * Absorbs more message bytes into the running hash state. - * @param buf - message chunk to absorb - * @returns The same hash instance for chaining. - */ - update(buf: TArg): this; - /** - * Finalizes the hash into a caller-provided buffer. - * @param buf - destination buffer - * @returns Nothing. Implementations write into `buf` in place. - */ - digestInto(buf: TArg): void; - /** - * Finalizes the hash and returns a freshly allocated digest. - * @returns Digest bytes. - */ - digest(): TRet; - /** Wipes internal state and makes the instance unusable. */ - destroy(): void; - /** - * Copies the current hash state into an existing or new instance. - * @param to - Optional destination instance to reuse. - * @returns Cloned hash state. - */ - _cloneInto(to?: T): T; - /** - * Creates an independent copy of the current hash state. - * @returns Cloned hash instance. - */ - clone(): T; -} - -/** Pseudorandom generator interface. */ -export interface PRG { - /** - * Mixes more entropy into the generator state. - * @param seed - fresh entropy bytes - * @returns Nothing. Implementations update internal state in place. - */ - addEntropy(seed: TArg): void; - /** - * Generates pseudorandom output bytes. - * @param length - number of bytes to generate - * @returns Generated pseudorandom bytes. - */ - randomBytes(length: number): TRet; - /** Wipes generator state and makes the instance unusable. */ - clean(): void; -} - -/** - * XOF: streaming API to read digest in chunks. - * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name. - * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot - * destroy state, next call can require more bytes. - */ -export type HashXOF> = Hash & { - /** - * Reads more bytes from the XOF stream. - * @param bytes - number of bytes to read - * @returns Requested digest bytes. - */ - xof(bytes: number): TRet; - /** - * Reads more bytes from the XOF stream into a caller-provided buffer. - * @param buf - destination buffer - * @returns Filled output buffer. - */ - xofInto(buf: TArg): TRet; -}; - -/** Hash constructor or factory type. */ -export type HasherCons = Opts extends undefined ? () => T : (opts?: Opts) => T; -/** Optional hash metadata. */ -export type HashInfo = { - /** DER-encoded object identifier bytes for the hash algorithm. */ - oid?: TRet; -}; -/** Callable hash function type. */ -export type CHash = Hash, Opts = undefined> = { - /** Digest size in bytes. */ - outputLen: number; - /** Input block size in bytes. */ - blockLen: number; - /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */ - canXOF: boolean; -} & HashInfo & - (Opts extends undefined - ? { - (msg: TArg): TRet; - create(): T; - } - : { - (msg: TArg, opts?: TArg): TRet; - create(opts?: Opts): T; - }); -/** Callable extendable-output hash function type. */ -export type CHashXOF = HashXOF, Opts = undefined> = CHash; - -/** - * Creates a callable hash function from a stateful class constructor. - * @param hashCons - hash constructor or factory - * @param info - optional metadata such as DER OID - * @returns Frozen callable hash wrapper with `.create()`. - * Wrapper construction eagerly calls `hashCons(undefined)` once to read - * `outputLen` / `blockLen`, so constructor side effects happen at module - * init time. - * @example - * Wrap a stateful hash constructor into a callable helper. - * ```ts - * import { createHasher } from '@noble/hashes/utils.js'; - * import { sha256 } from '@noble/hashes/sha2.js'; - * const wrapped = createHasher(sha256.create, { oid: sha256.oid }); - * wrapped(new Uint8Array([1])); - * ``` - */ -export function createHasher, Opts = undefined>( - hashCons: HasherCons, - info: TArg = {} -): TRet> { - const hashC: any = (msg: TArg, opts?: TArg) => - hashCons(opts as Opts) - .update(msg) - .digest(); - const tmp = hashCons(undefined); - hashC.outputLen = tmp.outputLen; - hashC.blockLen = tmp.blockLen; - hashC.canXOF = tmp.canXOF; - hashC.create = (opts?: Opts) => hashCons(opts); - Object.assign(hashC, info); - return Object.freeze(hashC) as TRet>; -} - -/** - * 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): TRet { - // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion. - anumber(bytesLength, 'bytesLength'); - const cr = typeof globalThis === 'object' ? (globalThis as any).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: number): TRet> => ({ - // 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]), -}); diff --git a/packages/noble-hashes/src/webcrypto.ts b/packages/noble-hashes/src/webcrypto.ts deleted file mode 100644 index 95eca040..00000000 --- a/packages/noble-hashes/src/webcrypto.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { type Pbkdf2Opt } from './pbkdf2'; -import { - abytes, - ahash, - anumber, - type CHash, - checkOpts, - type KDFInput, - kdfInputToBytes, - type TArg, - type TRet, -} from './utils'; - -function _subtle(): typeof crypto.subtle { - const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null; - const sb = cr?.subtle; - if (typeof sb === 'object' && sb != null) return sb; - throw new Error('crypto.subtle must be defined'); -} - -/** Callable WebCrypto hash function descriptor. */ -export type WebHash = { - /** - * Hashes one message with the selected WebCrypto digest. - * @param msg - message bytes to hash - * @returns Promise resolving to digest bytes. - */ - (msg: TArg): Promise>; - /** WebCrypto algorithm name passed to `crypto.subtle`. */ - webCryptoName: string; - /** Digest size in bytes. */ - outputLen: number; - /** Input block size in bytes. */ - blockLen: number; -}; - -function createWebHash(name: string, blockLen: number, outputLen: number): TRet { - const hashC: any = async (msg: TArg): Promise> => { - abytes(msg); - const crypto = _subtle(); - return new Uint8Array(await crypto.digest(name, msg as BufferSource)) as TRet; - }; - hashC.webCryptoName = name; // make sure it won't interfere with function name - hashC.outputLen = outputLen; - hashC.blockLen = blockLen; - hashC.create = () => { - // Present only so this async wrapper satisfies the shared - // hash-wrapper shape checked by `ahashWeb()`. - throw new Error('not implemented'); - }; - // Later WebCrypto HMAC/HKDF/PBKDF2 calls read descriptor metadata directly, so freezing prevents - // callers from retargeting a `sha256` wrapper into a different backend digest by mutation. - return Object.freeze(hashC) as TRet; -} - -function ahashWeb(hash: TArg) { - ahash(hash as unknown as TArg); - if (typeof hash.webCryptoName !== 'string') throw new Error('non-web hash'); -} - -/** WebCrypto SHA1 (RFC 3174) legacy hash function. It was cryptographically broken. */ -// export const sha1: WebHash = createHash('SHA-1', 64, 20); - -/** - * WebCrypto SHA2-256 hash function from RFC 6234. - * @param msg - message bytes to hash - * @returns Promise resolving to digest bytes. - * @example - * Hash a message with WebCrypto SHA2-256. - * ```ts - * await sha256(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha256: TRet = /* @__PURE__ */ createWebHash('SHA-256', 64, 32); -/** - * WebCrypto SHA2-384 hash function from RFC 6234. - * @param msg - message bytes to hash - * @returns Promise resolving to digest bytes. - * @example - * Hash a message with WebCrypto SHA2-384. - * ```ts - * await sha384(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha384: TRet = /* @__PURE__ */ createWebHash('SHA-384', 128, 48); -/** - * WebCrypto SHA2-512 hash function from RFC 6234. - * @param msg - message bytes to hash - * @returns Promise resolving to digest bytes. - * @example - * Hash a message with WebCrypto SHA2-512. - * ```ts - * await sha512(new Uint8Array([97, 98, 99])); - * ``` - */ -export const sha512: TRet = /* @__PURE__ */ createWebHash('SHA-512', 128, 64); - -/** - * WebCrypto HMAC: RFC2104 message authentication code. - * @param hash - function that would be used e.g. sha256. Webcrypto version. - * @param key - authentication key bytes - * @param message - message bytes to authenticate - * @returns Promise resolving to authentication tag bytes. - * `.create()` exists only to mirror the synchronous API surface - * and always throws `not implemented`. - * @example - * Compute an RFC 2104 HMAC with WebCrypto. - * ```ts - * import { hmac, sha256 } from '@noble/hashes/webcrypto.js'; - * await hmac(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])); - * ``` - */ -type WebHmacFn = { - ( - hash: TArg, - key: TArg, - message: TArg - ): Promise>; - create(hash: TArg, key: TArg): any; -}; -export const hmac: TRet = /* @__PURE__ */ (() => { - const hmac_ = async ( - hash: TArg, - key: TArg, - message: TArg - ): Promise> => { - const crypto = _subtle(); - abytes(key, undefined, 'key'); - abytes(message, undefined, 'message'); - ahashWeb(hash); - // WebCrypto keys can't be zeroized - // prettier-ignore - const wkey = await crypto.importKey( - 'raw', - key as BufferSource, - { name: 'HMAC', hash: hash.webCryptoName }, - false, - ['sign'] - ); - return new Uint8Array( - await crypto.sign('HMAC', wkey, message as BufferSource) - ) as TRet; - }; - hmac_.create = (_hash: TArg, _key: TArg) => { - throw new Error('not implemented'); - }; - return hmac_ as TRet; -})(); - -/** - * WebCrypto HKDF (RFC 5869): derive keys from an initial input. - * Combines hkdf_extract + hkdf_expand in one step - * @param hash - hash function that would be used (e.g. sha256). Webcrypto version. - * @param ikm - input keying material, the initial key - * @param salt - optional salt value (a non-secret random value) - * @param info - optional context and application specific information bytes - * @param length - length of output keying material in bytes. - * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` requests an empty OKM. - * @returns Promise resolving to derived key bytes. - * The RFC `L <= 255 * HashLen` bound is currently enforced only by backend - * `deriveBits()` rejection, not by an explicit library-side guard. - * @throws If the current runtime does not provide `crypto.subtle`. {@link Error} - * @example - * WebCrypto HKDF (RFC 5869): derive keys from an initial input. - * ```ts - * import { hkdf, sha256 } from '@noble/hashes/webcrypto.js'; - * import { randomBytes, utf8ToBytes } from '@noble/hashes/utils.js'; - * const inputKey = randomBytes(32); - * const salt = randomBytes(32); - * const info = utf8ToBytes('application-key'); - * const okm = await hkdf(sha256, inputKey, salt, info, 32); - * ``` - */ -export async function hkdf( - hash: TArg, - ikm: TArg, - salt: TArg, - info: TArg, - length: number -): Promise> { - const crypto = _subtle(); - ahashWeb(hash); - abytes(ikm, undefined, 'ikm'); - anumber(length, 'length'); - if (salt !== undefined) abytes(salt, undefined, 'salt'); - if (info !== undefined) abytes(info, undefined, 'info'); - const wkey = await crypto.importKey('raw', ikm as BufferSource, 'HKDF', false, ['deriveBits']); - const opts = { - name: 'HKDF', - hash: hash.webCryptoName, - salt: salt === undefined ? new Uint8Array(0) : salt, - info: info === undefined ? new Uint8Array(0) : info, - }; - return new Uint8Array(await crypto.deriveBits(opts, wkey, 8 * length)) as TRet; -} - -/** - * WebCrypto PBKDF2-HMAC: RFC 8018 key derivation function. - * @param hash - hash function that would be used e.g. sha256. Webcrypto version. - * @param password - password from which a derived key is generated; string - * inputs are normalized through `kdfInputToBytes()`, i.e. UTF-8 - * @param salt - cryptographic salt; string inputs are normalized through - * `kdfInputToBytes()`, i.e. UTF-8 - * @param opts - PBKDF2 work factor and output settings. `dkLen`, if provided, - * must be `>= 1` per RFC 8018 §5.2. See {@link Pbkdf2Opt}. - * @returns Promise resolving to derived key bytes. - * Positive-iteration enforcement is currently delegated to backend - * `deriveBits()` rejection (for example `c = 0`), not a dedicated - * library-side guard. - * @throws If the current runtime does not provide `crypto.subtle`. {@link Error} - * @example - * WebCrypto PBKDF2-HMAC: RFC 2898 key derivation function. - * ```ts - * import { pbkdf2, sha256 } from '@noble/hashes/webcrypto.js'; - * const key = await pbkdf2(sha256, 'password', 'salt', { dkLen: 32, c: Math.pow(2, 18) }); - * ``` - */ -export async function pbkdf2( - hash: TArg, - password: TArg, - salt: TArg, - opts: Pbkdf2Opt -): Promise> { - const crypto = _subtle(); - ahashWeb(hash); - const _opts = checkOpts({ dkLen: 32 }, opts); - const { c, dkLen } = _opts; - anumber(c, 'c'); - anumber(dkLen, 'dkLen'); - // RFC 8018 §5.2 defines dkLen as a positive integer. - if (dkLen < 1) throw new Error('"dkLen" must be >= 1'); - const _password = kdfInputToBytes(password, 'password'); - const _salt = kdfInputToBytes(salt, 'salt'); - const key = await crypto.importKey('raw', _password as BufferSource, 'PBKDF2', false, [ - 'deriveBits', - ]); - const deriveOpts = { name: 'PBKDF2', salt: _salt, iterations: c, hash: hash.webCryptoName }; - return new Uint8Array(await crypto.deriveBits(deriveOpts, key, 8 * dkLen)) as TRet; -} diff --git a/packages/noble-hashes/tsconfig.esm.json b/packages/noble-hashes/tsconfig.esm.json deleted file mode 100644 index 800d7506..00000000 --- a/packages/noble-hashes/tsconfig.esm.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "dist/esm", - "module": "es2022", - "rootDir": "src/", - "declaration": false - } -} diff --git a/packages/noble-hashes/tsconfig.json b/packages/noble-hashes/tsconfig.json deleted file mode 100644 index 1a9d5696..00000000 --- a/packages/noble-hashes/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src/" - }, - "include": ["src/**/*.ts"], - "exclude": ["dist", "node_modules", "**/*.spec.*", "**/*.test.*"] -}