Utilize complete formulas for weierstrass curves

This commit is contained in:
Paul Miller 2022-12-27 00:27:09 +00:00
parent 7a34c16c2b
commit 135e69bd7b
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
9 changed files with 194 additions and 172 deletions

@ -259,7 +259,7 @@ export type CurveFn = {
signature: Hex | SignatureType, msgHash: Hex, publicKey: PubKey, opts?: {lowS?: boolean;} signature: Hex | SignatureType, msgHash: Hex, publicKey: PubKey, opts?: {lowS?: boolean;}
) => boolean; ) => boolean;
Point: PointConstructor; Point: PointConstructor;
JacobianPoint: JacobianPointConstructor; ProjectivePoint: ProjectivePointConstructor;
Signature: SignatureConstructor; Signature: SignatureConstructor;
utils: { utils: {
mod: (a: bigint, b?: bigint) => bigint; mod: (a: bigint, b?: bigint) => bigint;

@ -9,7 +9,7 @@
"module": "lib/index.js", "module": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
"dependencies": { "dependencies": {
"@noble/curves": "0.4.0", "@noble/curves": "file:..",
"@noble/hashes": "1.1.5" "@noble/hashes": "1.1.5"
}, },
"devDependencies": { "devDependencies": {

@ -14,7 +14,7 @@ import {
bitMask, bitMask,
} from '@noble/curves/utils'; } from '@noble/curves/utils';
// Types // Types
import { PointType, JacobianPointType, JacobianConstructor } from '@noble/curves/weierstrass'; import { PointType, ProjectivePointType, ProjectiveConstructor } from '@noble/curves/weierstrass';
// Differences from bls12-381: // Differences from bls12-381:
// - PointG1 -> G1.Point // - PointG1 -> G1.Point
@ -1010,7 +1010,7 @@ function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
return [x2, y2]; return [x2, y2];
} }
// Ψ endomorphism // Ψ endomorphism
function G2psi(c: JacobianConstructor<Fp2>, P: JacobianPointType<Fp2>) { function G2psi(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
const affine = P.toAffine(); const affine = P.toAffine();
const p = psi(affine.x, affine.y); const p = psi(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE); return new c(p[0], p[1], Fp2.ONE);
@ -1023,7 +1023,7 @@ const PSI2_C1 =
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] { function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
return [Fp2.multiply(x, PSI2_C1), Fp2.negate(y)]; return [Fp2.multiply(x, PSI2_C1), Fp2.negate(y)];
} }
function G2psi2(c: JacobianConstructor<Fp2>, P: JacobianPointType<Fp2>) { function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
const affine = P.toAffine(); const affine = P.toAffine();
const p = psi2(affine.x, affine.y); const p = psi2(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE); return new c(p[0], p[1], Fp2.ONE);

@ -18,7 +18,7 @@ import { randomBytes } from '@noble/hashes/utils';
* efficiently computable Frobenius endomorphism. * efficiently computable Frobenius endomorphism.
* Endomorphism improves efficiency: * Endomorphism improves efficiency:
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%. * Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
* Should always be used for Jacobian's double-and-add multiplication. * Should always be used for Projective's double-and-add multiplication.
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit. * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 * https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
*/ */

@ -3,12 +3,12 @@ import { keccak_256 } from '@noble/hashes/sha3';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { hmac } from '@noble/hashes/hmac'; import { hmac } from '@noble/hashes/hmac';
import { concatBytes, randomBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes } from '@noble/hashes/utils';
import { weierstrass, JacobianPointType } from '@noble/curves/weierstrass'; import { weierstrass, ProjectivePointType } from '@noble/curves/weierstrass';
import * as cutils from '@noble/curves/utils'; import * as cutils from '@noble/curves/utils';
import { Fp } from '@noble/curves/modular'; import { Fp } from '@noble/curves/modular';
import { getHash } from './_shortw_utils.js'; import { getHash } from './_shortw_utils.js';
type JacobianPoint = JacobianPointType<bigint>; type ProjectivePoint = ProjectivePointType<bigint>;
// Stark-friendly elliptic curve // Stark-friendly elliptic curve
// https://docs.starkware.co/starkex/stark-curve.html // https://docs.starkware.co/starkex/stark-curve.html
@ -108,13 +108,13 @@ function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey)); return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
} }
const { CURVE, Point, JacobianPoint, Signature } = starkCurve; const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
export const utils = starkCurve.utils; export const utils = starkCurve.utils;
export { export {
CURVE, CURVE,
Point, Point,
Signature, Signature,
JacobianPoint, ProjectivePoint,
getPublicKey0x as getPublicKey, getPublicKey0x as getPublicKey,
getSharedSecret0x as getSharedSecret, getSharedSecret0x as getSharedSecret,
sign0x as sign, sign0x as sign,
@ -173,7 +173,7 @@ export function getAccountPath(
} }
// https://docs.starkware.co/starkex/pedersen-hash-function.html // https://docs.starkware.co/starkex/pedersen-hash-function.html
const PEDERSEN_POINTS = [ const PEDERSEN_POINTS_AFFINE = [
new Point( new Point(
2089986280348253421170679821480865132823066470938446095505822317253594081284n, 2089986280348253421170679821480865132823066470938446095505822317253594081284n,
1713931329540660377023406109199410414810705867260802078187082345529207694986n 1713931329540660377023406109199410414810705867260802078187082345529207694986n
@ -196,10 +196,10 @@ const PEDERSEN_POINTS = [
), ),
]; ];
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8); // for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
const PEDERSEN_POINTS_JACOBIAN = PEDERSEN_POINTS.map(JacobianPoint.fromAffine); const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
function pedersenPrecompute(p1: JacobianPoint, p2: JacobianPoint): JacobianPoint[] { function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
const out: JacobianPoint[] = []; const out: ProjectivePoint[] = [];
let p = p1; let p = p1;
for (let i = 0; i < 248; i++) { for (let i = 0; i < 248; i++) {
out.push(p); out.push(p);
@ -212,14 +212,8 @@ function pedersenPrecompute(p1: JacobianPoint, p2: JacobianPoint): JacobianPoint
} }
return out; return out;
} }
const PEDERSEN_POINTS1 = pedersenPrecompute( const PEDERSEN_POINTS1 = pedersenPrecompute(PEDERSEN_POINTS[1], PEDERSEN_POINTS[2]);
PEDERSEN_POINTS_JACOBIAN[1], const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[4]);
PEDERSEN_POINTS_JACOBIAN[2]
);
const PEDERSEN_POINTS2 = pedersenPrecompute(
PEDERSEN_POINTS_JACOBIAN[3],
PEDERSEN_POINTS_JACOBIAN[4]
);
type PedersenArg = Hex | bigint | number; type PedersenArg = Hex | bigint | number;
function pedersenArg(arg: PedersenArg): bigint { function pedersenArg(arg: PedersenArg): bigint {
@ -235,7 +229,7 @@ function pedersenArg(arg: PedersenArg): bigint {
return value; return value;
} }
function pedersenSingle(point: JacobianPoint, value: PedersenArg, constants: JacobianPoint[]) { function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: ProjectivePoint[]) {
let x = pedersenArg(value); let x = pedersenArg(value);
for (let j = 0; j < 252; j++) { for (let j = 0; j < 252; j++) {
const pt = constants[j]; const pt = constants[j];
@ -248,7 +242,7 @@ function pedersenSingle(point: JacobianPoint, value: PedersenArg, constants: Jac
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3 // shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
export function pedersen(x: PedersenArg, y: PedersenArg) { export function pedersen(x: PedersenArg, y: PedersenArg) {
let point: JacobianPoint = PEDERSEN_POINTS_JACOBIAN[0]; let point: ProjectivePoint = PEDERSEN_POINTS[0];
point = pedersenSingle(point, x, PEDERSEN_POINTS1); point = pedersenSingle(point, x, PEDERSEN_POINTS1);
point = pedersenSingle(point, y, PEDERSEN_POINTS2); point = pedersenSingle(point, y, PEDERSEN_POINTS2);
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1)); return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));

@ -51,7 +51,7 @@ for (const name in CURVES) {
const O = name === 'secp256k1' ? secp256r1 : secp256k1; const O = name === 'secp256k1' ? secp256r1 : secp256k1;
const POINTS = {}; const POINTS = {};
const OTHER_POINTS = {}; const OTHER_POINTS = {};
for (const name of ['Point', 'JacobianPoint', 'ExtendedPoint', 'ProjectivePoint']) { for (const name of ['Point', 'ProjectivePoint', 'ExtendedPoint', 'ProjectivePoint']) {
POINTS[name] = C[name]; POINTS[name] = C[name];
OTHER_POINTS[name] = O[name]; OTHER_POINTS[name] = O[name];
} }

@ -164,15 +164,15 @@ should('secp256k1.Point#multiply(privateKey)', () => {
}); });
// multiply() should equal multiplyUnsafe() // multiply() should equal multiplyUnsafe()
// should('JacobianPoint#multiplyUnsafe', () => { // should('ProjectivePoint#multiplyUnsafe', () => {
// const p0 = new secp.JacobianPoint( // const p0 = new secp.ProjectivePoint(
// 55066263022277343669578718895168534326250603453777594175500187360389116729240n, // 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
// 32670510020758816978083085130507043184471273380659243275938904335757337482424n, // 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
// 1n // 1n
// ); // );
// const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n; // const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n;
// console.log(p0.multiply(z)); // console.log(p0.multiply(z));
// console.log(secp.JacobianPoint.normalizeZ([p0.multiplyUnsafe(z)])[0]) // console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
// }); // });
should('secp256k1.Signature.fromCompactHex() roundtrip', () => { should('secp256k1.Signature.fromCompactHex() roundtrip', () => {

@ -337,7 +337,7 @@ export function bls<Fp2, Fp6, Fp12>(
if (!publicKeys.length) throw new Error('Expected non-empty array'); if (!publicKeys.length) throw new Error('Expected non-empty array');
const agg = publicKeys const agg = publicKeys
.map(normP1) .map(normP1)
.reduce((sum, p) => sum.add(G1.JacobianPoint.fromAffine(p)), G1.JacobianPoint.ZERO); .reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
const aggAffine = agg.toAffine(); const aggAffine = agg.toAffine();
if (publicKeys[0] instanceof G1.Point) { if (publicKeys[0] instanceof G1.Point) {
aggAffine.assertValidity(); aggAffine.assertValidity();
@ -354,7 +354,7 @@ export function bls<Fp2, Fp6, Fp12>(
if (!signatures.length) throw new Error('Expected non-empty array'); if (!signatures.length) throw new Error('Expected non-empty array');
const agg = signatures const agg = signatures
.map(normP2) .map(normP2)
.reduce((sum, s) => sum.add(G2.JacobianPoint.fromAffine(s)), G2.JacobianPoint.ZERO); .reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
const aggAffine = agg.toAffine(); const aggAffine = agg.toAffine();
if (signatures[0] instanceof G2.Point) { if (signatures[0] instanceof G2.Point) {
aggAffine.assertValidity(); aggAffine.assertValidity();

@ -41,8 +41,11 @@ export type BasicCurve<T> = utils.BasicCurve<T> & {
// Endomorphism options for Koblitz curves // Endomorphism options for Koblitz curves
endo?: EndomorphismOpts; endo?: EndomorphismOpts;
// Torsions, can be optimized via endomorphisms // Torsions, can be optimized via endomorphisms
isTorsionFree?: (c: JacobianConstructor<T>, point: JacobianPointType<T>) => boolean; isTorsionFree?: (c: ProjectiveConstructor<T>, point: ProjectivePointType<T>) => boolean;
clearCofactor?: (c: JacobianConstructor<T>, point: JacobianPointType<T>) => JacobianPointType<T>; clearCofactor?: (
c: ProjectiveConstructor<T>,
point: ProjectivePointType<T>
) => ProjectivePointType<T>;
// Hash to field opts // Hash to field opts
htfDefaults?: htfOpts; htfDefaults?: htfOpts;
mapToCurve?: (scalar: bigint[]) => { x: T; y: T }; mapToCurve?: (scalar: bigint[]) => { x: T; y: T };
@ -94,9 +97,7 @@ function parseDERSignature(data: Uint8Array) {
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
const _0n = BigInt(0); const _0n = BigInt(0);
const _1n = BigInt(1); const _1n = BigInt(1);
const _2n = BigInt(2);
const _3n = BigInt(3); const _3n = BigInt(3);
const _8n = BigInt(8);
type Entropy = Hex | true; type Entropy = Hex | true;
type SignOpts = { lowS?: boolean; extraEntropy?: Entropy }; type SignOpts = { lowS?: boolean; extraEntropy?: Entropy };
@ -124,20 +125,20 @@ type SignOpts = { lowS?: boolean; extraEntropy?: Entropy };
*/ */
// Instance // Instance
export interface JacobianPointType<T> extends Group<JacobianPointType<T>> { export interface ProjectivePointType<T> extends Group<ProjectivePointType<T>> {
readonly x: T; readonly x: T;
readonly y: T; readonly y: T;
readonly z: T; readonly z: T;
multiply(scalar: number | bigint, affinePoint?: PointType<T>): JacobianPointType<T>; multiply(scalar: number | bigint, affinePoint?: PointType<T>): ProjectivePointType<T>;
multiplyUnsafe(scalar: bigint): JacobianPointType<T>; multiplyUnsafe(scalar: bigint): ProjectivePointType<T>;
toAffine(invZ?: T): PointType<T>; toAffine(invZ?: T): PointType<T>;
} }
// Static methods // Static methods
export interface JacobianConstructor<T> extends GroupConstructor<JacobianPointType<T>> { export interface ProjectiveConstructor<T> extends GroupConstructor<ProjectivePointType<T>> {
new (x: T, y: T, z: T): JacobianPointType<T>; new (x: T, y: T, z: T): ProjectivePointType<T>;
fromAffine(p: PointType<T>): JacobianPointType<T>; fromAffine(p: PointType<T>): ProjectivePointType<T>;
toAffineBatch(points: JacobianPointType<T>[]): PointType<T>[]; toAffineBatch(points: ProjectivePointType<T>[]): PointType<T>[];
normalizeZ(points: JacobianPointType<T>[]): JacobianPointType<T>[]; normalizeZ(points: ProjectivePointType<T>[]): ProjectivePointType<T>[];
} }
// Instance // Instance
export interface PointType<T> extends Group<PointType<T>> { export interface PointType<T> extends Group<PointType<T>> {
@ -199,7 +200,7 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
export type CurvePointsRes<T> = { export type CurvePointsRes<T> = {
Point: PointConstructor<T>; Point: PointConstructor<T>;
JacobianPoint: JacobianConstructor<T>; ProjectivePoint: ProjectiveConstructor<T>;
normalizePrivateKey: (key: PrivKey) => bigint; normalizePrivateKey: (key: PrivKey) => bigint;
weierstrassEquation: (x: T) => T; weierstrassEquation: (x: T) => T;
isWithinCurveOrder: (num: bigint) => boolean; isWithinCurveOrder: (num: bigint) => boolean;
@ -267,130 +268,160 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
} }
/** /**
* Jacobian Point works in 3d / jacobi coordinates: (x, y, z) (x=x/z², y=y/z³) * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) (x=x/z, y=y/z)
* Default Point works in 2d / affine coordinates: (x, y) * Default Point works in 2d / affine coordinates: (x, y)
* We're doing calculations in jacobi, because its operations don't require costly inversion. * We're doing calculations in projective, because its operations don't require costly inversion.
*/ */
class JacobianPoint implements JacobianPointType<T> { class ProjectivePoint implements ProjectivePointType<T> {
constructor(readonly x: T, readonly y: T, readonly z: T) {} constructor(readonly x: T, readonly y: T, readonly z: T) {}
static readonly BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, Fp.ONE); static readonly BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
static readonly ZERO = new JacobianPoint(Fp.ZERO, Fp.ONE, Fp.ZERO); static readonly ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
static fromAffine(p: Point): JacobianPoint { static fromAffine(p: Point): ProjectivePoint {
if (!(p instanceof Point)) { if (!(p instanceof Point)) {
throw new TypeError('JacobianPoint#fromAffine: expected Point'); throw new TypeError('ProjectivePoint#fromAffine: expected Point');
} }
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0) // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
if (p.equals(Point.ZERO)) return JacobianPoint.ZERO; if (p.equals(Point.ZERO)) return ProjectivePoint.ZERO;
return new JacobianPoint(p.x, p.y, Fp.ONE); return new ProjectivePoint(p.x, p.y, Fp.ONE);
} }
/** /**
* Takes a bunch of Jacobian Points but executes only one * Takes a bunch of Projective Points but executes only one
* invert on all of them. invert is very slow operation, * invert on all of them. invert is very slow operation,
* so this improves performance massively. * so this improves performance massively.
*/ */
static toAffineBatch(points: JacobianPoint[]): Point[] { static toAffineBatch(points: ProjectivePoint[]): Point[] {
const toInv = Fp.invertBatch(points.map((p) => p.z)); const toInv = Fp.invertBatch(points.map((p) => p.z));
return points.map((p, i) => p.toAffine(toInv[i])); return points.map((p, i) => p.toAffine(toInv[i]));
} }
static normalizeZ(points: JacobianPoint[]): JacobianPoint[] { static normalizeZ(points: ProjectivePoint[]): ProjectivePoint[] {
return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine); return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine);
} }
/** /**
* Compare one point to another. * Compare one point to another.
*/ */
equals(other: JacobianPoint): boolean { equals(other: ProjectivePoint): boolean {
if (!(other instanceof JacobianPoint)) throw new TypeError('JacobianPoint expected'); if (!(other instanceof ProjectivePoint)) throw new TypeError('ProjectivePoint expected');
const { x: X1, y: Y1, z: Z1 } = this; const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other; const { x: X2, y: Y2, z: Z2 } = other;
const Z1Z1 = Fp.square(Z1); // Z1 * Z1 const U1 = Fp.equals(Fp.multiply(X1, Z2), Fp.multiply(X2, Z1));
const Z2Z2 = Fp.square(Z2); // Z2 * Z2 const U2 = Fp.equals(Fp.multiply(Y1, Z2), Fp.multiply(Y2, Z1));
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2 return U1 && U2;
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1
return Fp.equals(U1, U2) && Fp.equals(S1, S2);
} }
/** /**
* Flips point to one corresponding to (x, -y) in Affine coordinates. * Flips point to one corresponding to (x, -y) in Affine coordinates.
*/ */
negate(): JacobianPoint { negate(): ProjectivePoint {
return new JacobianPoint(this.x, Fp.negate(this.y), this.z); return new ProjectivePoint(this.x, Fp.negate(this.y), this.z);
} }
// Fast algo for doubling 2 Jacobian Points. doubleAdd(): ProjectivePoint {
// From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl return this.add(this);
// Cost: 1M + 8S + 1*a + 10add + 2*2 + 1*3 + 1*8. }
double(): JacobianPoint {
// Renes-Costello-Batina exception-free doubling formula.
// There is 30% faster Jacobian formula, but it is not complete.
// https://eprint.iacr.org/2015/1060, algorithm 3
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
double() {
const { a, b } = CURVE;
const b3 = Fp.multiply(b, 3n);
const { x: X1, y: Y1, z: Z1 } = this; const { x: X1, y: Y1, z: Z1 } = this;
const { a } = CURVE; let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
// Faster algorithm: when a=0 let t0 = Fp.multiply(X1, X1); // step 1
// From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l let t1 = Fp.multiply(Y1, Y1);
// Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8. let t2 = Fp.multiply(Z1, Z1);
if (Fp.isZero(a)) { let t3 = Fp.multiply(X1, Y1);
const A = Fp.square(X1); // X1 * X1 t3 = Fp.add(t3, t3); // step 5
const B = Fp.square(Y1); // Y1 * Y1 Z3 = Fp.multiply(X1, Z1);
const C = Fp.square(B); // B * B Z3 = Fp.add(Z3, Z3);
const x1b = Fp.addN(X1, B); // X1 + B X3 = Fp.multiply(a, Z3);
const D = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(x1b), A), C), _2n); // ((x1b * x1b) - A - C) * 2 Y3 = Fp.multiply(b3, t2);
const E = Fp.multiply(A, _3n); // A * 3 Y3 = Fp.add(X3, Y3); // step 10
const F = Fp.square(E); // E * E X3 = Fp.subtract(t1, Y3);
const X3 = Fp.subtract(F, Fp.multiplyN(D, _2n)); // F - 2 * D Y3 = Fp.add(t1, Y3);
const Y3 = Fp.subtract(Fp.multiplyN(E, Fp.subtractN(D, X3)), Fp.multiplyN(C, _8n)); // E * (D - X3) - 8 * C; Y3 = Fp.multiply(X3, Y3);
const Z3 = Fp.multiply(Fp.multiplyN(Y1, _2n), Z1); // 2 * Y1 * Z1 X3 = Fp.multiply(t3, X3);
return new JacobianPoint(X3, Y3, Z3); Z3 = Fp.multiply(b3, Z3); // step 15
} t2 = Fp.multiply(a, t2);
const XX = Fp.square(X1); // X1 * X1 t3 = Fp.subtract(t0, t2);
const YY = Fp.square(Y1); // Y1 * Y1 t3 = Fp.multiply(a, t3);
const YYYY = Fp.square(YY); // YY * YY t3 = Fp.add(t3, Z3);
const ZZ = Fp.square(Z1); // Z1 * Z1 Z3 = Fp.add(t0, t0); // step 20
const tmp1 = Fp.add(X1, YY); // X1 + YY t0 = Fp.add(Z3, t0);
const S = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(tmp1), XX), YYYY), _2n); // 2*((X1+YY)^2-XX-YYYY) t0 = Fp.add(t0, t2);
const M = Fp.add(Fp.multiplyN(XX, _3n), Fp.multiplyN(Fp.square(ZZ), a)); // 3 * XX + a * ZZ^2 t0 = Fp.multiply(t0, t3);
const T = Fp.subtract(Fp.square(M), Fp.multiplyN(S, _2n)); // M^2-2*S Y3 = Fp.add(Y3, t0);
const X3 = T; t2 = Fp.multiply(Y1, Z1); // step 25
const Y3 = Fp.subtract(Fp.multiplyN(M, Fp.subtractN(S, T)), Fp.multiplyN(YYYY, _8n)); // M*(S-T)-8*YYYY t2 = Fp.add(t2, t2);
const y1az1 = Fp.add(Y1, Z1); // (Y1+Z1) t0 = Fp.multiply(t2, t3);
const Z3 = Fp.subtract(Fp.subtractN(Fp.square(y1az1), YY), ZZ); // (Y1+Z1)^2-YY-ZZ X3 = Fp.subtract(X3, t0);
return new JacobianPoint(X3, Y3, Z3); Z3 = Fp.multiply(t2, t1);
Z3 = Fp.add(Z3, Z3); // step 30
Z3 = Fp.add(Z3, Z3);
return new ProjectivePoint(X3, Y3, Z3);
} }
// Fast algo for adding 2 Jacobian Points. // Renes-Costello-Batina exception-free addition formula.
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-1998-cmo-2 // There is 30% faster Jacobian formula, but it is not complete.
// Cost: 12M + 4S + 6add + 1*2 // https://eprint.iacr.org/2015/1060, algorithm 1
// Note: 2007 Bernstein-Lange (11M + 5S + 9add + 4*2) is actually 10% slower. // Cost: 12M + 0S + 3*a + 3*b3 + 23add.
add(other: JacobianPoint): JacobianPoint { add(other: ProjectivePoint): ProjectivePoint {
if (!(other instanceof JacobianPoint)) throw new TypeError('JacobianPoint expected'); if (!(other instanceof ProjectivePoint)) throw new TypeError('ProjectivePoint expected');
const { x: X1, y: Y1, z: Z1 } = this; const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other; const { x: X2, y: Y2, z: Z2 } = other;
if (Fp.isZero(X2) || Fp.isZero(Y2)) return this; let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
if (Fp.isZero(X1) || Fp.isZero(Y1)) return other; const a = CURVE.a;
// We're using same code in equals() const b3 = Fp.multiply(CURVE.b, 3n);
const Z1Z1 = Fp.square(Z1); // Z1Z1 = Z1^2 let t0 = Fp.multiply(X1, X2); // step 1
const Z2Z2 = Fp.square(Z2); // Z2Z2 = Z2^2; let t1 = Fp.multiply(Y1, Y2);
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2 let t2 = Fp.multiply(Z1, Z2);
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1 let t3 = Fp.add(X1, Y1);
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2 let t4 = Fp.add(X2, Y2); // step 5
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1 t3 = Fp.multiply(t3, t4);
const H = Fp.subtractN(U2, U1); // H = U2 - U1 t4 = Fp.add(t0, t1);
const r = Fp.subtractN(S2, S1); // S2 - S1 t3 = Fp.subtract(t3, t4);
// H = 0 meaning it's the same point. t4 = Fp.add(X1, Z1);
if (Fp.isZero(H)) return Fp.isZero(r) ? this.double() : JacobianPoint.ZERO; let t5 = Fp.add(X2, Z2); // step 10
const HH = Fp.square(H); // HH = H2 t4 = Fp.multiply(t4, t5);
const HHH = Fp.multiply(H, HH); // HHH = H * HH t5 = Fp.add(t0, t2);
const V = Fp.multiply(U1, HH); // V = U1 * HH t4 = Fp.subtract(t4, t5);
const X3 = Fp.subtract(Fp.subtractN(Fp.squareN(r), HHH), Fp.multiplyN(V, _2n)); // X3 = r^2 - HHH - 2 * V; t5 = Fp.add(Y1, Z1);
const Y3 = Fp.subtract(Fp.multiplyN(r, Fp.subtractN(V, X3)), Fp.multiplyN(S1, HHH)); // Y3 = r * (V - X3) - S1 * HHH; X3 = Fp.add(Y2, Z2); // step 15
const Z3 = Fp.multiply(Fp.multiply(Z1, Z2), H); // Z3 = Z1 * Z2 * H; t5 = Fp.multiply(t5, X3);
return new JacobianPoint(X3, Y3, Z3); X3 = Fp.add(t1, t2);
t5 = Fp.subtract(t5, X3);
Z3 = Fp.multiply(a, t4);
X3 = Fp.multiply(b3, t2); // step 20
Z3 = Fp.add(X3, Z3);
X3 = Fp.subtract(t1, Z3);
Z3 = Fp.add(t1, Z3);
Y3 = Fp.multiply(X3, Z3);
t1 = Fp.add(t0, t0); // step 25
t1 = Fp.add(t1, t0);
t2 = Fp.multiply(a, t2);
t4 = Fp.multiply(b3, t4);
t1 = Fp.add(t1, t2);
t2 = Fp.subtract(t0, t2); // step 30
t2 = Fp.multiply(a, t2);
t4 = Fp.add(t4, t2);
t0 = Fp.multiply(t1, t4);
Y3 = Fp.add(Y3, t0);
t0 = Fp.multiply(t5, t4); // step 35
X3 = Fp.multiply(t3, X3);
X3 = Fp.subtract(X3, t0);
t0 = Fp.multiply(t3, t1);
Z3 = Fp.multiply(t5, Z3);
Z3 = Fp.add(Z3, t0); // step 40
return new ProjectivePoint(X3, Y3, Z3);
} }
subtract(other: JacobianPoint) { subtract(other: ProjectivePoint) {
return this.add(other.negate()); return this.add(other.negate());
} }
@ -399,8 +430,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
* It's faster, but should only be used when you don't care about * It's faster, but should only be used when you don't care about
* an exposed private key e.g. sig verification, which works over *public* keys. * an exposed private key e.g. sig verification, which works over *public* keys.
*/ */
multiplyUnsafe(scalar: bigint): JacobianPoint { multiplyUnsafe(scalar: bigint): ProjectivePoint {
const P0 = JacobianPoint.ZERO; const P0 = ProjectivePoint.ZERO;
if (typeof scalar === 'bigint' && scalar === _0n) return P0; if (typeof scalar === 'bigint' && scalar === _0n) return P0;
// Will throw on 0 // Will throw on 0
let n = normalizeScalar(scalar); let n = normalizeScalar(scalar);
@ -412,7 +443,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
let { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n); let { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
let k1p = P0; let k1p = P0;
let k2p = P0; let k2p = P0;
let d: JacobianPoint = this; let d: ProjectivePoint = this;
while (k1 > _0n || k2 > _0n) { while (k1 > _0n || k2 > _0n) {
if (k1 & _1n) k1p = k1p.add(d); if (k1 & _1n) k1p = k1p.add(d);
if (k2 & _1n) k2p = k2p.add(d); if (k2 & _1n) k2p = k2p.add(d);
@ -422,22 +453,22 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
} }
if (k1neg) k1p = k1p.negate(); if (k1neg) k1p = k1p.negate();
if (k2neg) k2p = k2p.negate(); if (k2neg) k2p = k2p.negate();
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z); k2p = new ProjectivePoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
return k1p.add(k2p); return k1p.add(k2p);
} }
/** /**
* Implements w-ary non-adjacent form for calculating ec multiplication. * Implements w-ary non-adjacent form for calculating ec multiplication.
*/ */
private wNAF(n: bigint, affinePoint?: Point): { p: JacobianPoint; f: JacobianPoint } { private wNAF(n: bigint, affinePoint?: Point): { p: ProjectivePoint; f: ProjectivePoint } {
if (!affinePoint && this.equals(JacobianPoint.BASE)) affinePoint = Point.BASE; if (!affinePoint && this.equals(ProjectivePoint.BASE)) affinePoint = Point.BASE;
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1; const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
// Calculate precomputes on a first run, reuse them after // Calculate precomputes on a first run, reuse them after
let precomputes = affinePoint && pointPrecomputes.get(affinePoint); let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
if (!precomputes) { if (!precomputes) {
precomputes = wnaf.precomputeWindow(this, W) as JacobianPoint[]; precomputes = wnaf.precomputeWindow(this, W) as ProjectivePoint[];
if (affinePoint && W !== 1) { if (affinePoint && W !== 1) {
precomputes = JacobianPoint.normalizeZ(precomputes); precomputes = ProjectivePoint.normalizeZ(precomputes);
pointPrecomputes.set(affinePoint, precomputes); pointPrecomputes.set(affinePoint, precomputes);
} }
} }
@ -452,20 +483,20 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
* @param affinePoint optional point ot save cached precompute windows on it * @param affinePoint optional point ot save cached precompute windows on it
* @returns New point * @returns New point
*/ */
multiply(scalar: number | bigint, affinePoint?: Point): JacobianPoint { multiply(scalar: number | bigint, affinePoint?: Point): ProjectivePoint {
let n = normalizeScalar(scalar); let n = normalizeScalar(scalar);
// Real point. // Real point.
let point: JacobianPoint; let point: ProjectivePoint;
// Fake point, we use it to achieve constant-time multiplication. // Fake point, we use it to achieve constant-time multiplication.
let fake: JacobianPoint; let fake: ProjectivePoint;
if (CURVE.endo) { if (CURVE.endo) {
const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n); const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint); let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint); let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
k1p = wnaf.constTimeNegate(k1neg, k1p); k1p = wnaf.constTimeNegate(k1neg, k1p);
k2p = wnaf.constTimeNegate(k2neg, k2p); k2p = wnaf.constTimeNegate(k2neg, k2p);
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z); k2p = new ProjectivePoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
point = k1p.add(k2p); point = k1p.add(k2p);
fake = f1p.add(f2p); fake = f1p.add(f2p);
} else { } else {
@ -474,46 +505,42 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
fake = f; fake = f;
} }
// Normalize `z` for both points, but return only real one // Normalize `z` for both points, but return only real one
return JacobianPoint.normalizeZ([point, fake])[0]; return ProjectivePoint.normalizeZ([point, fake])[0];
} }
// Converts Jacobian point to affine (x, y) coordinates. // Converts Projective point to affine (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch. // Can accept precomputed Z^-1 - for example, from invertBatch.
// (x, y, z) ∋ (x=x/z², y=y/z³) // (x, y, z) ∋ (x=x/z, y=y/z)
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#scaling-z
toAffine(invZ?: T): Point { toAffine(invZ?: T): Point {
const { x, y, z } = this; const { x, y, z } = this;
const is0 = this.equals(JacobianPoint.ZERO); const is0 = this.equals(ProjectivePoint.ZERO);
// If invZ was 0, we return zero point. However we still want to execute // If invZ was 0, we return zero point. However we still want to execute
// all operations, so we replace invZ with a random number, 1. // all operations, so we replace invZ with a random number, 1.
if (invZ == null) invZ = is0 ? Fp.ONE : Fp.invert(z); if (invZ == null) invZ = is0 ? Fp.ONE : Fp.invert(z);
const iz1 = invZ; const ax = Fp.multiply(x, invZ);
const iz2 = Fp.square(iz1); // iz1 * iz1 const ay = Fp.multiply(y, invZ);
const iz3 = Fp.multiply(iz2, iz1); // iz2 * iz1 const zz = Fp.multiply(z, invZ);
const ax = Fp.multiply(x, iz2); // x * iz2
const ay = Fp.multiply(y, iz3); // y * iz3
const zz = Fp.multiply(z, iz1); // z * iz1
if (is0) return Point.ZERO; if (is0) return Point.ZERO;
if (!Fp.equals(zz, Fp.ONE)) throw new Error('invZ was invalid'); if (!Fp.equals(zz, Fp.ONE)) throw new Error('invZ was invalid');
return new Point(ax, ay); return new Point(ax, ay);
} }
isTorsionFree(): boolean { isTorsionFree(): boolean {
if (CURVE.h === _1n) return true; // No subgroups, always torsion fee if (CURVE.h === _1n) return true; // No subgroups, always torsion fee
if (CURVE.isTorsionFree) return CURVE.isTorsionFree(JacobianPoint, this); if (CURVE.isTorsionFree) return CURVE.isTorsionFree(ProjectivePoint, this);
// is multiplyUnsafe(CURVE.n) is always ok, same as for edwards? // is multiplyUnsafe(CURVE.n) is always ok, same as for edwards?
throw new Error('Unsupported!'); throw new Error('Unsupported!');
} }
// Clear cofactor of G1 // Clear cofactor of G1
// https://eprint.iacr.org/2019/403 // https://eprint.iacr.org/2019/403
clearCofactor(): JacobianPoint { clearCofactor(): ProjectivePoint {
if (CURVE.h === _1n) return this; // Fast-path if (CURVE.h === _1n) return this; // Fast-path
if (CURVE.clearCofactor) return CURVE.clearCofactor(JacobianPoint, this) as JacobianPoint; if (CURVE.clearCofactor) return CURVE.clearCofactor(ProjectivePoint, this) as ProjectivePoint;
return this.multiplyUnsafe(CURVE.h); return this.multiplyUnsafe(CURVE.h);
} }
} }
const wnaf = wNAF(JacobianPoint, CURVE.endo ? nBitLength / 2 : nBitLength); const wnaf = wNAF(ProjectivePoint, CURVE.endo ? nBitLength / 2 : nBitLength);
// Stores precomputed values for points. // Stores precomputed values for points.
const pointPrecomputes = new WeakMap<Point, JacobianPoint[]>(); const pointPrecomputes = new WeakMap<Point, ProjectivePoint[]>();
/** /**
* Default Point works in default aka affine coordinates: (x, y) * Default Point works in default aka affine coordinates: (x, y)
@ -601,12 +628,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
// Adds point to itself // Adds point to itself
double() { double() {
return JacobianPoint.fromAffine(this).double().toAffine(); return ProjectivePoint.fromAffine(this).double().toAffine();
} }
// Adds point to other point // Adds point to other point
add(other: Point) { add(other: Point) {
return JacobianPoint.fromAffine(this).add(JacobianPoint.fromAffine(other)).toAffine(); return ProjectivePoint.fromAffine(this).add(ProjectivePoint.fromAffine(other)).toAffine();
} }
// Subtracts other point from the point // Subtracts other point from the point
@ -615,18 +642,18 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
} }
multiply(scalar: number | bigint) { multiply(scalar: number | bigint) {
return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine(); return ProjectivePoint.fromAffine(this).multiply(scalar, this).toAffine();
} }
multiplyUnsafe(scalar: bigint) { multiplyUnsafe(scalar: bigint) {
return JacobianPoint.fromAffine(this).multiplyUnsafe(scalar).toAffine(); return ProjectivePoint.fromAffine(this).multiplyUnsafe(scalar).toAffine();
} }
clearCofactor() { clearCofactor() {
return JacobianPoint.fromAffine(this).clearCofactor().toAffine(); return ProjectivePoint.fromAffine(this).clearCofactor().toAffine();
} }
isTorsionFree(): boolean { isTorsionFree(): boolean {
return JacobianPoint.fromAffine(this).isTorsionFree(); return ProjectivePoint.fromAffine(this).isTorsionFree();
} }
/** /**
@ -636,12 +663,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
* @returns non-zero affine point * @returns non-zero affine point
*/ */
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined { multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
const P = JacobianPoint.fromAffine(this); const P = ProjectivePoint.fromAffine(this);
const aP = const aP =
a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a); a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b); const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
const sum = aP.add(bQ); const sum = aP.add(bQ);
return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine(); return sum.equals(ProjectivePoint.ZERO) ? undefined : sum.toAffine();
} }
// Encodes byte string to elliptic curve // Encodes byte string to elliptic curve
@ -666,7 +693,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
} }
return { return {
Point: Point as PointConstructor<T>, Point: Point as PointConstructor<T>,
JacobianPoint: JacobianPoint as JacobianConstructor<T>, ProjectivePoint: ProjectivePoint as ProjectiveConstructor<T>,
normalizePrivateKey, normalizePrivateKey,
weierstrassEquation, weierstrassEquation,
isWithinCurveOrder, isWithinCurveOrder,
@ -732,7 +759,7 @@ export type CurveFn = {
} }
) => boolean; ) => boolean;
Point: PointConstructor<bigint>; Point: PointConstructor<bigint>;
JacobianPoint: JacobianConstructor<bigint>; ProjectivePoint: ProjectiveConstructor<bigint>;
Signature: SignatureConstructor; Signature: SignatureConstructor;
utils: { utils: {
mod: (a: bigint, b?: bigint) => bigint; mod: (a: bigint, b?: bigint) => bigint;
@ -812,7 +839,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
return _0n < num && num < Fp.ORDER; return _0n < num && num < Fp.ORDER;
} }
const { Point, JacobianPoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = const { Point, ProjectivePoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } =
weierstrassPoints({ weierstrassPoints({
...CURVE, ...CURVE,
toBytes(c, point, isCompressed: boolean): Uint8Array { toBytes(c, point, isCompressed: boolean): Uint8Array {
@ -1132,16 +1159,17 @@ export function weierstrass(curveDef: CurveType): CurveFn {
* @returns Signature with its point on curve Q OR undefined if params were invalid * @returns Signature with its point on curve Q OR undefined if params were invalid
*/ */
function kmdToSig(kBytes: Uint8Array, m: bigint, d: bigint, lowS = true): Signature | undefined { function kmdToSig(kBytes: Uint8Array, m: bigint, d: bigint, lowS = true): Signature | undefined {
const { n } = CURVE;
const k = truncateHash(kBytes, true); const k = truncateHash(kBytes, true);
if (!isWithinCurveOrder(k)) return; if (!isWithinCurveOrder(k)) return;
// Important: all mod() calls in the function must be done over `n` // Important: all mod() calls in the function must be done over `n`
const { n } = CURVE; const kinv = mod.invert(k, n);
const q = Point.BASE.multiply(k); const q = Point.BASE.multiply(k);
// r = x mod n // r = x mod n
const r = mod.mod(q.x, n); const r = mod.mod(q.x, n);
if (r === _0n) return; if (r === _0n) return;
// s = (1/k * (m + dr) mod n // s = (1/k * (m + dr) mod n
const s = mod.mod(mod.invert(k, n) * mod.mod(m + d * r, n), n); const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
if (s === _0n) return; if (s === _0n) return;
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
let normS = s; let normS = s;
@ -1221,7 +1249,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const u1 = mod.mod(h * sinv, n); const u1 = mod.mod(h * sinv, n);
const u2 = mod.mod(r * sinv, n); const u2 = mod.mod(r * sinv, n);
// Some implementations compare R.x in jacobian, without inversion. // Some implementations compare R.x in projective, without inversion.
// The speed-up is <5%, so we don't complicate the code. // The speed-up is <5%, so we don't complicate the code.
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2); const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
if (!R) return false; if (!R) return false;
@ -1235,7 +1263,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
sign, sign,
verify, verify,
Point, Point,
JacobianPoint, ProjectivePoint,
Signature, Signature,
utils, utils,
}; };