diff --git a/src/abstract/modular.ts b/src/abstract/modular.ts index 3b44c2d..0ded40b 100644 --- a/src/abstract/modular.ts +++ b/src/abstract/modular.ts @@ -407,7 +407,7 @@ export function hashToPrivateScalar( groupOrder: bigint, isLE = false ): bigint { - hash = ensureBytes(hash); + hash = ensureBytes('privateHash', hash); const hashLen = hash.length; const minLen = nLength(groupOrder).nByteLength + 8; if (minLen < 24 || hashLen < minLen || hashLen > 1024) diff --git a/src/abstract/utils.ts b/src/abstract/utils.ts index b9ea3c3..8c9c030 100644 --- a/src/abstract/utils.ts +++ b/src/abstract/utils.ts @@ -33,14 +33,14 @@ export function numberToHexUnpadded(num: number | bigint): string { } export function hexToNumber(hex: string): bigint { - if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex); + if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex); // Big Endian return BigInt(hex === '' ? '0' : `0x${hex}`); } // Caching slows it down 2-3x export function hexToBytes(hex: string): Uint8Array { - if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex); + if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex); if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length); const array = new Uint8Array(hex.length / 2); for (let i = 0; i < array.length; i++) { @@ -68,13 +68,25 @@ export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, le // Returns variable number bytes (minimal bigint encoding?) export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n)); -export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array { - // Uint8Array.from() instead of hash.slice() because node.js Buffer - // is instance of Uint8Array, and its slice() creates **mutable** copy - const bytes = u8a(hex) ? Uint8Array.from(hex) : hexToBytes(hex); - if (typeof expectedLength === 'number' && bytes.length !== expectedLength) - throw new Error(`Expected ${expectedLength} bytes`); - return bytes; +export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array { + let res: Uint8Array; + if (typeof hex === 'string') { + try { + res = hexToBytes(hex); + } catch (e) { + throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`); + } + } else if (u8a(hex)) { + // Uint8Array.from() instead of hash.slice() because node.js Buffer + // is instance of Uint8Array, and its slice() creates **mutable** copy + res = Uint8Array.from(hex); + } else { + throw new Error(`${title} must be hex string or Uint8Array`); + } + const len = res.length; + if (typeof expectedLength === 'number' && len !== expectedLength) + throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`); + return res; } // Copies several Uint8Arrays into one. @@ -96,6 +108,16 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array) { return true; } +// Global symbols in both browsers and Node.js since v11 +// See https://github.com/microsoft/TypeScript/issues/31535 +declare const TextEncoder: any; +export function utf8ToBytes(str: string): Uint8Array { + if (typeof str !== 'string') { + throw new Error(`utf8ToBytes expected string, got ${typeof str}`); + } + return new TextEncoder().encode(str); +} + // Bit operations // Amount of bits inside bigint (Same as n.toString(2).length) diff --git a/src/abstract/weierstrass.ts b/src/abstract/weierstrass.ts index c80cf84..2fd8658 100644 --- a/src/abstract/weierstrass.ts +++ b/src/abstract/weierstrass.ts @@ -59,9 +59,6 @@ export interface ProjPointType extends Group> { readonly py: T; readonly pz: T; multiply(scalar: bigint): ProjPointType; - multiplyUnsafe(scalar: bigint): ProjPointType; - multiplyAndAddUnsafe(Q: ProjPointType, a: bigint, b: bigint): ProjPointType | undefined; - _setWindowSize(windowSize: number): void; toAffine(iz?: T): AffinePoint; isTorsionFree(): boolean; clearCofactor(): ProjPointType; @@ -69,6 +66,10 @@ export interface ProjPointType extends Group> { hasEvenY(): boolean; toRawBytes(isCompressed?: boolean): Uint8Array; toHex(isCompressed?: boolean): string; + + multiplyUnsafe(scalar: bigint): ProjPointType; + multiplyAndAddUnsafe(Q: ProjPointType, a: bigint, b: bigint): ProjPointType | undefined; + _setWindowSize(windowSize: number): void; } // Static methods for 3d XYZ points export interface ProjConstructor extends GroupConstructor> { @@ -213,7 +214,10 @@ export function weierstrassPoints(opts: CurvePointsType) { } let num: bigint; try { - num = typeof key === 'bigint' ? key : ut.bytesToNumberBE(ensureBytes(key, nByteLength)); + num = + typeof key === 'bigint' + ? key + : ut.bytesToNumberBE(ensureBytes('private key', key, nByteLength)); } catch (error) { throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`); } @@ -276,7 +280,7 @@ export function weierstrassPoints(opts: CurvePointsType) { * @param hex short/long ECDSA hex */ static fromHex(hex: Hex): Point { - const P = Point.fromAffine(CURVE.fromBytes(ensureBytes(hex))); + const P = Point.fromAffine(CURVE.fromBytes(ensureBytes('pointHex', hex))); P.assertValidity(); return P; } @@ -782,24 +786,22 @@ export function weierstrass(curveDef: CurveType): CurveFn { // pair (bytes of r, bytes of s) static fromCompact(hex: Hex) { - const gl = CURVE.nByteLength; - hex = ensureBytes(hex, gl * 2); - return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl)); + const l = CURVE.nByteLength; + hex = ensureBytes('compactSignature', hex, l * 2); + return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l)); } // DER encoded ECDSA signature // https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script static fromDER(hex: Hex) { - if (typeof hex !== 'string' && !(hex instanceof Uint8Array)) - throw new Error(`Signature.fromDER: Expected string or Uint8Array`); - const { r, s } = DER.toSig(ensureBytes(hex)); + const { r, s } = DER.toSig(ensureBytes('DER', hex)); return new Signature(r, s); } assertValidity(): void { // can use assertGE here - if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < n'); - if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < n'); + if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < CURVE.n'); + if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n'); } addRecoveryBit(recovery: number) { @@ -807,11 +809,10 @@ export function weierstrass(curveDef: CurveType): CurveFn { } recoverPublicKey(msgHash: Hex): typeof Point.BASE { - const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6 const { r, s, recovery: rec } = this; - const h = bits2int_modN(ensureBytes(msgHash)); // Truncate hash + const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid'); - const radj = rec === 2 || rec === 3 ? r + N : r; + const radj = rec === 2 || rec === 3 ? r + CURVE.n : r; if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid'); const prefix = (rec & 1) === 0 ? '02' : '03'; const R = Point.fromHex(prefix + numToNByteStr(radj)); @@ -936,8 +937,8 @@ export function weierstrass(curveDef: CurveType): CurveFn { function (bytes: Uint8Array): bigint { // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m) // for some cases, since bytes.length * 8 is not actual bitLength. - const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits const num = ut.bytesToNumberBE(bytes); // check for == u8 done here + const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits return delta > 0 ? num >> BigInt(delta) : num; }; const bits2int_modN = @@ -962,26 +963,25 @@ export function weierstrass(curveDef: CurveType): CurveFn { // NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521. // Also it can be bigger for P224 + SHA256 function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) { - const { hash, randomBytes } = CURVE; - if (msgHash == null) throw new Error(`sign: expected valid message hash, not "${msgHash}"`); if (['recovered', 'canonical'].some((k) => k in opts)) - // Ban legacy options throw new Error('sign() legacy options not supported'); + const { hash, randomBytes } = CURVE; let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default - if (prehash) msgHash = hash(ensureBytes(msgHash)); if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash + msgHash = ensureBytes('msgHash', msgHash); + if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash)); // We can't later call bits2octets, since nested bits2int is broken for curves // with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call. // const bits2octets = (bits) => int2octets(bits2int_modN(bits)) - const h1int = bits2int_modN(ensureBytes(msgHash)); + const h1int = bits2int_modN(msgHash); const d = normalizePrivateKey(privateKey); // validate private key, convert to bigint const seedArgs = [int2octets(d), int2octets(h1int)]; // extraEntropy. RFC6979 3.6: additional k' (optional). if (ent != null) { // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') - // Either pass as-is, or generate random bytes. Then validate for being ui8a of size BYTES - seedArgs.push(ensureBytes(ent === true ? randomBytes(Fp.BYTES) : ent, Fp.BYTES)); + const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is + seedArgs.push(ensureBytes('extraEntropy', e, Fp.BYTES)); // check for being of size BYTES } const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2 const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash! @@ -1057,30 +1057,38 @@ export function weierstrass(curveDef: CurveType): CurveFn { publicKey: Hex, opts = defaultVerOpts ): boolean { - let P: ProjPointType; + const sg = signature; + msgHash = ensureBytes('msgHash', msgHash); + publicKey = ensureBytes('publicKey', publicKey); + if ('strict' in opts) throw new Error('options.strict was renamed to lowS'); + const { lowS, prehash } = opts; + let _sig: Signature | undefined = undefined; - if (publicKey instanceof Point) throw new Error('publicKey must be hex'); + let P: ProjPointType; try { - if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) { - const { r, s } = signature; - _sig = new Signature(r, s); // assertValidity() is executed on creation - } else { + if (typeof sg === 'string' || sg instanceof Uint8Array) { // Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length). // Since DER can also be 2*nByteLength bytes, we check for it first. try { - _sig = Signature.fromDER(signature as Hex); + _sig = Signature.fromDER(sg); } catch (derError) { if (!(derError instanceof DER.Err)) throw derError; - _sig = Signature.fromCompact(signature as Hex); + _sig = Signature.fromCompact(sg); } + } else if (typeof sg === 'object' && typeof sg.r === 'bigint' && typeof sg.s === 'bigint') { + const { r, s } = sg; + _sig = new Signature(r, s); + } else { + throw new Error('PARSE'); } - msgHash = ensureBytes(msgHash); P = Point.fromHex(publicKey); } catch (error) { + if ((error as Error).message === 'PARSE') + throw new Error(`signature must be Signature instance, Uint8Array or hex string`); return false; } - if (opts.lowS && _sig.hasHighS()) return false; - if (opts.prehash) msgHash = CURVE.hash(msgHash); + if (lowS && _sig.hasHighS()) return false; + if (prehash) msgHash = CURVE.hash(msgHash); const { r, s } = _sig; const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element const is = invN(s); // s^-1 diff --git a/src/secp256k1.ts b/src/secp256k1.ts index 9c63eb4..a0863b7 100644 --- a/src/secp256k1.ts +++ b/src/secp256k1.ts @@ -1,26 +1,12 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ import { sha256 } from '@noble/hashes/sha256'; -import { Fp as Field, mod, pow2 } from './abstract/modular.js'; -import { createCurve } from './_shortw_utils.js'; -import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js'; -import { - ensureBytes, - concatBytes, - Hex, - bytesToNumberBE as bytesToInt, - PrivKey, - numberToBytesBE, -} from './abstract/utils.js'; import { randomBytes } from '@noble/hashes/utils'; +import { Fp as Field, mod, pow2 } from './abstract/modular.js'; +import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js'; +import type { Hex, PrivKey } from './abstract/utils.js'; +import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js'; import * as htf from './abstract/hash-to-curve.js'; - -/** - * secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism. - * Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%. - * Should always be used for Projective's double-and-add multiplication. - * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit. - * https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 - */ +import { createCurve } from './_shortw_utils.js'; const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'); const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'); @@ -61,23 +47,22 @@ type Fp = bigint; export const secp256k1 = createCurve( { - // Params: a, b - // Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975 - a: BigInt(0), - b: BigInt(7), - // Field over which we'll do calculations; - // 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n - Fp, - // Curve order, total count of valid points in the field - n: secp256k1N, + a: BigInt(0), // equation params: a, b + b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975 + Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n + n: secp256k1N, // Curve order, total count of valid points in the field // Base point (x, y) aka generator point Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'), Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'), - h: BigInt(1), - // Alllow only low-S signatures by default in sign() and verify() - lowS: true, + h: BigInt(1), // Cofactor + lowS: true, // Allow only low-S signatures by default in sign() and verify() + /** + * secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism. + * Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%. + * For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit. + * Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 + */ endo: { - // Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'), splitScalar: (k: bigint) => { const n = secp256k1N; @@ -105,19 +90,11 @@ export const secp256k1 = createCurve( sha256 ); -// Schnorr signatures are superior to ECDSA from above. -// Below is Schnorr-specific code as per BIP0340. +// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code. // https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki const _0n = BigInt(0); const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P; const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N; - -const TAGS = { - challenge: 'BIP0340/challenge', - aux: 'BIP0340/aux', - nonce: 'BIP0340/nonce', -} as const; - /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */ const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {}; function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array { @@ -132,49 +109,53 @@ function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array { const pointToBytes = (point: PointType) => point.toRawBytes(true).slice(1); const numTo32b = (n: bigint) => numberToBytesBE(n, 32); +const modP = (x: bigint) => mod(x, secp256k1P); const modN = (x: bigint) => mod(x, secp256k1N); const Point = secp256k1.ProjectivePoint; const GmulAdd = (Q: PointType, a: bigint, b: bigint) => Point.BASE.multiplyAndAddUnsafe(Q, a, b); -const hex32ToInt = (key: Hex) => bytesToInt(ensureBytes(key, 32)); function schnorrGetExtPubKey(priv: PrivKey) { - let d = typeof priv === 'bigint' ? priv : hex32ToInt(priv); + const d = secp256k1.utils.normPrivateKeyToScalar(priv); const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d' return { point, scalar, bytes: pointToBytes(point) }; } function lift_x(x: bigint): PointType { if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p. - const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p. + const xx = modP(x * x); + const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p. let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p. - if (y % 2n !== 0n) y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and + if (y % 2n !== 0n) y = modP(-y); // Return the unique point P such that x(P) = x and const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. p.assertValidity(); return p; } function challenge(...args: Uint8Array[]): bigint { - return modN(bytesToInt(taggedHash(TAGS.challenge, ...args))); + return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args))); } -// Schnorr's pubkey is just `x` of Point (BIP340) +/** + * Schnorr public key is just `x` coordinate of Point as per BIP340. + */ function schnorrGetPublicKey(privateKey: Hex): Uint8Array { return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G) } -// 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 +/** + * 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. + */ function schnorrSign( message: Hex, privateKey: PrivKey, auxRand: Hex = randomBytes(32) ): Uint8Array { - if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`); - const m = ensureBytes(message); // checks for isWithinCurveOrder - const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); - const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array - const t = numTo32b(d ^ bytesToInt(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a) - const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m) - const k_ = modN(bytesToInt(rand)); // Let k' = int(rand) mod n + const m = ensureBytes('message', message); + const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder + const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array + const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a) + const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m) + const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0. const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G. const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n. @@ -187,18 +168,19 @@ function schnorrSign( } /** - * Verifies Schnorr signature synchronously. + * Verifies Schnorr signature. */ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean { + const sig = ensureBytes('signature', signature, 64); + const m = ensureBytes('message', message); + const pub = ensureBytes('publicKey', publicKey, 32); try { - const P = lift_x(hex32ToInt(publicKey)); // P = lift_x(int(pk)); fail if that fails - const sig = ensureBytes(signature, 64); - const r = bytesToInt(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p. + const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails + const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p. if (!fe(r)) return false; - const s = bytesToInt(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n. + const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n. if (!ge(s)) return false; - const m = ensureBytes(message); - const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n + const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r. @@ -212,11 +194,12 @@ export const schnorr = { sign: schnorrSign, verify: schnorrVerify, utils: { + randomPrivateKey: secp256k1.utils.randomPrivateKey, getExtendedPublicKey: schnorrGetExtPubKey, lift_x, pointToBytes, numberToBytesBE, - bytesToNumberBE: bytesToInt, + bytesToNumberBE, taggedHash, mod, }, @@ -259,7 +242,7 @@ const mapSWU = mapToCurveSimpleSWU(Fp, { B: BigInt('1771'), Z: Fp.create(BigInt('-11')), }); -const { hashToCurve, encodeToCurve } = htf.hashToCurve( +export const { hashToCurve, encodeToCurve } = htf.hashToCurve( secp256k1.ProjectivePoint, (scalars: bigint[]) => { const { x, y } = mapSWU(Fp.create(scalars[0])); @@ -275,4 +258,3 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve( hash: sha256, } ); -export { hashToCurve, encodeToCurve };