]> git.codecow.com Git - libnemo.git/commitdiff
Promote final point functions to static class members.
authorChris Duncan <chris@zoso.dev>
Fri, 5 Dec 2025 22:07:51 +0000 (14:07 -0800)
committerChris Duncan <chris@zoso.dev>
Fri, 5 Dec 2025 22:07:51 +0000 (14:07 -0800)
src/lib/crypto/secp256k1.ts

index 8f72788b940fe286bb94789749a2cd6a2b32ff63..50c006884961f3e2ab8ad4f70b373ab2fce0b342 100644 (file)
@@ -15,8 +15,6 @@ type Point = {
        X: bigint
        Y: bigint
        Z: bigint
-       toAffine: () => AffinePoint
-       toBytes: (isCompressed?: boolean) => Bytes
 }
 
 /** Alias to Uint8Array. */
@@ -208,6 +206,19 @@ export class Secp256k1 {
                return this.Point(X3, Y3, Z3)
        }
 
+       /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
+       static Affine (p: Point): AffinePoint {
+               const { X: x, Y: y, Z: z } = p
+               // fast-paths for ZERO point OR Z=1
+               if (this.Equals(p, this.I)) return { x: 0n, y: 0n }
+               if (z === 1n) return { x, y }
+               const iz = this.invert(z)
+               // (Z * Z^-1) must be 1, otherwise bad math
+               if (this.M(z * iz) !== 1n) this.err('inverse invalid')
+               // x = X*Z^-1; y = Y*Z^-1
+               return { x: this.M(x * iz), y: this.M(y * iz) }
+       }
+
        /** Point doubling: P+P */
        static Double (p: Point) {
                return this.Add(p, p)
@@ -236,25 +247,6 @@ export class Secp256k1 {
                        X: this.bigintInRange(X, 0n, this.P),
                        Y: this.bigintInRange(Y, 1n, this.P), // Y can't be 0 in Projective
                        Z: this.bigintInRange(Z, 0n, this.P),
-                       /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
-                       toAffine (): AffinePoint {
-                               const { X: x, Y: y, Z: z } = this
-                               // fast-paths for ZERO point OR Z=1
-                               if (secp256k1.Equals(this, secp256k1.I)) return { x: 0n, y: 0n }
-                               if (z === 1n) return { x, y }
-                               const iz = secp256k1.invert(z)
-                               // (Z * Z^-1) must be 1, otherwise bad math
-                               if (secp256k1.M(z * iz) !== 1n) secp256k1.err('inverse invalid')
-                               // x = X*Z^-1; y = Y*Z^-1
-                               return { x: secp256k1.M(x * iz), y: secp256k1.M(y * iz) }
-                       },
-                       /** Converts point to 33/65-byte Uint8Array. */
-                       toBytes (isCompressed = true): Bytes {
-                               const { x, y } = secp256k1.isValidPoint(this).toAffine()
-                               const x32b = secp256k1.bigintTo32Bytes(x)
-                               if (isCompressed) return secp256k1.concatBytes(secp256k1.getPrefix(y), x32b)
-                               return secp256k1.concatBytes(Uint8Array.of(0x04), x32b, secp256k1.bigintTo32Bytes(y))
-                       }
                })
        }
 
@@ -282,6 +274,14 @@ export class Secp256k1 {
                return this.isValidPoint(p)
        }
 
+       /** Converts point to 33/65-byte Uint8Array. */
+       static pointToBytes (p: Point, isCompressed = true): Bytes {
+               const { x, y } = this.Affine(this.isValidPoint(p))
+               const x32b = this.bigintTo32Bytes(x)
+               if (isCompressed) return this.concatBytes(this.getPrefix(y), x32b)
+               return this.concatBytes(Uint8Array.of(0x04), x32b, this.bigintTo32Bytes(y))
+       }
+
        /** Generator / base point */
        static G: Point = this.Point(this.Gx, this.Gy, 1n)
        /** Identity / zero point */
@@ -310,7 +310,7 @@ export class Secp256k1 {
        /** Checks if the point is valid and on-curve. */
        static isValidPoint (p?: Point): Point {
                if (p) {
-                       const { x, y } = p.toAffine() // convert to 2d xy affine point.
+                       const { x, y } = this.Affine(p) // convert to 2d xy affine point.
                        this.bigintInRange(x, 1n, this.P) // must be in range 1 <= x,y < P
                        this.bigintInRange(y, 1n, this.P)
                        // y² == x³ + ax + b, equation sides must be equal
@@ -327,7 +327,8 @@ export class Secp256k1 {
 
        /** Creates 33/65-byte public key from 32-byte private key. */
        static getPublicKey (sk: Bytes, isCompressed = true): Bytes {
-               return this.wNAF(this.secretKeyToScalar(sk)).p.toBytes(isCompressed)
+               const pk = this.pointToBytes(this.wNAF(this.secretKeyToScalar(sk)).p, isCompressed)
+               return this.isValidPublicKey(pk) ? pk : this.err('derived invalid public key from secret key')
        }
 
        static isValidPublicKey (pk: Bytes, isCompressed?: boolean): boolean {