* noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com)
*/
+/** Alias to Uint8Array. */
+type Bytes = Uint8Array<ArrayBuffer>
+
+/** Point in 3d xyz coordinates. */
type Point = {
X: bigint
Y: bigint
Z: bigint
}
-/** Alias to Uint8Array. */
-type Bytes = Uint8Array<ArrayBuffer>
-
/** Point in 2d xy affine coordinates. */
type AffinePoint = {
x: bigint
y: bigint
}
-/** Type arithmetic to enable validation of literal integer types */
-type EnsureNumber<A extends number> = A extends number ? A : never
-type Length<T extends any[]> = EnsureNumber<T['length']>
-type Tuple<N extends number, T, Accumulator extends T[] = []> = Length<Accumulator> extends N ? Accumulator : Tuple<N, T, [...Accumulator, T]>
-type Add<A extends number, B extends number> = Length<[...Tuple<A, any>, ...Tuple<B, any>]>
-
-/** Public key length definitions */
-type Secp256k1Lengths = {
- compressed: Add<typeof Secp256k1.L, 1>
- uncompressed: Add<typeof Secp256k1.L2, 1>
-}
-
export class Secp256k1 {
/**
* Curve params. secp256k1 is short weierstrass / koblitz curve. Equation is y² == x³ + ax + b.
static L: 32 = 32 // field / group byte length
static L2: 64 = 64
- static pkLength: Secp256k1Lengths = {
- compressed: 33,
- uncompressed: 65
- }
+ static pkLength: 33 = 33
// ## Helpers
// ----------
const tail = bytes.subarray(1)
const x = this.bytesToBigint(tail.subarray(0, this.L))
// No actual validation is done here: use .assertValidity()
- if (length === this.pkLength.compressed && (head === 0x02 || head === 0x03)) {
+ if (length === this.pkLength && (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)
if (evenH !== evenY) y = this.M(-y)
p = this.Point(x, y, 1n)
}
- // 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 this.isValidPoint(p)
}
/** Converts point to 33/65-byte Uint8Array. */
- static pointToBytes (p: Point, isCompressed = true): Bytes {
+ static pointToBytes (p: Point): 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))
+ return this.concatBytes(this.getPrefix(y), x32b)
}
/** Generator / base point */
}
/** Creates 33/65-byte public key from 32-byte private key. */
- static getPublicKey (sk: Bytes, isCompressed = true): Bytes {
- const pk = this.pointToBytes(this.wNAF(this.secretKeyToScalar(sk)).p, isCompressed)
+ static getPublicKey (sk: Bytes): Bytes {
+ const pk = this.pointToBytes(this.wNAF(this.secretKeyToScalar(sk)).p)
return this.isValidPublicKey(pk) ? pk : this.err('derived invalid public key from secret key')
}
- static isValidPublicKey (pk: Bytes, isCompressed?: boolean): boolean {
+ static isValidPublicKey (pk: Bytes): boolean {
try {
const l = pk.length
- if (isCompressed === true && l !== this.pkLength.compressed) return false
- if (isCompressed === false && l !== this.pkLength.uncompressed) return false
+ if (l !== this.pkLength) return false
return !!this.pointFromBytes(pk)
} catch (error) {
return false