X: bigint
Y: bigint
Z: bigint
- equals: (other: Point) => boolean
- negate: () => Point
- double: () => Point
toAffine: () => AffinePoint
- assertValidity: () => Point
toBytes: (isCompressed?: boolean) => Bytes
}
return this.Point(X3, Y3, Z3)
}
+ /** Point doubling: P+P */
+ static Double (p: Point) {
+ return this.Add(p, p)
+ }
+
+ /** Equality check: compare points P&Q. */
+ static Equals (p: Point, q: Point): boolean {
+ const { X: X1, Y: Y1, Z: Z1 } = p
+ const { X: X2, Y: Y2, Z: Z2 } = q
+ const X1Z2 = this.M(X1 * Z2)
+ const X2Z1 = this.M(X2 * Z1)
+ const Y1Z2 = this.M(Y1 * Z2)
+ const Y2Z1 = this.M(Y2 * Z1)
+ return X1Z2 === X2Z1 && Y1Z2 === Y2Z1
+ }
+
+ /** Flip point over y coordinate. */
+ static Negate (p: Point) {
+ return this.Point(p.X, this.M(-p.Y), p.Z)
+ }
+
/** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */
static Point (X: bigint, Y: bigint, Z: bigint): Point {
const secp256k1 = this
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),
- /** Equality check: compare points P&Q. */
- 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)
- return X1Z2 === X2Z1 && Y1Z2 === Y2Z1
- },
- /** Flip point over y coordinate. */
- negate (): Point {
- return secp256k1.Point(X, secp256k1.M(-Y), Z)
- },
- /** Point doubling: P+P, complete formula. */
- double (): Point {
- return secp256k1.Add(this, this)
- },
/** 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 (this.equals(secp256k1.I)) return { x: 0n, y: 0n }
+ 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
// x = X*Z^-1; y = Y*Z^-1
return { x: secp256k1.M(x * iz), y: secp256k1.M(y * iz) }
},
- /** Checks if the point is valid and on-curve. */
- assertValidity (): Point {
- const { x, y } = this.toAffine() // convert to 2d xy affine point.
- secp256k1.bigintInRange(x, 1n, secp256k1.P) // must be in range 1 <= x,y < P
- secp256k1.bigintInRange(y, 1n, secp256k1.P)
- // 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')
- },
/** Converts point to 33/65-byte Uint8Array. */
toBytes (isCompressed = true): Bytes {
- const { x, y } = this.assertValidity().toAffine()
+ 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))
// Uncompressed 65-byte point, 0x04 prefix
if (length === this.pkLength.uncompressed && head === 0x04) p = this.Point(x, this.bytesToBigint(tail.subarray(this.L, this.L2)), 1n)
// Validate point
- return p ? p.assertValidity() : this.err('bad point: not on curve')
+ return this.isValidPoint(p)
}
/** Generator / base point */
return bytes
}
+ /** 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.
+ 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
+ return this.M(y * y) === this.koblitz(x) ? p : this.err('bad point: not on curve')
+ }
+ this.err('bad point: undefined')
+ }
+
/** Normalize private key to scalar (bigint). Verifies scalar is in range 1<s<N */
static secretKeyToScalar (sk: Bytes): bigint {
const num = this.bytesToBigint(this.abytes(sk, this.L, 'secret key'))
b = this.Add(b, p)
points.push(b)
} // i=1, bc we skip 0
- p = b.double()
+ p = this.Double(b)
}
return points
}
// const-time negate
static ctneg (cnd: boolean, p: Point): Point {
- const n = p.negate()
+ const n = this.Negate(p)
return cnd ? n : p
}
static wNAF (n: bigint): { p: Point; f: Point } {