static Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n
static Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n
- static secp256k1_CURVE: WeierstrassOpts<bigint> = {
+ static CURVE: WeierstrassOpts<bigint> = {
p: this.P,
n: this.N,
h: 1n,
/** 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
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
}
}
/** 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 {
}
}
+ /** 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()
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
}
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
}
}
}
+ 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.
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
}
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.
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
* @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
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
// --------------