-/*! 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 = {
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']>
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
// -----------------
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)
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 = {}
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
* 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
// --------------
* 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`.