From: Chris Duncan Date: Sun, 30 Nov 2025 09:43:20 +0000 (-0800) Subject: Extract or delete static members from inner classes to move toward eliminating class... X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=c3d994dabc2a87c8c1461d8d00b6fe627e900b94;p=libnemo.git Extract or delete static members from inner classes to move toward eliminating class name self-references. --- diff --git a/src/lib/crypto/secp256k1.ts b/src/lib/crypto/secp256k1.ts index 6504124..61ab406 100644 --- a/src/lib/crypto/secp256k1.ts +++ b/src/lib/crypto/secp256k1.ts @@ -104,7 +104,7 @@ export class Secp256k1 { static Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n static Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n - static secp256k1_CURVE: WeierstrassOpts = { + static CURVE: WeierstrassOpts = { p: this.P, n: this.N, h: 1n, @@ -261,8 +261,6 @@ export class Secp256k1 { /** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */ static Point = class { - static BASE: Point - static ZERO: Point readonly X: bigint readonly Y: bigint readonly Z: bigint @@ -272,41 +270,6 @@ export class Secp256k1 { this.Z = Secp256k1.FpIsValid(Z) Object.freeze(this) } - static CURVE (): WeierstrassOpts { - return Secp256k1.secp256k1_CURVE - } - /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */ - static fromAffine (ap: AffinePoint): Point { - const { x, y } = ap - return x === 0n && y === 0n ? Secp256k1.I : new this(x, y, 1n) - } - /** Convert Uint8Array or hex string to Point. */ - static fromBytes (bytes: Bytes): Point { - Secp256k1.abytes(bytes) - const { publicKey: comp, publicKeyUncompressed: uncomp } = Secp256k1.lengths // e.g. for 32-byte: 33, 65 - let p: Point | undefined = undefined - const length = bytes.length - const head = bytes[0] - const tail = bytes.subarray(1) - const x = Secp256k1.sliceBytesNumBE(tail, 0, Secp256k1.L) - // No actual validation is done here: use .assertValidity() - if (length === comp && (head === 0x02 || head === 0x03)) { - // Equation is y² == x³ + ax + b. We calculate y from x. - // y = √y²; there are two solutions: y, -y. Determine proper solution based on prefix - let y = Secp256k1.lift_x(x) - const evenY = Secp256k1.isEven(y) - const evenH = Secp256k1.isEven(Secp256k1.big(head)) - if (evenH !== evenY) y = Secp256k1.M(-y) - p = new this(x, y, 1n) - } - // Uncompressed 65-byte point, 0x04 prefix - if (length === uncomp && head === 0x04) p = new this(x, Secp256k1.sliceBytesNumBE(tail, Secp256k1.L, Secp256k1.L2), 1n) - // Validate point - return p ? p.assertValidity() : Secp256k1.err('bad point: not on curve') - } - static fromHex (hex: string): Point { - return Secp256k1.Point.fromBytes(Secp256k1.hexToBytes(hex)) - } get x (): bigint { return this.toAffine().x } @@ -328,7 +291,7 @@ export class Secp256k1 { } /** Flip point over y coordinate. */ negate (): Point { - return new Secp256k1.Point(this.X, Secp256k1.M(-this.Y), this.Z) + return new (this.constructor as typeof Secp256k1.Point)(this.X, Secp256k1.M(-this.Y), this.Z) } /** Point doubling: P+P, complete formula. */ double (): Point { @@ -427,15 +390,43 @@ export class Secp256k1 { } } + /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */ + static pointFromAffine (ap: AffinePoint): Point { + const { x, y } = ap + return x === 0n && y === 0n ? Secp256k1.I : new this.Point(x, y, 1n) + } + /** Convert Uint8Array or hex string to Point. */ + static pointFromBytes (bytes: Bytes): Point { + this.abytes(bytes) + const { publicKey: comp, publicKeyUncompressed: uncomp } = this.lengths // e.g. for 32-byte: 33, 65 + let p: Point | undefined = undefined + const length = bytes.length + const head = bytes[0] + const tail = bytes.subarray(1) + const x = this.sliceBytesNumBE(tail, 0, this.L) + // No actual validation is done here: use .assertValidity() + if (length === comp && (head === 0x02 || head === 0x03)) { + // Equation is y² == x³ + ax + b. We calculate y from x. + // y = √y²; there are two solutions: y, -y. Determine proper solution based on prefix + let y = this.lift_x(x) + const evenY = this.isEven(y) + const evenH = this.isEven(this.big(head)) + if (evenH !== evenY) y = this.M(-y) + p = new this.Point(x, y, 1n) + } + // Uncompressed 65-byte point, 0x04 prefix + if (length === uncomp && head === 0x04) p = new this.Point(x, this.sliceBytesNumBE(tail, this.L, this.L2), 1n) + // Validate point + return p ? p.assertValidity() : this.err('bad point: not on curve') + } + static pointFromHex (hex: string): Point { + return this.pointFromBytes(Secp256k1.hexToBytes(hex)) + } + /** Generator / base point */ static G: Point = new this.Point(this.Gx, this.Gy, 1n) /** Identity / zero point */ static I: Point = new this.Point(0n, 1n, 0n) - // Static aliases - static { - this.Point.BASE = this.G - this.Point.ZERO = this.I - } /** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */ static doubleScalarMulUns = (R: Point, u1: bigint, u2: bigint): Point => { return this.G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity() @@ -470,7 +461,7 @@ export class Secp256k1 { const l = publicKey.length if (isCompressed === true && l !== comp) return false if (isCompressed === false && l !== publicKeyUncompressed) return false - return !!this.Point.fromBytes(publicKey) + return !!this.pointFromBytes(publicKey) } catch (error) { return false } @@ -526,17 +517,6 @@ export class Secp256k1 { if (recovery != null) this.recovery = recovery Object.freeze(this) } - static fromBytes (b: Bytes, format: ECDSASignatureFormat = Secp256k1.SIG_COMPACT): Signature { - Secp256k1.assertSigLength(b, format) - let rec: number | undefined - if (format === Secp256k1.SIG_RECOVERED) { - rec = b[0] - b = b.subarray(1) - } - const r = Secp256k1.sliceBytesNumBE(b, 0, Secp256k1.L) - const s = Secp256k1.sliceBytesNumBE(b, Secp256k1.L, Secp256k1.L2) - return new this(r, s, rec) - } addRecoveryBit (bit: number): RecoveredSignature { return new Secp256k1.Signature(this.r, this.s, bit) as RecoveredSignature } @@ -554,6 +534,18 @@ export class Secp256k1 { } } + static signatureFromBytes (b: Bytes, format: ECDSASignatureFormat = Secp256k1.SIG_COMPACT): Signature { + Secp256k1.assertSigLength(b, format) + let rec: number | undefined + if (format === Secp256k1.SIG_RECOVERED) { + rec = b[0] + b = b.subarray(1) + } + const r = Secp256k1.sliceBytesNumBE(b, 0, Secp256k1.L) + const s = Secp256k1.sliceBytesNumBE(b, Secp256k1.L, Secp256k1.L2) + return new this.Signature(r, s, rec) + } + /** * RFC6979: ensure ECDSA msg is X bytes, convert to BigInt. * RFC suggests optional truncating via bits2octets. @@ -729,9 +721,9 @@ export class Secp256k1 { this.assertSigLength(sig, format) this.abytes(publicKey, undefined, 'publicKey') try { - const { r, s } = this.Signature.fromBytes(sig, format) + const { r, s } = this.signatureFromBytes(sig, format) const h = this.bits2int_modN(messageHash) // Truncate hash - const P = this.Point.fromBytes(publicKey) // Validate public key + 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 @@ -849,7 +841,7 @@ export class Secp256k1 { } static _recover = (signature: Bytes, messageHash: Bytes) => { - const sig = this.Signature.fromBytes(signature, 'recovered') + const sig = this.signatureFromBytes(signature, 'recovered') const { r, s, recovery } = sig // 0 or 1 recovery id determines sign of "y" coordinate. // 2 or 3 means q.x was >N. @@ -859,7 +851,7 @@ export class Secp256k1 { this.FpIsValidNot0(radj) // ensure q.x is still a field element const head = this.getPrefix(this.big(recovery!)) // head is 0x02 or 0x03 const Rb = this.concatBytes(head, this.numTo32b(radj)) // concat head + r - const R = this.Point.fromBytes(Rb) + const R = this.pointFromBytes(Rb) const ir = this.invert(radj, this.N) // r^-1 const u1 = this.modN(-h * ir) // -hr^-1 const u2 = this.modN(s * ir) // sr^-1 @@ -892,7 +884,7 @@ export class Secp256k1 { * @returns public key C */ static getSharedSecret = (secretKeyA: Bytes, publicKeyB: Bytes, isCompressed = true): Bytes => { - return this.Point.fromBytes(publicKeyB).multiply(this.secretKeyToScalar(secretKeyA)).toBytes(isCompressed) + return this.pointFromBytes(publicKeyB).multiply(this.secretKeyToScalar(secretKeyA)).toBytes(isCompressed) } // FIPS 186 B.4.1 compliant key generation produces private keys @@ -1083,21 +1075,14 @@ export class Secp256k1 { static verifySchnorrAsync = async (s: Bytes, m: Bytes, p: Bytes): Promise => this._verifSchnorr(s, m, p, this.challengeAsync) as Promise - static schnorr: { - keygen: typeof Secp256k1.keygenSchnorr, - getPublicKey: typeof Secp256k1.pubSchnorr - sign: typeof Secp256k1.signSchnorr - verify: typeof Secp256k1.verifySchnorr - signAsync: typeof Secp256k1.signSchnorrAsync, - verifyAsync: typeof Secp256k1.verifySchnorrAsync - } = { - keygen: this.keygenSchnorr, - getPublicKey: this.pubSchnorr, - sign: this.signSchnorr, - verify: this.verifySchnorr, - signAsync: this.signSchnorrAsync, - verifyAsync: this.verifySchnorrAsync, - } + static schnorr = { + keygen: this.keygenSchnorr, + getPublicKey: this.pubSchnorr, + sign: this.signSchnorr, + verify: this.verifySchnorr, + signAsync: this.signSchnorrAsync, + verifyAsync: this.verifySchnorrAsync, + } // ## Precomputes // --------------