From 443ccd63d06ce96f534a5d70da58f6e985527f93 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Wed, 3 Dec 2025 14:45:02 -0800 Subject: [PATCH] Add modular division helper to be explicit about the arguments. --- src/lib/crypto/secp256k1.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/crypto/secp256k1.ts b/src/lib/crypto/secp256k1.ts index c5c0560..e1e2bda 100644 --- a/src/lib/crypto/secp256k1.ts +++ b/src/lib/crypto/secp256k1.ts @@ -226,12 +226,12 @@ export class Secp256k1 { } /** modular division */ - static M (a: bigint, b: bigint = this.P): bigint { + static M (a: bigint, b: bigint): bigint { const r = a % b return r >= 0n ? r : b + r } - static modN = (a: bigint) => this.M(a, this.N) + static modP = (a: bigint) => this.M(a, this.P) /** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */ // prettier-ignore @@ -256,7 +256,7 @@ export class Secp256k1 { // ----------------- /** secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0, making it x³+b */ - static koblitz = (x: bigint) => this.M(this.M(x * x) * x + this._b) + static koblitz = (x: bigint) => this.modP(this.modP(x * x) * x + this._b) /** assert is element of field mod P (incl. 0) */ static FpIsValid = (n: bigint) => this.bigintInRange(n, 0n, this.P) /** assert is element of field mod P (excl. 0) */ @@ -281,7 +281,7 @@ export class Secp256k1 { if (e & 1n) r = (r * num) % this.P // Uses exponentiation by squaring. num = (num * num) % this.P // Not constant-time. } - return this.M(r * r) === c ? r : this.err('sqrt invalid') // check if result is valid + return this.modP(r * r) === c ? r : this.err('sqrt invalid') // check if result is valid } /** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */ @@ -295,15 +295,15 @@ export class Secp256k1 { equals (other: Point): boolean { const { X: X1, Y: Y1, Z: Z1 } = this const { X: X2, Y: Y2, Z: Z2 } = other - const X1Z2 = secp256k1.M(X1 * Z2) - const X2Z1 = secp256k1.M(X2 * Z1) - const Y1Z2 = secp256k1.M(Y1 * Z2) - const Y2Z1 = secp256k1.M(Y2 * Z1) + const X1Z2 = secp256k1.modP(X1 * Z2) + const X2Z1 = secp256k1.modP(X2 * Z1) + const Y1Z2 = secp256k1.modP(Y1 * Z2) + const Y2Z1 = secp256k1.modP(Y2 * Z1) return X1Z2 === X2Z1 && Y1Z2 === Y2Z1 }, /** Flip point over y coordinate. */ negate (): Point { - return secp256k1.Point(X, secp256k1.M(-Y), Z) + return secp256k1.Point(X, secp256k1.modP(-Y), Z) }, /** Point doubling: P+P, complete formula. */ double (): Point { @@ -316,7 +316,7 @@ export class Secp256k1 { */ // prettier-ignore add (other: Point): Point { - const M = (v: bigint): bigint => secp256k1.M(v) + const M = (v: bigint): bigint => secp256k1.modP(v) const { X: X1, Y: Y1, Z: Z1 } = { X, Y, Z } const { X: X2, Y: Y2, Z: Z2 } = other const a = 0n @@ -378,9 +378,9 @@ export class Secp256k1 { if (z === 1n) return { x, y } const iz = secp256k1.invert(z, secp256k1.P) // (Z * Z^-1) must be 1, otherwise bad math - if (secp256k1.M(z * iz) !== 1n) secp256k1.err('inverse invalid') + if (secp256k1.modP(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) } + return { x: secp256k1.modP(x * iz), y: secp256k1.modP(y * iz) } }, /** Checks if the point is valid and on-curve. */ assertValidity (): Point { @@ -388,7 +388,7 @@ export class Secp256k1 { secp256k1.FpIsValidNot0(x) // must be in range 1 <= x,y < P secp256k1.FpIsValidNot0(y) // y² == x³ + ax + b, equation sides must be equal - return secp256k1.M(y * y) === secp256k1.koblitz(x) ? this : secp256k1.err('bad point: not on curve') + return secp256k1.modP(y * y) === secp256k1.koblitz(x) ? this : secp256k1.err('bad point: not on curve') }, /** Converts point to 33/65-byte Uint8Array. */ toBytes (isCompressed = true): Bytes { @@ -425,7 +425,7 @@ export class Secp256k1 { let y = this.lift_x(x) const evenY = this.isEven(y) const evenH = this.isEven(BigInt(head)) - if (evenH !== evenY) y = this.M(-y) + if (evenH !== evenY) y = this.modP(-y) p = this.Point(x, y, 1n) } // Uncompressed 65-byte point, 0x04 prefix @@ -1029,7 +1029,7 @@ export class Secp256k1 { // 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.M(-y) + 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() -- 2.47.3