From d4a7e0e2ec8a5f5b910c760fe3c97efd6bc73e8b Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Fri, 5 Dec 2025 09:08:22 -0800 Subject: [PATCH] Public key derivation always multiplies secret key scalar by base point G, so eliminate extra multiplication call and code that is never called in this particular implementation. --- src/lib/crypto/secp256k1.ts | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/lib/crypto/secp256k1.ts b/src/lib/crypto/secp256k1.ts index 0d56660..7d9198c 100644 --- a/src/lib/crypto/secp256k1.ts +++ b/src/lib/crypto/secp256k1.ts @@ -19,7 +19,6 @@ type Point = { negate: () => Point double: () => Point add: (other: Point) => Point - multiply: (n: bigint) => Point toAffine: () => AffinePoint assertValidity: () => Point toBytes: (isCompressed?: boolean) => Bytes @@ -217,27 +216,6 @@ export class Secp256k1 { Z3 = M(Z3 + t0) // step 40 return secp256k1.Point(X3, Y3, Z3) }, - /** - * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n. - * Uses {@link wNAF} for base point. - * Uses fake point to mitigate side-channel leakage. - * @param n scalar by which point is multiplied - * @param safe safe mode guards against timing attacks; unsafe mode is faster - */ - multiply (n: bigint): Point { - if (secp256k1.bigintInRange(n, 1n, secp256k1.N) === 1n) return this - if (this.equals(secp256k1.G)) return secp256k1.wNAF(n).p - // init result point & fake point - let p = secp256k1.I - let f = secp256k1.G - for (let d: Point = this; n > 0n; d = d.double(), n >>= 1n) { - // if bit is present, add to point - // if not present, add to fake, for timing safety - if (n & 1n) p = p.add(d) - else f = f.add(d) - } - return 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 @@ -325,7 +303,7 @@ export class Secp256k1 { /** Creates 33/65-byte public key from 32-byte private key. */ static getPublicKey (sk: Bytes, isCompressed = true): Bytes { - return this.G.multiply(this.secretKeyToScalar(sk)).toBytes(isCompressed) + return this.wNAF(this.secretKeyToScalar(sk)).p.toBytes(isCompressed) } static isValidPublicKey (pk: Bytes, isCompressed?: boolean): boolean { @@ -375,6 +353,7 @@ export class Secp256k1 { return cnd ? n : p } static wNAF (n: bigint): { p: Point; f: Point } { + console.log('mult', n) const comp = this.Gpows || (this.Gpows = this.precompute()) let p = this.I let f = this.G // f must be G, or could become I in the end -- 2.47.3