UNPKG

libnexa-ts

Version:

A pure and powerful Nexa SDK library.

161 lines (136 loc) 4.38 kB
import BN from "../crypto/bn.extension"; import type { curve, BNInput } from 'elliptic'; import elliptic from 'elliptic'; import BufferUtils from "../utils/buffer.utils"; const EC = elliptic.ec; export default class Point { private static readonly ec = new EC('secp256k1').curve as curve.short; public ecPoint: curve.short.ShortPoint; private static _g: Point = new Point(this.ec.g); constructor(point: curve.short.ShortPoint, skipValidation = false) { this.ecPoint = point; if (!skipValidation) { this.validate(); } } /** * Will return the X coordinate of the Point * * @returns A BN instance of the X coordinate */ public getX(): BN { return new BN(this.ecPoint.getX().toArray()); } /** * Will return the Y coordinate of the Point * * @returns A BN instance of the Y coordinate */ public getY(): BN { return new BN(this.ecPoint.getY().toArray()); } public add(p: Point): Point { return new Point(this.ecPoint.add(p.ecPoint) as curve.short.ShortPoint, true); } public mul(k: BN): Point { let p = this.ecPoint.mul(k); return new Point(p as curve.short.ShortPoint, true); } public mulAdd(k1: BN, p2: Point, k2: BN): Point { let p = (this.ecPoint as any).mulAdd(k1, p2.ecPoint, k2); // eslint-disable-line @typescript-eslint/no-explicit-any return new Point(p as curve.short.ShortPoint, true); } public eq(p: Point): boolean { return this.ecPoint.eq(p.ecPoint); } /** * Will determine if the point is valid * * @see {@link https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf} * @throws A validation error if exists * @returns An instance of the same Point */ public validate(): this { if (this.ecPoint.isInfinity()){ throw new Error('Point cannot be equal to Infinity'); } let p2; try { p2 = Point.ec.pointFromX(this.getX(), this.getY().isOdd()); } catch { throw new Error('Point does not lie on the curve'); } if (p2.y!.cmp(this.ecPoint.y!) !== 0) { throw new Error('Invalid y value for curve.'); } if (!(this.ecPoint.mul(Point.getN()).isInfinity())) { throw new Error('Point times N must be infinity'); } return this; } public hasSquare(): boolean { return !this.ecPoint.isInfinity() && Point.isSquare(this.getY()); } private static isSquare(x: BN): boolean { let p = new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 'hex'); let x0 = new BN(x); let base = x0.toRed(BN.red(p)); let res = base.redPow(p.sub(BN.One).div(new BN(2))).fromRed(); return res.eq(new BN(1)); } /** * Instantiate a valid secp256k1 Point from the X and Y coordinates. * * @param x - The X coordinate * @param y - The Y coordinate * @see {@link https://github.com/indutny/elliptic} * @throws A validation error if exists */ public static ecPoint(x: BNInput, y: BNInput, isRed?: boolean): Point { return new Point(this.ec.point(x, y, isRed)); } /** * * Instantiate a valid secp256k1 Point from only the X coordinate * * @param odd - If the Y coordinate is odd * @param x - The X coordinate * @throws A validation error if exists */ public static ecPointFromX(odd: boolean, x: BNInput): Point { let point; try { point = this.ec.pointFromX(x, odd); } catch { throw new Error('Invalid X'); } return new Point(point); } /** * * Will return a secp256k1 ECDSA base point. * * @see {@link https://en.bitcoin.it/wiki/Secp256k1} * @returns An instance of the base point. */ public static getG(): Point { return this._g; }; /** * * Will return the max of range of valid private keys as governed by the secp256k1 ECDSA standard. * * @see {@link https://en.bitcoin.it/wiki/Private_key#Range_of_valid_ECDSA_private_keys} * @returns A BN instance of the number of points on the curve */ public static getN(): BN { return new BN(this.ec.n.toArray()); }; public static pointToCompressed(point: Point): Uint8Array { let xbuf = point.getX().toByteArray({size: 32}); let ybuf = point.getY().toByteArray({size: 32}); let odd = ybuf[ybuf.length - 1] % 2; let prefix = Uint8Array.from(odd ? [0x03] : [0x02]); return BufferUtils.concat([prefix, xbuf]); }; }