UNPKG

@0xfutbol/id

Version:

React component library with shared providers for 0xFutbol ID

1,201 lines (1,200 loc) 80.8 kB
import {s as sha256}from'./sha256-DQJaatHj.js';import {b4 as Hash,bz as ahash,b7 as toBytes,b6 as aexists,bA as abytes,bB as numberToBytesLE,bC as numberToBytesBE,bD as bitMask,bE as bytesToNumberLE,bF as bytesToNumberBE,bG as validateObject,bH as bitLen,bI as concatBytes,bJ as ensureBytes,bK as aInRange,bL as bytesToHex,bM as hexToBytes,bN as isBytes,bO as createHmacDrbg,bP as memoized,bQ as abool,bR as inRange,bS as ut,bT as abytes$1,bU as numberToHexUnpadded,bV as randomBytes,bW as concatBytes$1}from'./index-BJCJzM2_.js';import'react';import'react/jsx-runtime';import'@0xfutbol/id-sign';import'react-use';import'@0xfutbol/constants';import'thirdweb';import'@matchain/matchid-sdk-react';import'@tanstack/react-query';import'@matchain/matchid-sdk-react/index.css';import'react-dom';/** * HMAC: RFC2104 message authentication code. * @module */ class HMAC extends Hash { constructor(hash, _key) { super(); this.finished = false; this.destroyed = false; ahash(hash); const key = toBytes(_key); this.iHash = hash.create(); if (typeof this.iHash.update !== 'function') throw new Error('Expected instance of class which extends utils.Hash'); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad = new Uint8Array(blockLen); // blockLen can be bigger than outputLen pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36; this.iHash.update(pad); // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone this.oHash = hash.create(); // Undo internal XOR && apply outer XOR for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c; this.oHash.update(pad); pad.fill(0); } update(buf) { aexists(this); this.iHash.update(buf); return this; } digestInto(out) { aexists(this); abytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { // Create new instance without calling constructor since key already in state and we don't know it. to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } destroy() { 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 - message key * @param message - message data * @example * import { hmac } from '@noble/hashes/hmac'; * import { sha256 } from '@noble/hashes/sha2'; * const mac1 = hmac(sha256, 'key', 'message'); */ const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest(); hmac.create = (hash, key) => new HMAC(hash, key);/** * Utils for modular division and finite fields. * A finite field over 11 is integer number operations `mod 11`. * There is no division: it is replaced by modular multiplicative inverse. * @module */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ // prettier-ignore const _0n$2 = BigInt(0), _1n$3 = BigInt(1), _2n$1 = /* @__PURE__ */ BigInt(2), _3n$1 = /* @__PURE__ */ BigInt(3); // prettier-ignore const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8); // Calculates a modulo b function mod(a, b) { const result = a % b; return result >= _0n$2 ? result : b + result; } /** * Efficiently raise num to power and do modular division. * Unsafe in some contexts: uses ladder, so can expose bigint bits. * @todo use field version && remove * @example * pow(2n, 6n, 11n) // 64n % 11n == 9n */ function pow(num, power, modulo) { if (power < _0n$2) throw new Error('invalid exponent, negatives unsupported'); if (modulo <= _0n$2) throw new Error('invalid modulus'); if (modulo === _1n$3) return _0n$2; let res = _1n$3; while (power > _0n$2) { if (power & _1n$3) res = (res * num) % modulo; num = (num * num) % modulo; power >>= _1n$3; } return res; } /** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */ function pow2(x, power, modulo) { let res = x; while (power-- > _0n$2) { res *= res; res %= modulo; } return res; } /** * Inverses number over modulo. * Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/). */ function invert(number, modulo) { if (number === _0n$2) throw new Error('invert: expected non-zero number'); if (modulo <= _0n$2) throw new Error('invert: expected positive modulus, got ' + modulo); // Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower. let a = mod(number, modulo); let b = modulo; // prettier-ignore let x = _0n$2, u = _1n$3; while (a !== _0n$2) { // JIT applies optimization if those two lines follow each other const q = b / a; const r = b % a; const m = x - u * q; // prettier-ignore b = a, a = r, x = u, u = m; } const gcd = b; if (gcd !== _1n$3) throw new Error('invert: does not exist'); return mod(x, modulo); } /** * Tonelli-Shanks square root search algorithm. * 1. https://eprint.iacr.org/2012/685.pdf (page 12) * 2. Square Roots from 1; 24, 51, 10 to Dan Shanks * Will start an infinite loop if field order P is not prime. * @param P field order * @returns function that takes field Fp (created from P) and number n */ function tonelliShanks(P) { // Legendre constant: used to calculate Legendre symbol (a | p), // which denotes the value of a^((p-1)/2) (mod p). // (a | p) ≡ 1 if a is a square (mod p) // (a | p) ≡ -1 if a is not a square (mod p) // (a | p) ≡ 0 if a ≡ 0 (mod p) const legendreC = (P - _1n$3) / _2n$1; let Q, S, Z; // Step 1: By factoring out powers of 2 from p - 1, // find q and s such that p - 1 = q*(2^s) with q odd for (Q = P - _1n$3, S = 0; Q % _2n$1 === _0n$2; Q /= _2n$1, S++) ; // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq for (Z = _2n$1; Z < P && pow(Z, legendreC, P) !== P - _1n$3; Z++) { // Crash instead of infinity loop, we cannot reasonable count until P. if (Z > 1000) throw new Error('Cannot find square root: likely non-prime P'); } // Fast-path if (S === 1) { const p1div4 = (P + _1n$3) / _4n; return function tonelliFast(Fp, n) { const root = Fp.pow(n, p1div4); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); return root; }; } // Slow-path const Q1div2 = (Q + _1n$3) / _2n$1; return function tonelliSlow(Fp, n) { // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1 if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root'); let r = S; // TODO: will fail at Fp2/etc let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b let x = Fp.pow(n, Q1div2); // first guess at the square root let b = Fp.pow(n, Q); // first guess at the fudge factor while (!Fp.eql(b, Fp.ONE)) { if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0) // Find m such b^(2^m)==1 let m = 1; for (let t2 = Fp.sqr(b); m < r; m++) { if (Fp.eql(t2, Fp.ONE)) break; t2 = Fp.sqr(t2); // t2 *= t2 } // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow const ge = Fp.pow(g, _1n$3 << BigInt(r - m - 1)); // ge = 2^(r-m-1) g = Fp.sqr(ge); // g = ge * ge x = Fp.mul(x, ge); // x *= ge b = Fp.mul(b, g); // b *= g r = m; } return x; }; } /** * Square root for a finite field. It will try to check if optimizations are applicable and fall back to 4: * * 1. P ≡ 3 (mod 4) * 2. P ≡ 5 (mod 8) * 3. P ≡ 9 (mod 16) * 4. Tonelli-Shanks algorithm * * Different algorithms can give different roots, it is up to user to decide which one they want. * For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve). */ function FpSqrt(P) { // P ≡ 3 (mod 4) // √n = n^((P+1)/4) if (P % _4n === _3n$1) { // Not all roots possible! // const ORDER = // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; // const NUM = 72057594037927816n; const p1div4 = (P + _1n$3) / _4n; return function sqrt3mod4(Fp, n) { const root = Fp.pow(n, p1div4); // Throw if root**2 != n if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); return root; }; } // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10) if (P % _8n === _5n) { const c1 = (P - _5n) / _8n; return function sqrt5mod8(Fp, n) { const n2 = Fp.mul(n, _2n$1); const v = Fp.pow(n2, c1); const nv = Fp.mul(n, v); const i = Fp.mul(Fp.mul(nv, _2n$1), v); const root = Fp.mul(nv, Fp.sub(i, Fp.ONE)); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); return root; }; } // Other cases: Tonelli-Shanks algorithm return tonelliShanks(P); } // prettier-ignore const FIELD_FIELDS = [ 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr', 'eql', 'add', 'sub', 'mul', 'pow', 'div', 'addN', 'subN', 'mulN', 'sqrN' ]; function validateField(field) { const initial = { ORDER: 'bigint', MASK: 'bigint', BYTES: 'isSafeInteger', BITS: 'isSafeInteger', }; const opts = FIELD_FIELDS.reduce((map, val) => { map[val] = 'function'; return map; }, initial); return validateObject(field, opts); } // Generic field functions /** * Same as `pow` but for Fp: non-constant-time. * Unsafe in some contexts: uses ladder, so can expose bigint bits. */ function FpPow(f, num, power) { // Should have same speed as pow for bigints // TODO: benchmark! if (power < _0n$2) throw new Error('invalid exponent, negatives unsupported'); if (power === _0n$2) return f.ONE; if (power === _1n$3) return num; let p = f.ONE; let d = num; while (power > _0n$2) { if (power & _1n$3) p = f.mul(p, d); d = f.sqr(d); power >>= _1n$3; } return p; } /** * Efficiently invert an array of Field elements. * `inv(0)` will return `undefined` here: make sure to throw an error. */ function FpInvertBatch(f, nums) { const tmp = new Array(nums.length); // Walk from first to last, multiply them by each other MOD p const lastMultiplied = nums.reduce((acc, num, i) => { if (f.is0(num)) return acc; tmp[i] = acc; return f.mul(acc, num); }, f.ONE); // Invert last element const inverted = f.inv(lastMultiplied); // Walk from last to first, multiply them by inverted each other MOD p nums.reduceRight((acc, num, i) => { if (f.is0(num)) return acc; tmp[i] = f.mul(acc, tmp[i]); return f.mul(acc, num); }, inverted); return tmp; } // CURVE.n lengths function nLength(n, nBitLength) { // Bit size, byte size of CURVE.n const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length; const nByteLength = Math.ceil(_nBitLength / 8); return { nBitLength: _nBitLength, nByteLength }; } /** * Initializes a finite field over prime. * Major performance optimizations: * * a) denormalized operations like mulN instead of mul * * b) same object shape: never add or remove keys * * c) Object.freeze * Fragile: always run a benchmark on a change. * Security note: operations don't check 'isValid' for all elements for performance reasons, * it is caller responsibility to check this. * This is low-level code, please make sure you know what you're doing. * @param ORDER prime positive bigint * @param bitLen how many bits the field consumes * @param isLE (def: false) if encoding / decoding should be in little-endian * @param redef optional faster redefinitions of sqrt and other methods */ function Field(ORDER, bitLen, isLE = false, redef = {}) { if (ORDER <= _0n$2) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER); const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen); if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes'); let sqrtP; // cached sqrtP const f = Object.freeze({ ORDER, isLE, BITS, BYTES, MASK: bitMask(BITS), ZERO: _0n$2, ONE: _1n$3, create: (num) => mod(num, ORDER), isValid: (num) => { if (typeof num !== 'bigint') throw new Error('invalid field element: expected bigint, got ' + typeof num); return _0n$2 <= num && num < ORDER; // 0 is valid element, but it's not invertible }, is0: (num) => num === _0n$2, isOdd: (num) => (num & _1n$3) === _1n$3, neg: (num) => mod(-num, ORDER), eql: (lhs, rhs) => lhs === rhs, sqr: (num) => mod(num * num, ORDER), add: (lhs, rhs) => mod(lhs + rhs, ORDER), sub: (lhs, rhs) => mod(lhs - rhs, ORDER), mul: (lhs, rhs) => mod(lhs * rhs, ORDER), pow: (num, power) => FpPow(f, num, power), div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER), // Same as above, but doesn't normalize sqrN: (num) => num * num, addN: (lhs, rhs) => lhs + rhs, subN: (lhs, rhs) => lhs - rhs, mulN: (lhs, rhs) => lhs * rhs, inv: (num) => invert(num, ORDER), sqrt: redef.sqrt || ((n) => { if (!sqrtP) sqrtP = FpSqrt(ORDER); return sqrtP(f, n); }), invertBatch: (lst) => FpInvertBatch(f, lst), // TODO: do we really need constant cmov? // We don't have const-time bigints anyway, so probably will be not very useful cmov: (a, b, c) => (c ? b : a), toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)), fromBytes: (bytes) => { if (bytes.length !== BYTES) throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length); return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes); }, }); return Object.freeze(f); } /** * Returns total number of bytes consumed by the field element. * For example, 32 bytes for usual 256-bit weierstrass curve. * @param fieldOrder number of field elements, usually CURVE.n * @returns byte length of field */ function getFieldBytesLength(fieldOrder) { if (typeof fieldOrder !== 'bigint') throw new Error('field order must be bigint'); const bitLength = fieldOrder.toString(2).length; return Math.ceil(bitLength / 8); } /** * Returns minimal amount of bytes that can be safely reduced * by field order. * Should be 2^-128 for 128-bit curve such as P256. * @param fieldOrder number of field elements, usually CURVE.n * @returns byte length of target hash */ function getMinHashLength(fieldOrder) { const length = getFieldBytesLength(fieldOrder); return length + Math.ceil(length / 2); } /** * "Constant-time" private key generation utility. * Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF * and convert them into private scalar, with the modulo bias being negligible. * Needs at least 48 bytes of input for 32-byte private key. * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ * FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final * RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5 * @param hash hash output from SHA3 or a similar function * @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n) * @param isLE interpret hash bytes as LE num * @returns valid private scalar */ function mapHashToField(key, fieldOrder, isLE = false) { const len = key.length; const fieldLen = getFieldBytesLength(fieldOrder); const minLen = getMinHashLength(fieldOrder); // No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings. if (len < 16 || len < minLen || len > 1024) throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len); const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key); // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0 const reduced = mod(num, fieldOrder - _1n$3) + _1n$3; return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen); }/** * Methods for elliptic curve multiplication by scalars. * Contains wNAF, pippenger * @module */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ const _0n$1 = BigInt(0); const _1n$2 = BigInt(1); function constTimeNegate(condition, item) { const neg = item.negate(); return condition ? neg : item; } function validateW(W, bits) { if (!Number.isSafeInteger(W) || W <= 0 || W > bits) throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W); } function calcWOpts(W, bits) { validateW(W, bits); const windows = Math.ceil(bits / W) + 1; // +1, because const windowSize = 2 ** (W - 1); // -1 because we skip zero return { windows, windowSize }; } function validateMSMPoints(points, c) { if (!Array.isArray(points)) throw new Error('array expected'); points.forEach((p, i) => { if (!(p instanceof c)) throw new Error('invalid point at index ' + i); }); } function validateMSMScalars(scalars, field) { if (!Array.isArray(scalars)) throw new Error('array of scalars expected'); scalars.forEach((s, i) => { if (!field.isValid(s)) throw new Error('invalid scalar at index ' + i); }); } // Since points in different groups cannot be equal (different object constructor), // we can have single place to store precomputes const pointPrecomputes = new WeakMap(); const pointWindowSizes = new WeakMap(); // This allows use make points immutable (nothing changes inside) function getW(P) { return pointWindowSizes.get(P) || 1; } /** * Elliptic curve multiplication of Point by scalar. Fragile. * Scalars should always be less than curve order: this should be checked inside of a curve itself. * Creates precomputation tables for fast multiplication: * - private scalar is split by fixed size windows of W bits * - every window point is collected from window's table & added to accumulator * - since windows are different, same point inside tables won't be accessed more than once per calc * - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar) * - +1 window is neccessary for wNAF * - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication * * @todo Research returning 2d JS array of windows, instead of a single window. * This would allow windows to be in different memory locations */ function wNAF(c, bits) { return { constTimeNegate, hasPrecomputes(elm) { return getW(elm) !== 1; }, // non-const time multiplication ladder unsafeLadder(elm, n, p = c.ZERO) { let d = elm; while (n > _0n$1) { if (n & _1n$2) p = p.add(d); d = d.double(); n >>= _1n$2; } return p; }, /** * Creates a wNAF precomputation window. Used for caching. * Default window size is set by `utils.precompute()` and is equal to 8. * Number of precomputed points depends on the curve size: * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where: * - 𝑊 is the window size * - 𝑛 is the bitlength of the curve order. * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224. * @param elm Point instance * @param W window size * @returns precomputed point tables flattened to a single array */ precomputeWindow(elm, W) { const { windows, windowSize } = calcWOpts(W, bits); const points = []; let p = elm; let base = p; for (let window = 0; window < windows; window++) { base = p; points.push(base); // =1, because we skip zero for (let i = 1; i < windowSize; i++) { base = base.add(p); points.push(base); } p = base.double(); } return points; }, /** * Implements ec multiplication using precomputed tables and w-ary non-adjacent form. * @param W window size * @param precomputes precomputed tables * @param n scalar (we don't check here, but should be less than curve order) * @returns real and fake (for const-time) points */ wNAF(W, precomputes, n) { // TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise // But need to carefully remove other checks before wNAF. ORDER == bits here const { windows, windowSize } = calcWOpts(W, bits); let p = c.ZERO; let f = c.BASE; const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc. const maxNumber = 2 ** W; const shiftBy = BigInt(W); for (let window = 0; window < windows; window++) { const offset = window * windowSize; // Extract W bits. let wbits = Number(n & mask); // Shift number by W bits. n >>= shiftBy; // If the bits are bigger than max size, we'll split those. // +224 => 256 - 32 if (wbits > windowSize) { wbits -= maxNumber; n += _1n$2; } // This code was first written with assumption that 'f' and 'p' will never be infinity point: // since each addition is multiplied by 2 ** W, it cannot cancel each other. However, // there is negate now: it is possible that negated element from low value // would be the same as high element, which will create carry into next window. // It's not obvious how this can fail, but still worth investigating later. // Check if we're onto Zero point. // Add random point inside current window to f. const offset1 = offset; const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero const cond1 = window % 2 !== 0; const cond2 = wbits < 0; if (wbits === 0) { // The most important part for const-time getPublicKey f = f.add(constTimeNegate(cond1, precomputes[offset1])); } else { p = p.add(constTimeNegate(cond2, precomputes[offset2])); } } // JIT-compiler should not eliminate f here, since it will later be used in normalizeZ() // Even if the variable is still unused, there are some checks which will // throw an exception, so compiler needs to prove they won't happen, which is hard. // At this point there is a way to F be infinity-point even if p is not, // which makes it less const-time: around 1 bigint multiply. return { p, f }; }, /** * Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form. * @param W window size * @param precomputes precomputed tables * @param n scalar (we don't check here, but should be less than curve order) * @param acc accumulator point to add result of multiplication * @returns point */ wNAFUnsafe(W, precomputes, n, acc = c.ZERO) { const { windows, windowSize } = calcWOpts(W, bits); const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc. const maxNumber = 2 ** W; const shiftBy = BigInt(W); for (let window = 0; window < windows; window++) { const offset = window * windowSize; if (n === _0n$1) break; // No need to go over empty scalar // Extract W bits. let wbits = Number(n & mask); // Shift number by W bits. n >>= shiftBy; // If the bits are bigger than max size, we'll split those. // +224 => 256 - 32 if (wbits > windowSize) { wbits -= maxNumber; n += _1n$2; } if (wbits === 0) continue; let curr = precomputes[offset + Math.abs(wbits) - 1]; // -1 because we skip zero if (wbits < 0) curr = curr.negate(); // NOTE: by re-using acc, we can save a lot of additions in case of MSM acc = acc.add(curr); } return acc; }, getPrecomputes(W, P, transform) { // Calculate precomputes on a first run, reuse them after let comp = pointPrecomputes.get(P); if (!comp) { comp = this.precomputeWindow(P, W); if (W !== 1) pointPrecomputes.set(P, transform(comp)); } return comp; }, wNAFCached(P, n, transform) { const W = getW(P); return this.wNAF(W, this.getPrecomputes(W, P, transform), n); }, wNAFCachedUnsafe(P, n, transform, prev) { const W = getW(P); if (W === 1) return this.unsafeLadder(P, n, prev); // For W=1 ladder is ~x2 faster return this.wNAFUnsafe(W, this.getPrecomputes(W, P, transform), n, prev); }, // We calculate precomputes for elliptic curve point multiplication // using windowed method. This specifies window size and // stores precomputed values. Usually only base point would be precomputed. setWindowSize(P, W) { validateW(W, bits); pointWindowSizes.set(P, W); pointPrecomputes.delete(P); }, }; } /** * Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...). * 30x faster vs naive addition on L=4096, 10x faster with precomputes. * For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL. * Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0. * @param c Curve Point constructor * @param fieldN field over CURVE.N - important that it's not over CURVE.P * @param points array of L curve points * @param scalars array of L scalars (aka private keys / bigints) */ function pippenger(c, fieldN, points, scalars) { // If we split scalars by some window (let's say 8 bits), every chunk will only // take 256 buckets even if there are 4096 scalars, also re-uses double. // TODO: // - https://eprint.iacr.org/2024/750.pdf // - https://tches.iacr.org/index.php/TCHES/article/view/10287 // 0 is accepted in scalars validateMSMPoints(points, c); validateMSMScalars(scalars, fieldN); if (points.length !== scalars.length) throw new Error('arrays of points and scalars must have equal length'); const zero = c.ZERO; const wbits = bitLen(BigInt(points.length)); const windowSize = wbits > 12 ? wbits - 3 : wbits > 4 ? wbits - 2 : wbits ? 2 : 1; // in bits const MASK = (1 << windowSize) - 1; const buckets = new Array(MASK + 1).fill(zero); // +1 for zero array const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize; let sum = zero; for (let i = lastBits; i >= 0; i -= windowSize) { buckets.fill(zero); for (let j = 0; j < scalars.length; j++) { const scalar = scalars[j]; const wbits = Number((scalar >> BigInt(i)) & BigInt(MASK)); buckets[wbits] = buckets[wbits].add(points[j]); } let resI = zero; // not using this will do small speed-up, but will lose ct // Skip first bucket, because it is zero for (let j = buckets.length - 1, sumI = zero; j > 0; j--) { sumI = sumI.add(buckets[j]); resI = resI.add(sumI); } sum = sum.add(resI); if (i !== 0) for (let j = 0; j < windowSize; j++) sum = sum.double(); } return sum; } function validateBasic(curve) { validateField(curve.Fp); validateObject(curve, { n: 'bigint', h: 'bigint', Gx: 'field', Gy: 'field', }, { nBitLength: 'isSafeInteger', nByteLength: 'isSafeInteger', }); // Set defaults return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve, ...{ p: curve.Fp.ORDER }, }); }/** * Short Weierstrass curve methods. The formula is: y² = x³ + ax + b. * * ### Design rationale for types * * * Interaction between classes from different curves should fail: * `k256.Point.BASE.add(p256.Point.BASE)` * * For this purpose we want to use `instanceof` operator, which is fast and works during runtime * * Different calls of `curve()` would return different classes - * `curve(params) !== curve(params)`: if somebody decided to monkey-patch their curve, * it won't affect others * * TypeScript can't infer types for classes created inside a function. Classes is one instance * of nominative types in TypeScript and interfaces only check for shape, so it's hard to create * unique type for every function call. * * We can use generic types via some param, like curve opts, but that would: * 1. Enable interaction between `curve(params)` and `curve(params)` (curves of same params) * which is hard to debug. * 2. Params can be generic and we can't enforce them to be constant value: * if somebody creates curve from non-constant params, * it would be allowed to interact with other curves with non-constant params * * @todo https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol * @module */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ function validateSigVerOpts(opts) { if (opts.lowS !== undefined) abool('lowS', opts.lowS); if (opts.prehash !== undefined) abool('prehash', opts.prehash); } function validatePointOpts(curve) { const opts = validateBasic(curve); validateObject(opts, { a: 'field', b: 'field', }, { allowedPrivateKeyLengths: 'array', wrapPrivateKey: 'boolean', isTorsionFree: 'function', clearCofactor: 'function', allowInfinityPoint: 'boolean', fromBytes: 'function', toBytes: 'function', }); const { endo, Fp, a } = opts; if (endo) { if (!Fp.eql(a, Fp.ZERO)) { throw new Error('invalid endomorphism, can only be defined for Koblitz curves that have a=0'); } if (typeof endo !== 'object' || typeof endo.beta !== 'bigint' || typeof endo.splitScalar !== 'function') { throw new Error('invalid endomorphism, expected beta: bigint and splitScalar: function'); } } return Object.freeze({ ...opts }); } const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut; class DERErr extends Error { constructor(m = '') { super(m); } } /** * ASN.1 DER encoding utilities. ASN is very complex & fragile. Format: * * [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S] * * Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html */ const DER = { // asn.1 DER encoding utils Err: DERErr, // Basic building block is TLV (Tag-Length-Value) _tlv: { encode: (tag, data) => { const { Err: E } = DER; if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag'); if (data.length & 1) throw new E('tlv.encode: unpadded data'); const dataLen = data.length / 2; const len = numberToHexUnpadded(dataLen); if ((len.length / 2) & 128) throw new E('tlv.encode: long form length too big'); // length of length with long form flag const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) | 128) : ''; const t = numberToHexUnpadded(tag); return t + lenLen + len + data; }, // v - value, l - left bytes (unparsed) decode(tag, data) { const { Err: E } = DER; let pos = 0; if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag'); if (data.length < 2 || data[pos++] !== tag) throw new E('tlv.decode: wrong tlv'); const first = data[pos++]; const isLong = !!(first & 128); // First bit of first length byte is flag for short/long form let length = 0; if (!isLong) length = first; else { // Long form: [longFlag(1bit), lengthLength(7bit), length (BE)] const lenLen = first & 127; if (!lenLen) throw new E('tlv.decode(long): indefinite length not supported'); if (lenLen > 4) throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js const lengthBytes = data.subarray(pos, pos + lenLen); if (lengthBytes.length !== lenLen) throw new E('tlv.decode: length bytes not complete'); if (lengthBytes[0] === 0) throw new E('tlv.decode(long): zero leftmost byte'); for (const b of lengthBytes) length = (length << 8) | b; pos += lenLen; if (length < 128) throw new E('tlv.decode(long): not minimal encoding'); } const v = data.subarray(pos, pos + length); if (v.length !== length) throw new E('tlv.decode: wrong value length'); return { v, l: data.subarray(pos + length) }; }, }, // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag, // since we always use positive integers here. It must always be empty: // - add zero byte if exists // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding) _int: { encode(num) { const { Err: E } = DER; if (num < _0n) throw new E('integer: negative integers are not allowed'); let hex = numberToHexUnpadded(num); // Pad with zero byte if negative flag is present if (Number.parseInt(hex[0], 16) & 0b1000) hex = '00' + hex; if (hex.length & 1) throw new E('unexpected DER parsing assertion: unpadded hex'); return hex; }, decode(data) { const { Err: E } = DER; if (data[0] & 128) throw new E('invalid signature integer: negative'); if (data[0] === 0x00 && !(data[1] & 128)) throw new E('invalid signature integer: unnecessary leading zero'); return b2n(data); }, }, toSig(hex) { // parse DER signature const { Err: E, _int: int, _tlv: tlv } = DER; const data = typeof hex === 'string' ? h2b(hex) : hex; abytes$1(data); const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data); if (seqLeftBytes.length) throw new E('invalid signature: left bytes after parsing'); const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes); const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes); if (sLeftBytes.length) throw new E('invalid signature: left bytes after parsing'); return { r: int.decode(rBytes), s: int.decode(sBytes) }; }, hexFromSig(sig) { const { _tlv: tlv, _int: int } = DER; const rs = tlv.encode(0x02, int.encode(sig.r)); const ss = tlv.encode(0x02, int.encode(sig.s)); const seq = rs + ss; return tlv.encode(0x30, seq); }, }; // Be friendly to bad ECMAScript parsers by not using bigint literals // prettier-ignore const _0n = BigInt(0), _1n$1 = BigInt(1); BigInt(2); const _3n = BigInt(3); BigInt(4); function weierstrassPoints(opts) { const CURVE = validatePointOpts(opts); const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ const Fn = Field(CURVE.n, CURVE.nBitLength); const toBytes = CURVE.toBytes || ((_c, point, _isCompressed) => { const a = point.toAffine(); return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y)); }); const fromBytes = CURVE.fromBytes || ((bytes) => { // const head = bytes[0]; const tail = bytes.subarray(1); // if (head !== 0x04) throw new Error('Only non-compressed encoding is supported'); const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES)); const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES)); return { x, y }; }); /** * y² = x³ + ax + b: Short weierstrass curve formula * @returns y² */ function weierstrassEquation(x) { const { a, b } = CURVE; const x2 = Fp.sqr(x); // x * x const x3 = Fp.mul(x2, x); // x2 * x return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b } // Validate whether the passed curve params are valid. // We check if curve equation works for generator point. // `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381. // ProjectivePoint class has not been initialized yet. if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx))) throw new Error('bad generator point: equation left != right'); // Valid group elements reside in range 1..n-1 function isWithinCurveOrder(num) { return inRange(num, _1n$1, CURVE.n); } // Validates if priv key is valid and converts it to bigint. // Supports options allowedPrivateKeyLengths and wrapPrivateKey. function normPrivateKeyToScalar(key) { const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE; if (lengths && typeof key !== 'bigint') { if (isBytes(key)) key = bytesToHex(key); // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes if (typeof key !== 'string' || !lengths.includes(key.length)) throw new Error('invalid private key'); key = key.padStart(nByteLength * 2, '0'); } let num; try { num = typeof key === 'bigint' ? key : bytesToNumberBE(ensureBytes('private key', key, nByteLength)); } catch (error) { throw new Error('invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key); } if (wrapPrivateKey) num = mod(num, N); // disabled by default, enabled for BLS aInRange('private key', num, _1n$1, N); // num in range [1..N-1] return num; } function assertPrjPoint(other) { if (!(other instanceof Point)) throw new Error('ProjectivePoint expected'); } // Memoized toAffine / validity check. They are heavy. Points are immutable. // Converts Projective point to affine (x, y) coordinates. // Can accept precomputed Z^-1 - for example, from invertBatch. // (x, y, z) ∋ (x=x/z, y=y/z) const toAffineMemo = memoized((p, iz) => { const { px: x, py: y, pz: z } = p; // Fast-path for normalized points if (Fp.eql(z, Fp.ONE)) return { x, y }; const is0 = p.is0(); // If invZ was 0, we return zero point. However we still want to execute // all operations, so we replace invZ with a random number, 1. if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z); const ax = Fp.mul(x, iz); const ay = Fp.mul(y, iz); const zz = Fp.mul(z, iz); if (is0) return { x: Fp.ZERO, y: Fp.ZERO }; if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid'); return { x: ax, y: ay }; }); // NOTE: on exception this will crash 'cached' and no value will be set. // Otherwise true will be return const assertValidMemo = memoized((p) => { if (p.is0()) { // (0, 1, 0) aka ZERO is invalid in most contexts. // In BLS, ZERO can be serialized, so we allow it. // (0, 0, 0) is invalid representation of ZERO. if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return; throw new Error('bad point: ZERO'); } // Some 3rd-party test vectors require different wording between here & `fromCompressedHex` const { x, y } = p.toAffine(); // Check if x, y are valid field elements if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE'); const left = Fp.sqr(y); // y² const right = weierstrassEquation(x); // x³ + ax + b if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right'); if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup'); return true; }); /** * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z) * Default Point works in 2d / affine coordinates: (x, y) * We're doing calculations in projective, because its operations don't require costly inversion. */ class Point { constructor(px, py, pz) { this.px = px; this.py = py; this.pz = pz; if (px == null || !Fp.isValid(px)) throw new Error('x required'); if (py == null || !Fp.isValid(py)) throw new Error('y required'); if (pz == null || !Fp.isValid(pz)) throw new Error('z required'); Object.freeze(this); } // Does not validate if the point is on-curve. // Use fromHex instead, or call assertValidity() later. static fromAffine(p) { const { x, y } = p || {}; if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point'); if (p instanceof Point) throw new Error('projective point not allowed'); const is0 = (i) => Fp.eql(i, Fp.ZERO); // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0) if (is0(x) && is0(y)) return Point.ZERO; return new Point(x, y, Fp.ONE); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } /** * Takes a bunch of Projective Points but executes only one * inversion on all of them. Inversion is very slow operation, * so this improves performance massively. * Optimization: converts a list of projective points to a list of identical points with Z=1. */ static normalizeZ(points) { const toInv = Fp.invertBatch(points.map((p) => p.pz)); return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine); } /** * Converts hash string or Uint8Array to Point. * @param hex short/long ECDSA hex */ static fromHex(hex) { const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex))); P.assertValidity(); return P; } // Multiplies generator point by privateKey. static fromPrivateKey(privateKey) { return Point.BASE.multiply(normPrivateKeyToScalar(privateKey)); } // Multiscalar Multiplication static msm(points, scalars) { return pippenger(Point, Fn, points, scalars); } // "Private method", don't use it directly _setWindowSize(windowSize) { wnaf.setWindowSize(this, windowSize); } // A point on curve is valid if it conforms to equation. assertValidity() { assertValidMemo(this); } hasEvenY() { const { y } = this.toAffine(); if (Fp.isOdd) return !Fp.isOdd(y); throw new Error("Field doesn't support isOdd"); } /** * Compare one point to another. */ equals(other) { assertPrjPoint(other); const { px: X1, py: Y1, pz: Z1 } = this; const { px: X2, py: Y2, pz: Z2 } = other; const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1)); const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1)); return U1 && U2; } /** * Flips point to one corresponding to (x, -y) in Affine coordinates. */ negate() { return new Point(this.px, Fp.neg(this.py), this.pz); } // Renes-Costello-Batina exception-free doubling formula. // There is 30% faster Jacobian formula, but it is not complete. // https://eprint.iacr.org/2015/1060, algorithm 3 // Cost: 8M + 3S + 3*a + 2*b3 + 15add. double() { const { a, b } = CURVE; const b3 = Fp.mul(b, _3n); const { px: X1, py: Y1, pz: Z1 } = this; let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore let t0 = Fp.mul(X1, X1); // step 1 let t1 = Fp.mul(Y1, Y1); let t2 = Fp.mul(Z1, Z1); let t3 = Fp.mul(X1, Y1); t3 = Fp.add(t3, t3); // step 5 Z3 = Fp.mul(X1, Z1); Z3 = Fp.add(Z3, Z3); X3 = Fp.mul(a, Z3); Y3 = Fp.mul(b3, t2); Y3 = Fp.add(X3, Y3); // step 10 X3 = Fp.sub(t1, Y3); Y3 = Fp.add(t1, Y3); Y3 = Fp.mul(X3, Y3); X3 = Fp.mul(t3, X3); Z3 = Fp.mul(b3, Z3); // step 15 t2 = Fp.mul(a, t2); t3 = Fp.sub(t0, t2); t3 = Fp.mul(a, t3); t3 = Fp.add(t3, Z3); Z3 = Fp.add(t0, t0); // step 20 t0 = Fp.add(Z3, t0); t0 = Fp.add(t0, t2); t0 = Fp.mul(t0, t3); Y3 = Fp.add(Y3, t0); t2 = Fp.mul(Y1, Z1); // step 25 t2 = Fp.add(t2, t2); t0 = Fp.mul(t2, t3); X3 = Fp.sub(X3, t0); Z3 = Fp.mul(t2, t1); Z3 = Fp.add(Z3, Z3); // step 30 Z3 = Fp.add(Z3, Z3); return new Point(X3, Y3, Z3); } // Renes-Costello-Batina exception-free addition formula. // There is 30% faster Jacobian formula, but it is not complete. // https://eprint.iacr.org/2015/1060, algorithm 1 // Cost: 12M + 0S + 3*a + 3*b3 + 23add. add(other) { assertPrjPoint(other); const { px: X1, py: Y1, pz: Z1 } = this; const { px: X2, py: Y2, pz: Z2 } = other; let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore const a = CURVE.a; const b3 = Fp.mul(CURVE.b, _3n); let t0 = Fp.mul(X1, X2); // step 1 let t1 = Fp.mul(Y1, Y2); let t2 = Fp.mul(Z1, Z2); let t3 = Fp.add(X1, Y1);