From 984e2f2f96c734773ca0244b0d2eb79477df37c6 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Wed, 3 Dec 2025 15:04:33 -0800 Subject: [PATCH] Strip all functionality related to signing since we only need public key derivation. --- src/lib/crypto/secp256k1.ts | 449 +----------------------------------- 1 file changed, 11 insertions(+), 438 deletions(-) diff --git a/src/lib/crypto/secp256k1.ts b/src/lib/crypto/secp256k1.ts index dd4e244..28804b2 100644 --- a/src/lib/crypto/secp256k1.ts +++ b/src/lib/crypto/secp256k1.ts @@ -1,14 +1,14 @@ -/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ +//! SPDX-FileCopyrightText: 2025 Chris Duncan +//! SPDX-License-Identifier: GPL-3.0-or-later -/** - * 5KB JS implementation of secp256k1 ECDSA / Schnorr signatures & ECDH. - * Compliant with RFC6979 & BIP340. - * @module +/*! + * Fork of `noble-secp256k1` in order to wrap functionality in a class for + * embedding into a Web Worker and to prune methods down to only public key + * derivation for supporting Nano wallet seed functionality from the Exodus app. */ -/** - * Fork rereleased under GPLv3. - * Functionality wrapped in a class for embedding into a Web Worker. +/*! + * noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ type Point = { @@ -93,12 +93,6 @@ export type ECDSASignOpts = { format?: ECDSASignatureFormat extraEntropy?: ECDSAExtraEntropy } -type Pred = (v: Bytes) => T | undefined - -type KeysSecPub = { secretKey: Bytes; publicKey: Bytes } -type KeygenFn = (seed?: Bytes) => KeysSecPub - -type MaybePromise = T | Promise type EnsureNumber = A extends number ? A : never type Length = EnsureNumber @@ -246,12 +240,6 @@ export class Secp256k1 { return b === 1n ? this.M(x, md) : this.err('no inverse') // b is gcd at this point } - static callHash (name: string) { - // @ts-ignore - const fn = hashes[name] - if (typeof fn !== 'function') this.err('hashes.' + name + ' not set') - return fn - } // ## End of Helpers // ----------------- @@ -585,21 +573,10 @@ export class Secp256k1 { extraEntropy: false, } - static hashes = { - hmacSha256Async: async (key: Bytes, message: Bytes): Promise => { - const name = 'HMAC' - const k = await crypto.subtle.importKey('raw', key, { name, hash: { name: 'SHA-256' } }, false, ['sign']) - return new Uint8Array(await crypto.subtle.sign(name, k, message)) - }, - hmacSha256: undefined as undefined | ((key: Bytes, message: Bytes) => Bytes), - sha256Async: async (msg: Bytes): Promise => new Uint8Array(await crypto.subtle.digest('SHA-256', msg)), - sha256: undefined as undefined | ((message: Bytes) => Bytes), - } - - static prepMsg (msg: Bytes, opts: ECDSARecoverOpts, async_: boolean): Bytes | Promise { + static async prepMsg (msg: Bytes, opts: ECDSARecoverOpts): Promise { this.abytes(msg, undefined, 'message') if (!opts.prehash) return msg - return async_ ? this.hashes.sha256Async(msg) : this.callHash('sha256')(msg) + return new Uint8Array(await crypto.subtle.digest('SHA-256', msg)) } static NULL: Bytes = new Uint8Array(0) @@ -607,145 +584,6 @@ export class Secp256k1 { static byte1 = this.u8of(0x01) static _maxDrbgIters = 1000 static _drbgErr = 'drbg: tried max amount of iterations' - // HMAC-DRBG from NIST 800-90. Minimal, non-full-spec - used for RFC6979 signatures. - static hmacDrbg (seed: Bytes, pred: Pred): Bytes { - let v = new Uint8Array(this.L) // Steps B, C of RFC6979 3.2: set hashLen - let k = new Uint8Array(this.L) // In our case, it's always equal to L - let i = 0 // Iterations counter, will throw when over max - const reset = () => { - v.fill(1) - k.fill(0) - } - // h = hmac(K || V || ...) - const h = (...b: Bytes[]) => this.callHash('hmacSha256')(k, this.concatBytes(v, ...b)) - const reseed = (seed = this.NULL) => { - // HMAC-DRBG reseed() function. Steps D-G - k = h(this.byte0, seed) // k = hmac(k || v || 0x00 || seed) - v = h() // v = hmac(k || v) - if (seed.length === 0) return - k = h(this.byte1, seed) // k = hmac(k || v || 0x01 || seed) - v = h() // v = hmac(k || v) - } - // HMAC-DRBG generate() function - const gen = () => { - if (i++ >= this._maxDrbgIters) this.err(this._drbgErr) - v = h() // v = hmac(k || v) - return v // this diverges from noble-curves: we don't allow arbitrary output len! - } - reset() - reseed(seed) // Steps D-G - let res: Bytes | undefined = undefined // Step H: grind until k is in [1..n-1] - while (!(res = pred(gen()))) reseed() // test predicate until it returns ok - reset() - return res! - } - - // Identical to hmacDrbg, but async: uses built-in WebCrypto - static async hmacDrbgAsync (seed: Bytes, pred: Pred): Promise { - let v = new Uint8Array(this.L) // Steps B, C of RFC6979 3.2: set hashLen - let k = new Uint8Array(this.L) // In our case, it's always equal to L - let i = 0 // Iterations counter, will throw when over max - const reset = () => { - v.fill(1) - k.fill(0) - } - // h = hmac(K || V || ...) - const h = (...b: Bytes[]) => this.hashes.hmacSha256Async(k, this.concatBytes(v, ...b)) - const reseed = async (seed = this.NULL) => { - // HMAC-DRBG reseed() function. Steps D-G - k = await h(this.byte0, seed) // k = hmac(K || V || 0x00 || seed) - v = await h() // v = hmac(K || V) - if (seed.length === 0) return - k = await h(this.byte1, seed) // k = hmac(K || V || 0x01 || seed) - v = await h() // v = hmac(K || V) - } - // HMAC-DRBG generate() function - const gen = async () => { - if (i++ >= this._maxDrbgIters) this.err(this._drbgErr) - v = await h() // v = hmac(K || V) - return v // this diverges from noble-curves: we don't allow arbitrary output len! - } - reset() - await reseed(seed) // Steps D-G - let res: Bytes | undefined = undefined // Step H: grind until k is in [1..n-1] - while (!(res = pred(await gen()))) await reseed() // test predicate until it returns ok - reset() - return res! - } - - // RFC6979 signature generation, preparation step. - // Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.2 & RFC6979. - static _sign (messageHash: Bytes, secretKey: Bytes, opts: ECDSASignOpts, hmacDrbg: (seed: Bytes, pred: Pred) => T): T { - let { lowS, extraEntropy } = opts // generates low-s sigs by default - // RFC6979 3.2: we skip step A - const int2octets = this.bigintTo32Bytes // int to octets - const h1i = this.bits2int_modN(messageHash) // msg bigint - const h1o = int2octets(h1i) // msg octets - const d = this.secretKeyToScalar(secretKey) // validate private key, convert to bigint - const seedArgs = [int2octets(d), h1o] // Step D of RFC6979 3.2 - /** RFC6979 3.6: additional k' (optional). See {@link ECDSAExtraEntropy}. */ - if (extraEntropy != null && extraEntropy !== false) { - // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') - // gen random bytes OR pass as-is - const e = extraEntropy === true ? crypto.getRandomValues(new Uint8Array(this.L)) : extraEntropy - seedArgs.push(this.abytes(e, undefined, 'extraEntropy')) // check for being bytes - } - const seed = this.concatBytes(...seedArgs) - const m = h1i // convert msg to bigint - // Converts signature params into point w r/s, checks result for validity. - // To transform k => Signature: - // q = k⋅G - // r = q.x mod n - // s = k^-1(m + rd) mod n - // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to - // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it: - // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT - const k2sig = (kBytes: Bytes): Bytes | undefined => { - // RFC 6979 Section 3.2, step 3: k = bits2int(T) - // Important: all mod() calls here must be done over N - const k = this.bits2int(kBytes) - if (!(1n <= k && k < this.N)) return // Valid scalars (including k) must be in 1..N-1 - const ik = this.invert(k, this.N) // k^-1 mod n - const q = this.G.multiply(k).toAffine() // q = k⋅G - const r = this.modN(q.x) // r = q.x mod n - if (r === 0n) return - const s = this.modN(ik * this.modN(m + r * d)) // s = k^-1(m + rd) mod n - if (s === 0n) return - let recovery = (q.x === r ? 0 : 2) | Number(q.y & 1n) // recovery bit (2 or 3, when q.x > n) - let normS = s // normalized S - if (lowS && this.highS(s)) { - // if lowS was passed, ensure s is always - normS = this.modN(-s) // in the bottom half of CURVE.n - recovery ^= 1 - } - const sig = this.Signature(r, normS, recovery) as RecoveredSignature // use normS, not s - return sig.toBytes(opts.format) - } - return hmacDrbg(seed, k2sig) - } - - // Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.4. - static _verify (sig: Bytes, messageHash: Bytes, publicKey: Bytes, opts: ECDSAVerifyOpts = {}): boolean { - const { lowS, format } = opts - if (sig instanceof this.Signature) this.err('Signature must be in Uint8Array, use .toBytes()') - this.assertSigLength(sig, format) - this.abytes(publicKey, undefined, 'publicKey') - try { - const { r, s } = this.signatureFromBytes(sig, format) - const h = this.bits2int_modN(messageHash) // Truncate hash - const P = this.pointFromBytes(publicKey) // Validate public key - if (lowS && this.highS(s)) return false // lowS bans sig.s >= CURVE.n/2 - const is = this.invert(s, this.N) // s^-1 - const u1 = this.modN(h * is) // u1 = hs^-1 mod n - const u2 = this.modN(r * is) // u2 = rs^-1 mod n - const R = this.doubleScalarMultiplyUnsafe(P, u1, u2).toAffine() // R = u1⋅G + u2⋅P - // Stop if R is identity / zero point. Check is done inside `doubleScalarMulUns` - const v = this.modN(R.x) // R.x must be in N's field, not P's - return v === r // mod(R.x, n) == r - } catch (error) { - return false - } - } static setDefaults (opts: ECDSASignOpts): Required { const res: ECDSASignOpts = {} @@ -756,86 +594,6 @@ export class Secp256k1 { return res as Required } - /** - * Sign a message using secp256k1. Sync: uses `hashes.sha256` and `hashes.hmacSha256`. - * Prehashes message with sha256, disable using `prehash: false`. - * @param opts - see {@link ECDSASignOpts} for details. Enabling {@link ECDSAExtraEntropy} will improve security. - * @example - * ```js - * const msg = new TextEncoder().encode('hello noble'); - * sign(msg, secretKey); - * sign(keccak256(msg), secretKey, { prehash: false }); - * sign(msg, secretKey, { extraEntropy: true }); - * sign(msg, secretKey, { format: 'recovered' }); - * ``` - */ - static sign (message: Bytes, secretKey: Bytes, opts: ECDSASignOpts = {}): Bytes { - opts = this.setDefaults(opts) - message = this.prepMsg(message, opts, false) as Bytes - return this._sign(message, secretKey, opts, this.hmacDrbg) - } - - /** - * Sign a message using secp256k1. Async: uses built-in WebCrypto hashes. - * Prehashes message with sha256, disable using `prehash: false`. - * @param opts - see {@link ECDSASignOpts} for details. Enabling {@link ECDSAExtraEntropy} will improve security. - * @example - * ```js - * const msg = new TextEncoder().encode('hello noble'); - * await signAsync(msg, secretKey); - * await signAsync(keccak256(msg), secretKey, { prehash: false }); - * await signAsync(msg, secretKey, { extraEntropy: true }); - * await signAsync(msg, secretKey, { format: 'recovered' }); - * ``` - */ - static async signAsync (message: Bytes, secretKey: Bytes, opts: ECDSASignOpts = {}): Promise { - opts = this.setDefaults(opts) - message = await this.prepMsg(message, opts, true) - return this._sign(message, secretKey, opts, this.hmacDrbgAsync) - } - - /** - * Verify a signature using secp256k1. Sync: uses `hashes.sha256` and `hashes.hmacSha256`. - * @param signature - default is 64-byte 'compact' format, also see {@link ECDSASignatureFormat} - * @param message - message which was signed. Keep in mind `prehash` from opts. - * @param publicKey - public key which - * @param opts - see {@link ECDSAVerifyOpts} for details. - * @example - * ```js - * const msg = new TextEncoder().encode('hello noble'); - * verify(sig, msg, publicKey); - * verify(sig, keccak256(msg), publicKey, { prehash: false }); - * verify(sig, msg, publicKey, { lowS: false }); - * verify(sigr, msg, publicKey, { format: 'recovered' }); - * ``` - */ - static verify (signature: Bytes, message: Bytes, publicKey: Bytes, opts: ECDSAVerifyOpts = {}): boolean { - opts = this.setDefaults(opts) - message = this.prepMsg(message, opts, false) as Bytes - return this._verify(signature, message, publicKey, opts) - } - - /** - * Verify a signature using secp256k1. Async: uses built-in WebCrypto hashes. - * @param signature - default is 64-byte 'compact' format, also see {@link ECDSASignatureFormat} - * @param message - message which was signed. Keep in mind `prehash` from opts. - * @param publicKey - public key which - * @param opts - see {@link ECDSAVerifyOpts} for details. - * @example - * ```js - * const msg = new TextEncoder().encode('hello noble'); - * verify(sig, msg, publicKey); - * verify(sig, keccak256(msg), publicKey, { prehash: false }); - * verify(sig, msg, publicKey, { lowS: false }); - * verify(sigr, msg, publicKey, { format: 'recovered' }); - * ``` - */ - static async verifyAsync (sig: Bytes, message: Bytes, publicKey: Bytes, opts: ECDSAVerifyOpts = {}): Promise { - opts = this.setDefaults(opts) - message = await this.prepMsg(message, opts, true) - return this._verify(sig, message, publicKey, opts) - } - static _recover (signature: Bytes, messageHash: Bytes): Bytes { const sig = this.signatureFromBytes(signature, 'recovered') const { r, s, recovery } = sig @@ -859,196 +617,12 @@ export class Secp256k1 { * ECDSA public key recovery. Requires msg hash and recovery id. * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.6. */ - static recoverPublicKey (signature: Bytes, message: Bytes, opts: ECDSARecoverOpts = {}): Bytes { - message = this.prepMsg(message, this.setDefaults(opts), false) as Bytes - return this._recover(signature, message) - } static async recoverPublicKeyAsync (signature: Bytes, message: Bytes, opts: ECDSARecoverOpts = {}): Promise { - message = await this.prepMsg(message, this.setDefaults(opts), true) + message = await this.prepMsg(message, this.setDefaults(opts)) return this._recover(signature, message) } - /** - * Elliptic Curve Diffie-Hellman (ECDH) on secp256k1. - * Result is **NOT hashed**. Use hash or KDF on it if you need. - * @param isCompressed 33-byte (true) or 65-byte (false) output - * @returns public key C - */ - static getSharedSecret (secretKeyA: Bytes, publicKeyB: Bytes, isCompressed = true): Bytes { - return this.pointFromBytes(publicKeyB).multiply(this.secretKeyToScalar(secretKeyA)).toBytes(isCompressed) - } - - // FIPS 186 B.4.1 compliant key generation produces private keys - // with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1 - static randomSecretKey (seed?: Bytes): Bytes { - seed ??= crypto.getRandomValues(new Uint8Array(this.lengths.seed)) - this.abytes(seed) - if (seed.length < this.lengths.seed || seed.length > 1024) this.err('expected 40-1024b') - const num = this.M(this.bytesToBigint(seed), this.N - 1n) - return this.bigintTo32Bytes(num + 1n) - } - - static createKeygen = (getPublicKey: (secretKey: Bytes) => Bytes) => (seed?: Bytes): KeysSecPub => { - const secretKey = this.randomSecretKey(seed) - return { secretKey, publicKey: getPublicKey(secretKey) } - } - static keygen: KeygenFn = this.createKeygen(this.getPublicKey) - - // Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code. - // https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki - static getTag = (tag: string) => Uint8Array.from('BIP0340/' + tag, (c) => c.charCodeAt(0)) - static T_AUX = 'aux' - static T_NONCE = 'nonce' - static T_CHALLENGE = 'challenge' - static taggedHash (tag: string, ...messages: Bytes[]): Bytes { - const fn = this.callHash('sha256') - const tagH = fn(this.getTag(tag)) - return fn(this.concatBytes(tagH, tagH, ...messages)) - } - static async taggedHashAsync (tag: string, ...messages: Bytes[]): Promise { - const fn = this.hashes.sha256Async - const tagH = await fn(this.getTag(tag)) - return await fn(this.concatBytes(tagH, tagH, ...messages)) - } - - // ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03 - // Calculate point, scalar and bytes - static extpubSchnorr (priv: Bytes) { - const d_ = this.secretKeyToScalar(priv) - const p = this.G.multiply(d_) // P = d'⋅G; 0 < d' < n check is done inside - const { x, y } = p.assertValidity().toAffine() // validate Point is not at infinity - const d = this.isEven(y) ? d_ : this.modN(-d_) - const px = this.bigintTo32Bytes(x) - return { d, px } - } - - static bytesModN = (bytes: Bytes) => this.modN(this.bytesToBigint(bytes)) - static challenge = (...args: Bytes[]): bigint => this.bytesModN(this.taggedHash(this.T_CHALLENGE, ...args)) - static challengeAsync = async (...args: Bytes[]): Promise => - this.bytesModN(await this.taggedHashAsync(this.T_CHALLENGE, ...args)) - - /** - * Schnorr public key is just `x` coordinate of Point as per BIP340. - */ - static pubSchnorr (secretKey: Bytes): Bytes { - return this.extpubSchnorr(secretKey).px // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G) - } - - static keygenSchnorr: KeygenFn = this.createKeygen(this.pubSchnorr) - - // Common preparation fn for both sync and async signing - static prepSigSchnorr (message: Bytes, secretKey: Bytes, auxRand: Bytes) { - const { px, d } = this.extpubSchnorr(secretKey) - return { m: this.abytes(message), px, d, a: this.abytes(auxRand, this.L) } - } - - static extractK (rand: Bytes) { - const k_ = this.bytesModN(rand) // Let k' = int(rand) mod n - if (k_ === 0n) this.err('sign failed: k is zero') // Fail if k' = 0. - const { px, d } = this.extpubSchnorr(this.bigintTo32Bytes(k_)) // Let R = k'⋅G. - return { rx: px, k: d } - } - - // Common signature creation helper - static createSigSchnorr (k: bigint, px: Bytes, e: bigint, d: bigint): Bytes { - return this.concatBytes(px, this.bigintTo32Bytes(this.modN(k + e * d))) - } - - static E_INVSIG = 'invalid signature produced' - /** - * Creates Schnorr signature as per BIP340. Verifies itself before returning anything. - * auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous. - */ - static signSchnorr (message: Bytes, secretKey: Bytes, auxRand?: Bytes): Bytes { - auxRand ??= crypto.getRandomValues(new Uint8Array(this.L)) - const { m, px, d, a } = this.prepSigSchnorr(message, secretKey, auxRand) - const aux = this.taggedHash(this.T_AUX, a) - // Let t be the byte-wise xor of bytes(d) and hash/aux(a) - const t = this.bigintTo32Bytes(d ^ this.bytesToBigint(aux)) - // Let rand = hash/nonce(t || bytes(P) || m) - const rand = this.taggedHash(this.T_NONCE, t, px, m) - const { rx, k } = this.extractK(rand) - // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n. - const e = this.challenge(rx, px, m) - const sig = this.createSigSchnorr(k, rx, e, d) - // If Verify(bytes(P), m, sig) (see below) returns failure, abort - if (!this.verifySchnorr(sig, m, px)) this.err(this.E_INVSIG) - return sig - } - - static async signSchnorrAsync (message: Bytes, secretKey: Bytes, auxRand?: Bytes): Promise { - auxRand ??= crypto.getRandomValues(new Uint8Array(this.L)) - const { m, px, d, a } = this.prepSigSchnorr(message, secretKey, auxRand) - const aux = await this.taggedHashAsync(this.T_AUX, a) - // Let t be the byte-wise xor of bytes(d) and hash/aux(a) - const t = this.bigintTo32Bytes(d ^ this.bytesToBigint(aux)) - // Let rand = hash/nonce(t || bytes(P) || m) - const rand = await this.taggedHashAsync(this.T_NONCE, t, px, m) - const { rx, k } = this.extractK(rand) - // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n. - const e = await this.challengeAsync(rx, px, m) - const sig = this.createSigSchnorr(k, rx, e, d) - // If Verify(bytes(P), m, sig) (see below) returns failure, abort - if (!(await this.verifySchnorrAsync(sig, m, px))) this.err(this.E_INVSIG) - return sig - } - - // const finishVerif = (P_: Point, r: bigint, s: bigint, e: bigint) => {}; - - static callSyncAsyncFn = (res: MaybePromise, later: (res2: T) => O) => { - return res instanceof Promise ? res.then(later) : later(res) - } - - static _verifSchnorr (signature: Bytes, message: Bytes, publicKey: Bytes, challengeFn: (...args: Bytes[]) => bigint | Promise): boolean | Promise { - const sig = this.abytes(signature, this.L2, 'signature') - const msg = this.abytes(message, undefined, 'message') - const pub = this.abytes(publicKey, this.L, 'publicKey') - try { - // lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point. - // Fail if x ≥ p. Let c = x³ + 7 mod p. - const x = this.bytesToBigint(pub) - const y = this.lift_x(x) // Let y = c^(p+1)/4 mod p. - const y_ = this.isEven(y) ? y : this.modP(-y) - // Return the unique point P such that x(P) = x and - // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. - const P_ = this.Point(x, y_, 1n).assertValidity() - const px = this.bigintTo32Bytes(P_.toAffine().x) - // P = lift_x(int(pk)); fail if that fails - const r = this.sliceBytesNumBE(sig, 0, this.L) // Let r = int(sig[0:32]); fail if r ≥ p. - this.bigintInRange(r, 1n, this.P) - const s = this.sliceBytesNumBE(sig, this.L, this.L2) // Let s = int(sig[32:64]); fail if s ≥ n. - this.bigintInRange(s, 1n, this.N) - const i = this.concatBytes(this.bigintTo32Bytes(r), px, msg) - // int(challenge(bytes(r)||bytes(P)||m))%n - return this.callSyncAsyncFn(challengeFn(i), (e) => { - const { x, y } = this.doubleScalarMultiplyUnsafe(P_, s, this.modN(-e)).toAffine() // R = s⋅G - e⋅P - if (!this.isEven(y) || x !== r) return false // -eP == (n-e)P - return true // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r. - }) - } catch (error) { - return false - } - } - - /** - * Verifies Schnorr signature. - * Will swallow errors & return false except for initial type validation of arguments. - */ - static verifySchnorr = (s: Bytes, m: Bytes, p: Bytes): boolean => - this._verifSchnorr(s, m, p, this.challenge) as boolean - static verifySchnorrAsync = async (s: Bytes, m: Bytes, p: Bytes): Promise => - this._verifSchnorr(s, m, p, this.challengeAsync) as Promise - - static schnorr = { - keygen: this.keygenSchnorr, - getPublicKey: this.pubSchnorr, - sign: this.signSchnorr, - verify: this.verifySchnorr, - signAsync: this.signSchnorrAsync, - verifyAsync: this.verifySchnorrAsync, - } - // ## Precomputes // -------------- @@ -1082,7 +656,6 @@ export class Secp256k1 { * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by * caching multiples of G (base point). Cache is stored in 32MB of RAM. * Any time `G.multiply` is done, precomputes are used. - * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`. * * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method, * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`. -- 2.47.3