From: Chris Duncan Date: Fri, 5 Dec 2025 22:43:19 +0000 (-0800) Subject: Refactor unused functions and combine where possible. X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=a97476bbd791714d0f173f01f52f3b32aef92f9b;p=libnemo.git Refactor unused functions and combine where possible. --- diff --git a/src/lib/crypto/secp256k1.ts b/src/lib/crypto/secp256k1.ts index f5d8ad8..2daef48 100644 --- a/src/lib/crypto/secp256k1.ts +++ b/src/lib/crypto/secp256k1.ts @@ -48,6 +48,11 @@ export class Secp256k1 { static L: 32 = 32 // field / group byte length static C: 33 = 33 // byte length of SEC1 compressed form of coordinate pair + /** Generator / base point */ + static G: Point = this.Point(this.Gx, this.Gy, 1n) + /** Identity / zero point */ + static I: Point = this.Point(0n, 1n, 0n) + // ## Helpers // ---------- static err (message = ''): never { @@ -72,21 +77,6 @@ export class Secp256k1 { return value } - static hexToBytes (hex: string): Bytes { - if (!/[0-9A-Fa-f]+/.test(hex ?? '')) return this.err('hex invalid') - if (hex.length % 2) hex = `0${hex}` - const intArray = hex.match(/.{2}/g)?.map(v => parseInt(v, 16)) - return new Uint8Array(intArray ?? []) - } - - // prettier-ignore - static concatBytes (...arrs: Bytes[]): Bytes { - const r = new Uint8Array(arrs.reduce((sum, a) => sum + this.abytes(a).length, 0)) // create u8a of summed length - let pad = 0 // walk through each array, - arrs.forEach(a => { r.set(a, pad); pad += a.length }) // ensure they have proper type - return r - } - static bigintInRange (n: bigint, min: bigint, max: bigint, msg?: string): bigint { return typeof n === 'bigint' && min <= n && n < max ? n @@ -95,8 +85,8 @@ export class Secp256k1 { /** modular division */ static M (a: bigint): bigint { - const r = a % this.P - return r >= 0n ? r : this.P + r + const p = this.P, r = a % p + return r >= 0n ? r : p + r } /** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */ @@ -118,7 +108,6 @@ export class Secp256k1 { /** secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0, making it x³+b */ static koblitz = (x: bigint): bigint => this.M(this.M(x * x) * x + this.b) static isEven = (y: bigint): boolean => (y & 1n) === 0n - static getPrefix = (y: bigint) => Uint8Array.of(this.isEven(y) ? 0x02 : 0x03) /** lift_x from BIP340 calculates square root. Validates x, then validates root*root. */ static lift_x (x: bigint): bigint { // Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1) @@ -144,7 +133,6 @@ export class Secp256k1 { static Add (p: Point, q: Point): Point { const { X: X1, Y: Y1, Z: Z1 } = p const { X: X2, Y: Y2, Z: Z2 } = q - const P = this.P const a = this.a const b3 = this.M(this.b * 3n) let X3 = 0n, Y3 = 0n, Z3 = 0n @@ -227,7 +215,6 @@ export class Secp256k1 { /** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */ static Point (X: bigint, Y: bigint, Z: bigint): Point { - const secp256k1 = this return Object.freeze({ X: this.bigintInRange(X, 0n, this.P), Y: this.bigintInRange(Y, 1n, this.P), // Y can't be 0 in Projective @@ -235,40 +222,6 @@ export class Secp256k1 { }) } - /** Convert Uint8Array or hex string to Point. */ - static pointFromBytes (bytes: Bytes): Point { - this.abytes(bytes) - let p: Point | undefined = undefined - const length = bytes.length - const head = bytes[0] - 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.C && (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(BigInt(head)) - if (evenH !== evenY) y = this.M(-y) - p = this.Point(x, y, 1n) - } - // Validate point - return this.isValidPoint(p) - } - - /** Converts point to 33/65-byte Uint8Array. */ - static pointToBytes (p: Point): Bytes { - const { x, y } = this.Affine(this.isValidPoint(p)) - const x32b = this.bigintTo32Bytes(x) - return this.concatBytes(this.getPrefix(y), x32b) - } - - /** Generator / base point */ - static G: Point = this.Point(this.Gx, this.Gy, 1n) - /** Identity / zero point */ - static I: Point = this.Point(0n, 1n, 0n) - static bytesToBigint (b: Bytes): bigint { let int = BigInt(b[0]), len = b.length for (let i = 1; i < len; i++) { @@ -307,17 +260,42 @@ export class Secp256k1 { return this.bigintInRange(num, 1n, this.N, 'invalid secret key: outside of range') } - /** Creates 33/65-byte public key from 32-byte private key. */ + /** Derives a compressed 33-byte public key from a 32-byte private key. */ static getPublicKey (sk: Bytes): Bytes { - const pk = this.pointToBytes(this.wNAF(this.secretKeyToScalar(sk)).p) + const p = this.wNAF(this.secretKeyToScalar(sk)).p + let { x, y } = this.Affine(this.isValidPoint(p)) + const len = this.C + const pk = new Uint8Array(len) + for (let i = len - 1; i >= 1; i--) { + pk[i] = Number(x & 0xffn) + x >>= 8n + } + pk[0] = this.isEven(y) ? 0x02 : 0x03 return this.isValidPublicKey(pk) ? pk : this.err('derived invalid public key from secret key') } + /** Converts point to 33-byte Uint8Array and checks validity. */ static isValidPublicKey (pk: Bytes): boolean { + if (pk.length !== this.C) return false try { - const l = pk.length - if (l !== this.C) return false - return !!this.pointFromBytes(pk) + this.abytes(pk) + let p: Point | undefined = undefined + const length = pk.length + const head = pk[0] + const tail = pk.subarray(1) + const x = this.bytesToBigint(tail.subarray(0, this.L)) + // No actual validation is done here until calling isValidPoint() + if (length === this.C && (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(BigInt(head)) + if (evenH !== evenY) y = this.M(-y) + p = this.Point(x, y, 1n) + } + // Validate point + return !!this.isValidPoint(p) } catch (error) { return false }