]> git.codecow.com Git - libnemo.git/commitdiff
Extract or delete static members from inner classes to move toward eliminating class...
authorChris Duncan <chris@zoso.dev>
Sun, 30 Nov 2025 09:43:20 +0000 (01:43 -0800)
committerChris Duncan <chris@zoso.dev>
Sun, 30 Nov 2025 09:43:20 +0000 (01:43 -0800)
src/lib/crypto/secp256k1.ts

index 65041241e2704d66b48edb0f6ca4cb0fb14ab597..61ab4069627e3803c8b5749cfe47bad12ff6632d 100644 (file)
@@ -104,7 +104,7 @@ export class Secp256k1 {
        static Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n
        static Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n
 
-       static secp256k1_CURVE: WeierstrassOpts<bigint> = {
+       static CURVE: WeierstrassOpts<bigint> = {
                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<bigint> {
-                       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<boolean> =>
                this._verifSchnorr(s, m, p, this.challengeAsync) as Promise<boolean>
 
-       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
        // --------------