libnexa-ts
Version:
A pure and powerful Nexa SDK library.
161 lines (136 loc) • 4.38 kB
text/typescript
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]);
};
}