]> git.codecow.com Git - libnemo.git/commitdiff
Strip all functionality related to signing since we only need public key derivation.
authorChris Duncan <chris@zoso.dev>
Wed, 3 Dec 2025 23:04:33 +0000 (15:04 -0800)
committerChris Duncan <chris@zoso.dev>
Wed, 3 Dec 2025 23:04:33 +0000 (15:04 -0800)
src/lib/crypto/secp256k1.ts

index dd4e2447a75474e76255b1d9640708ab3362c927..28804b244b000eed627b04bb58693ffe2ec2f0d7 100644 (file)
@@ -1,14 +1,14 @@
-/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! 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<T> = (v: Bytes) => T | undefined
-
-type KeysSecPub = { secretKey: Bytes; publicKey: Bytes }
-type KeygenFn = (seed?: Bytes) => KeysSecPub
-
-type MaybePromise<T> = T | Promise<T>
 
 type EnsureNumber<A extends number> = A extends number ? A : never
 type Length<T extends any[]> = EnsureNumber<T['length']>
@@ -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<Bytes> => {
-                       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<Bytes> => 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<Bytes> {
+       static async prepMsg (msg: Bytes, opts: ECDSARecoverOpts): Promise<Bytes> {
                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>): 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<Bytes>): Promise<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.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<T> (messageHash: Bytes, secretKey: Bytes, opts: ECDSASignOpts, hmacDrbg: (seed: Bytes, pred: Pred<Bytes>) => 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<ECDSASignOpts> {
                const res: ECDSASignOpts = {}
@@ -756,86 +594,6 @@ export class Secp256k1 {
                return res as Required<ECDSASignOpts>
        }
 
-       /**
-        * 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<Bytes> {
-               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<boolean> {
-               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<Bytes> {
-               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<Bytes> {
-               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<bigint> =>
-               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<Bytes> {
-               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 = <T, O> (res: MaybePromise<T>, 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<bigint>): boolean | Promise<boolean> {
-               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<boolean> =>
-               this._verifSchnorr(s, m, p, this.challengeAsync) as Promise<boolean>
-
-       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`.