Socket
Socket
Sign inDemoInstall

@noble/curves

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@noble/curves - npm Package Compare versions

Comparing version 1.4.2 to 1.5.0

abstract/tower.d.ts

71

abstract/bls.d.ts
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { AffinePoint } from './curve.js';
import { IField } from './modular.js';

@@ -7,5 +6,8 @@ import { Hex, PrivKey, CHash } from './utils.js';

import { CurvePointsType, ProjPointType as ProjPointType, CurvePointsRes } from './weierstrass.js';
import type { Fp2, Fp6, Fp12, Fp2Bls, Fp12Bls } from './tower.js';
/**
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
* Implements BLS (Boneh-Lynn-Shacham) signatures.
* BLS != BLS.
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
* families of pairing-friendly curves.
* Consists of two curves: G1 and G2:

@@ -17,6 +19,8 @@ * - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.

* Pairing is used to aggregate and verify signatures.
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
* Some projects may prefer to swap this relation, it is not supported for now.
* There are two main ways to use it:
* 1. Fp for short private keys, Fp₂ for signatures
* 2. Fp for short signatures, Fp₂ for private keys
**/
type Fp = bigint;
export type TwistType = 'multiplicative' | 'divisive';
export type ShortSignatureCoder<Fp> = {

@@ -27,22 +31,8 @@ fromHex(hex: Hex): ProjPointType<Fp>;

};
export type SignatureCoder<Fp2> = {
fromHex(hex: Hex): ProjPointType<Fp2>;
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
export type SignatureCoder<Fp> = {
fromHex(hex: Hex): ProjPointType<Fp>;
toRawBytes(point: ProjPointType<Fp>): Uint8Array;
toHex(point: ProjPointType<Fp>): string;
};
type Fp2Bls<Fp, Fp2> = IField<Fp2> & {
reim: (num: Fp2) => {
re: Fp;
im: Fp;
};
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
type Fp12Bls<Fp2, Fp12> = IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
export type CurveType = {
G1: Omit<CurvePointsType<Fp>, 'n'> & {

@@ -61,9 +51,11 @@ ShortSignature: SignatureCoder<Fp>;

Fr: IField<bigint>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
};
params: {
x: bigint;
ateLoopSize: bigint;
xNegative: boolean;
r: bigint;
twistType: TwistType;
};

@@ -73,4 +65,11 @@ htfDefaults: HTFOpts;

randomBytes: (bytesLength?: number) => Uint8Array;
postPrecompute?: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2, pointAdd: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) => {
Rx: Fp2;
Ry: Fp2;
Rz: Fp2;
}) => void;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
type Precompute = PrecomputeSingle[];
export type CurveFn = {
getPublicKey: (privateKey: PrivKey) => Uint8Array;

@@ -101,4 +100,8 @@ getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;

};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
pairingBatch: (pairs: {
g1: ProjPointType<Fp>;
g2: ProjPointType<Fp2>;
}[], withFinalExponent?: boolean) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof createHasher<Fp>>;

@@ -109,3 +112,3 @@ G2: CurvePointsRes<Fp2> & ReturnType<typeof createHasher<Fp2>>;

params: {
x: bigint;
ateLoopSize: bigint;
r: bigint;

@@ -117,5 +120,5 @@ G1b: bigint;

Fp: IField<Fp>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
Fr: IField<bigint>;

@@ -125,7 +128,7 @@ };

randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
calcPairingPrecomputes: (p: ProjPointType<Fp2>) => Precompute;
};
};
export declare function bls<Fp2, Fp6, Fp12>(CURVE: CurveType<Fp, Fp2, Fp6, Fp12>): CurveFn<Fp, Fp2, Fp6, Fp12>;
export declare function bls(CURVE: CurveType): CurveFn;
export {};
//# sourceMappingURL=bls.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bls = bls;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
// TODO: import { AffinePoint } from './curve.js';
const modular_js_1 = require("./modular.js");

@@ -10,69 +13,146 @@ const utils_js_1 = require("./utils.js");

// prettier-ignore
const _2n = BigInt(2), _3n = BigInt(3);
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// Not used with BLS12-381 (no sequential `11` in X). Useful for other curves.
function NAfDecomposition(a) {
const res = [];
// a>1 because of marker bit
for (; a > _1n; a >>= _1n) {
if ((a & _1n) === _0n)
res.unshift(0);
else if ((a & _3n) === _3n) {
res.unshift(-1);
a += _1n;
}
else
res.unshift(1);
}
return res;
}
function bls(CURVE) {
// Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
const BLS_X_LEN = (0, utils_js_1.bitLen)(CURVE.params.x);
const BLS_X_IS_NEGATIVE = CURVE.params.xNegative;
const TWIST = CURVE.params.twistType;
// Point on G1 curve: (x, y)
const G1_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G1 });
const G1 = Object.assign(G1_, (0, hash_to_curve_js_1.createHasher)(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
}));
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(G2_, (0, hash_to_curve_js_1.createHasher)(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
}));
// Applies sparse multiplication as line function
let lineFunction;
if (TWIST === 'multiplicative') {
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul014(f, c0, Fp2.mul(c1, Px), Fp2.mul(c2, Py));
}
else if (TWIST === 'divisive') {
// NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
// precompute calculations.
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul034(f, Fp2.mul(c2, Py), Fp2.mul(c1, Px), c0);
}
else
throw new Error('bls: unknown twist type');
const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n));
function pointDouble(ell, Rx, Ry, Rz) {
const t0 = Fp2.sqr(Ry); // Ry²
const t1 = Fp2.sqr(Rz); // Rz²
const t2 = Fp2.mulByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
const t3 = Fp2.mul(t2, _3n); // 3 * T2
const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
const c1 = Fp2.mul(Fp2.sqr(Rx), _3n); // 3 * Rx²
const c2 = Fp2.neg(t4); // -T4 (-h)
ell.push([c0, c1, c2]);
Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
return { Rx, Ry, Rz };
}
function pointAdd(ell, Rx, Ry, Rz, Qx, Qy) {
// Addition
const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
const c2 = t1; // == Rx - Qx * Rz
ell.push([c0, c1, c2]);
const t2 = Fp2.sqr(t1); // T1²
const t3 = Fp2.mul(t2, t1); // T2 * T1
const t4 = Fp2.mul(t2, Rx); // T2 * Rx
const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
return { Rx, Ry, Rz };
}
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
function calcPairingPrecomputes(p) {
const { x, y } = p;
// pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
// add + double in windowed precomputes here, otherwise it would be single op (since X is static)
const ATE_NAF = NAfDecomposition(CURVE.params.ateLoopSize);
const calcPairingPrecomputes = (0, utils_js_1.memoized)((point) => {
const p = point;
const { x, y } = p.toAffine();
// prettier-ignore
const Qx = x, Qy = y, Qz = Fp2.ONE;
const Qx = x, Qy = y, negQy = Fp2.neg(y);
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Qz;
let ell_coeff = [];
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
// Double
let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, _3n); // 3 * T2
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([
Fp2.sub(t2, t0), // T2 - T0
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
Fp2.neg(t4), // -T4
]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
if ((0, utils_js_1.bitGet)(CURVE.params.x, i)) {
// Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
ell_coeff.push([
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
Fp2.neg(t0), // -T0
t1, // T1
]);
let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
}
let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
const ell = [];
for (const bit of ATE_NAF) {
const cur = [];
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
if (bit)
({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
ell.push(cur);
}
return ell_coeff;
}
function millerLoop(ell, g1) {
const { x } = CURVE.params;
const Px = g1[0];
const Py = g1[1];
if (CURVE.postPrecompute) {
const last = ell[ell.length - 1];
CURVE.postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
}
return ell;
});
function millerLoopBatch(pairs, withFinalExponent = false) {
let f12 = Fp12.ONE;
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
const E = ell[j];
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
if ((0, utils_js_1.bitGet)(x, i)) {
j += 1;
const F = ell[j];
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
if (pairs.length) {
const ellLen = pairs[0][0].length;
for (let i = 0; i < ellLen; i++) {
f12 = Fp12.sqr(f12); // This allows us to do sqr only one time for all pairings
// NOTE: we apply multiple pairings in parallel here
for (const [ell, Px, Py] of pairs) {
for (const [c0, c1, c2] of ell[i])
f12 = lineFunction(c0, c1, c2, f12, Px, Py);
}
}
if (i !== 0)
f12 = Fp12.sqr(f12);
}
return Fp12.conjugate(f12);
if (BLS_X_IS_NEGATIVE)
f12 = Fp12.conjugate(f12);
return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
}
// Calculates product of multiple pairings
// This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
function pairingBatch(pairs, withFinalExponent = true) {
const res = [];
// This cache precomputed toAffine for all points
G1.ProjectivePoint.normalizeZ(pairs.map(({ g1 }) => g1));
G2.ProjectivePoint.normalizeZ(pairs.map(({ g2 }) => g2));
for (const { g1, g2 } of pairs) {
if (g1.equals(G1.ProjectivePoint.ZERO) || g2.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
// This uses toAffine inside
g1.assertValidity();
g2.assertValidity();
const Qa = g1.toAffine();
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
}
return millerLoopBatch(res, withFinalExponent);
}
// Calculates bilinear pairing
function pairing(Q, P, withFinalExponent = true) {
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
}
const utils = {

@@ -85,39 +165,4 @@ randomPrivateKey: () => {

};
// Point on G1 curve: (x, y)
const G1_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G1 });
const G1 = Object.assign(G1_, (0, hash_to_curve_js_1.createHasher)(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
}));
function pairingPrecomputes(point) {
const p = point;
if (p._PPRECOMPUTES)
return p._PPRECOMPUTES;
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
return p._PPRECOMPUTES;
}
// TODO: export
// function clearPairingPrecomputes(point: G2) {
// const p = point as G2 & withPairingPrecomputes;
// p._PPRECOMPUTES = undefined;
// }
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(G2_, (0, hash_to_curve_js_1.createHasher)(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
}));
const { ShortSignature } = CURVE.G1;
const { Signature } = CURVE.G2;
// Calculates bilinear pairing
function pairing(Q, P, withFinalExponent = true) {
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
Q.assertValidity();
P.assertValidity();
// Performance: 9ms for millerLoop and ~14ms for exp.
const Qa = Q.toAffine();
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
}
function normP1(point) {

@@ -172,7 +217,6 @@ return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);

const S = normP2(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P.negate(), Hm, false);
const eGS = pairing(G, S, false);
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
const exp = pairingBatch([
{ g1: P.negate(), g2: Hm }, // ePHM = pairing(P.negate(), Hm, false);
{ g1: G, g2: S }, // eGS = pairing(G, S, false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -187,7 +231,6 @@ }

const S = normP1(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const eHmP = pairing(Hm, P, false);
const eSG = pairing(S, G.negate(), false);
const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
const exp = pairingBatch([
{ g1: Hm, g2: P }, // eHmP = pairing(Hm, P, false);
{ g1: S, g2: G.negate() }, // eSG = pairing(S, G.negate(), false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -231,5 +274,5 @@ }

// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
function verifyBatch(signature, messages, publicKeys, htfOpts) {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
function verifyBatch(signature,
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
messages, publicKeys, htfOpts) {
if (!messages.length)

@@ -242,14 +285,22 @@ throw new Error('Expected non-empty messages array');

const nPublicKeys = publicKeys.map(normP1);
// NOTE: this works only for exact same object
const messagePubKeyMap = new Map();
for (let i = 0; i < nPublicKeys.length; i++) {
const pub = nPublicKeys[i];
const msg = nMessages[i];
let keys = messagePubKeyMap.get(msg);
if (keys === undefined) {
keys = [];
messagePubKeyMap.set(msg, keys);
}
keys.push(pub);
}
const paired = [];
try {
const paired = [];
for (const message of new Set(nMessages)) {
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
// Possible to batch pairing for same msg with different groupPublicKey here
paired.push(pairing(groupPublicKey, message, false));
for (const [msg, keys] of messagePubKeyMap) {
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
paired.push({ g1: groupPublicKey, g2: msg });
}
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
const exp = Fp12.finalExponentiate(product);
return Fp12.eql(exp, Fp12.ONE);
paired.push({ g1: G1.ProjectivePoint.BASE.negate(), g2: sig });
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
}

@@ -272,4 +323,5 @@ catch {

aggregateShortSignatures,
millerLoop,
millerLoopBatch,
pairing,
pairingBatch,
G1,

@@ -287,3 +339,3 @@ G2,

params: {
x: CURVE.params.x,
ateLoopSize: CURVE.params.ateLoopSize,
r: CURVE.params.r,

@@ -290,0 +342,0 @@ G1b: CURVE.G1.b,

@@ -48,6 +48,7 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

};
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): {
wNAFCached(P: T, n: bigint, transform: Mapper<T>): {
p: T;
f: T;
};
setWindowSize(P: T, W: number): void;
};

@@ -54,0 +55,0 @@ export type BasicCurve<T> = {

@@ -11,2 +11,6 @@ "use strict";

const _1n = BigInt(1);
// Since points in different groups cannot be equal (different object constructor),
// we can have single place to store precomputes
const pointPrecomputes = new WeakMap();
const pointWindowSizes = new WeakMap(); // This allows use make points immutable (nothing changes inside)
// Elliptic curve multiplication of Point by scalar. Fragile.

@@ -28,3 +32,8 @@ // Scalars should always be less than curve order: this should be checked inside of a curve itself.

};
const validateW = (W) => {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
};
const opts = (W) => {
validateW(W);
const windows = Math.ceil(bits / W) + 1; // +1, because

@@ -129,15 +138,21 @@ const windowSize = 2 ** (W - 1); // -1 because we skip zero

},
wNAFCached(P, precomputesMap, n, transform) {
// @ts-ignore
const W = P._WINDOW_SIZE || 1;
wNAFCached(P, n, transform) {
const W = pointWindowSizes.get(P) || 1;
// Calculate precomputes on a first run, reuse them after
let comp = precomputesMap.get(P);
let comp = pointPrecomputes.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W);
if (W !== 1) {
precomputesMap.set(P, transform(comp));
}
if (W !== 1)
pointPrecomputes.set(P, transform(comp));
}
return this.wNAF(W, comp, n);
},
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
setWindowSize(P, W) {
validateW(W);
pointWindowSizes.set(P, W);
pointPrecomputes.delete(P);
},
};

@@ -144,0 +159,0 @@ }

@@ -65,2 +65,6 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}
/**
* Edwards Curve interface.
* Main methods: `getPublicKey(priv)`, `sign(msg, priv)`, `verify(sig, msg, pub)`.
*/
export type CurveFn = {

@@ -88,4 +92,11 @@ CURVE: ReturnType<typeof validateOpts>;

};
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
export declare function twistedEdwards(curveDef: CurveType): CurveFn;
export {};
//# sourceMappingURL=edwards.d.ts.map

@@ -31,3 +31,9 @@ "use strict";

}
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
function twistedEdwards(curveDef) {

@@ -51,2 +57,3 @@ const CURVE = validateOpts(curveDef);

((data, ctx, phflag) => {
(0, utils_js_1.abool)('phflag', phflag);
if (ctx.length || phflag)

@@ -56,20 +63,50 @@ throw new Error('Contexts/pre-hash are not supported');

}); // NOOP
const inBig = (n) => typeof n === 'bigint' && _0n < n; // n in [1..]
const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
function assertInRange(n, max) {
// n in [1..max-1]
if (inRange(n, max))
return n;
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
// 0 <= n < MASK
// Coordinates larger than Fp.ORDER are allowed for zip215
function aCoordinate(title, n) {
ut.aInRange('coordinate ' + title, n, _0n, MASK);
}
function assertGE0(n) {
// n in [0..CURVE_ORDER-1]
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
}
const pointPrecomputes = new Map();
function isPoint(other) {
function assertPoint(other) {
if (!(other instanceof Point))
throw new Error('ExtendedPoint expected');
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
const toAffineMemo = (0, utils_js_1.memoized)((p, iz) => {
const { ex: x, ey: y, ez: z } = p;
const is0 = p.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
const assertValidMemo = (0, utils_js_1.memoized)((p) => {
const { a, d } = CURVE;
if (p.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = p;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
return true;
});
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).

@@ -83,10 +120,7 @@ // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates

this.et = et;
if (!in0MaskRange(ex))
throw new Error('x required');
if (!in0MaskRange(ey))
throw new Error('y required');
if (!in0MaskRange(ez))
throw new Error('z required');
if (!in0MaskRange(et))
throw new Error('t required');
aCoordinate('x', ex);
aCoordinate('y', ey);
aCoordinate('z', ez);
aCoordinate('t', et);
Object.freeze(this);
}

@@ -103,4 +137,4 @@ get x() {

const { x, y } = p || {};
if (!in0MaskRange(x) || !in0MaskRange(y))
throw new Error('invalid affine point');
aCoordinate('x', x);
aCoordinate('y', y);
return new Point(x, y, _1n, modP(x * y));

@@ -114,4 +148,3 @@ }

_setWindowSize(windowSize) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}

@@ -121,26 +154,7 @@ // Not required for fromHex(), which always creates valid points.

assertValidity() {
const { a, d } = CURVE;
if (this.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = this;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
assertValidMemo(this);
}
// Compare one point to another.
equals(other) {
isPoint(other);
assertPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;

@@ -186,3 +200,3 @@ const { ex: X2, ey: Y2, ez: Z2 } = other;

add(other) {
isPoint(other);
assertPoint(other);
const { a, d } = CURVE;

@@ -230,7 +244,9 @@ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;

wNAF(n) {
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}
// Constant-time multiplication.
multiply(scalar) {
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
const n = scalar;
ut.aInRange('scalar', n, _1n, CURVE_ORDER); // 1 <= scalar < L
const { p, f } = this.wNAF(n);
return Point.normalizeZ([p, f])[0];

@@ -243,3 +259,4 @@ }

multiplyUnsafe(scalar) {
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
const n = scalar;
ut.aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
if (n === _0n)

@@ -268,14 +285,3 @@ return I;

toAffine(iz) {
const { ex: x, ey: y, ez: z } = this;
const is0 = this.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -294,2 +300,3 @@ clearCofactor() {

hex = (0, utils_js_1.ensureBytes)('pointHex', hex, len); // copy hex to a new array
(0, utils_js_1.abool)('zip215', zip215);
const normed = hex.slice(); // copy again, we'll manipulate it

@@ -299,12 +306,7 @@ const lastByte = hex[len - 1]; // select last byte

const y = ut.bytesToNumberLE(normed);
if (y === _0n) {
// y=0 is allowed
}
else {
// RFC8032 prohibits >= p, but ZIP215 doesn't
if (zip215)
assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
else
assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
}
// RFC8032 prohibits >= p, but ZIP215 doesn't
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
const max = zip215 ? MASK : Fp.ORDER;
ut.aInRange('pointHex.y', y, _0n, max);
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:

@@ -384,3 +386,3 @@ // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)

const s = modN(r + k * scalar); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
ut.aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));

@@ -395,2 +397,4 @@ return (0, utils_js_1.ensureBytes)('result', res, nByteLength * 2); // 64-byte signature

msg = (0, utils_js_1.ensureBytes)('message', msg);
if (zip215 !== undefined)
(0, utils_js_1.abool)('zip215', zip215);
if (prehash)

@@ -397,0 +401,0 @@ msg = prehash(msg); // for ed25519ph, etc

@@ -65,2 +65,3 @@ export declare function mod(a: bigint, b: bigint): bigint;

export declare function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T;
export declare function FpLegendre(order: bigint): <T>(f: IField<T>, x: T) => T;
export declare function FpIsSquare<T>(f: IField<T>): (x: T) => boolean;

@@ -79,2 +80,5 @@ export declare function nLength(n: bigint, nBitLength?: number): {

* * c) Object.freeze
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you doing.
* @param ORDER prime positive bigint

@@ -81,0 +85,0 @@ * @param bitLen how many bits the field consumes

@@ -14,2 +14,3 @@ "use strict";

exports.FpDiv = FpDiv;
exports.FpLegendre = FpLegendre;
exports.FpIsSquare = FpIsSquare;

@@ -288,7 +289,14 @@ exports.nLength = nLength;

}
function FpLegendre(order) {
// (a | p) ≡ 1 if a is a square (mod p), quadratic residue
// (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
// (a | p) ≡ 0 if a ≡ 0 (mod p)
const legendreConst = (order - _1n) / _2n; // Integer arithmetic
return (f, x) => f.pow(x, legendreConst);
}
// This function returns True whenever the value x is a square in the field F.
function FpIsSquare(f) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
const legendre = FpLegendre(f.ORDER);
return (x) => {
const p = f.pow(x, legendreConst);
const p = legendre(f, x);
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);

@@ -311,2 +319,5 @@ };

* * c) Object.freeze
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you doing.
* @param ORDER prime positive bigint

@@ -313,0 +324,0 @@ * @param bitLen how many bits the field consumes

@@ -50,8 +50,2 @@ "use strict";

}
// Accepts 0 as well
function assertFieldElement(n) {
if (typeof n === 'bigint' && _0n <= n && n < P)
return n;
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
}
// x25519 from 4

@@ -66,7 +60,8 @@ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519

*/
function montgomeryLadder(pointU, scalar) {
const u = assertFieldElement(pointU);
function montgomeryLadder(u, scalar) {
(0, utils_js_1.aInRange)('u', u, _0n, P);
(0, utils_js_1.aInRange)('scalar', scalar, _0n, P);
// Section 5: Implementations MUST accept non-canonical values and process them as
// if they had been reduced modulo the field prime.
const k = assertFieldElement(scalar);
const k = scalar;
const x_1 = u;

@@ -73,0 +68,0 @@ let x_2 = _1n;

@@ -14,2 +14,3 @@ export type Hex = Uint8Array | string;

export declare function abytes(item: unknown): void;
export declare function abool(title: string, value: boolean): void;
/**

@@ -49,3 +50,10 @@ * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'

export declare function utf8ToBytes(str: string): Uint8Array;
export declare function inRange(n: bigint, min: bigint, max: bigint): boolean;
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
export declare function aInRange(title: string, n: bigint, min: bigint, max: bigint): void;
/**
* Calculates amount of bits in a bigint.

@@ -95,3 +103,12 @@ * Same as `n.toString(2).length`

export declare function validateObject<T extends Record<string, any>>(object: T, validators: ValMap<T>, optValidators?: ValMap<T>): T;
/**
* throws not implemented error
*/
export declare const notImplemented: () => never;
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
export declare function memoized<T extends object, R, O extends any[]>(fn: (arg: T, ...args: O) => R): (arg: T, ...args: O) => R;
export {};
//# sourceMappingURL=utils.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bitMask = void 0;
exports.notImplemented = exports.bitMask = void 0;
exports.isBytes = isBytes;
exports.abytes = abytes;
exports.abool = abool;
exports.bytesToHex = bytesToHex;

@@ -19,2 +20,4 @@ exports.numberToHexUnpadded = numberToHexUnpadded;

exports.utf8ToBytes = utf8ToBytes;
exports.inRange = inRange;
exports.aInRange = aInRange;
exports.bitLen = bitLen;

@@ -25,2 +28,3 @@ exports.bitGet = bitGet;

exports.validateObject = validateObject;
exports.memoized = memoized;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

@@ -42,2 +46,6 @@ // 100 lines of code in the file are duplicated from noble-hashes (utils).

}
function abool(title, value) {
if (typeof value !== 'boolean')
throw new Error(`${title} must be valid boolean, got "${value}".`);
}
// Array where index 0xf0 (240) is mapped to string 'f0'

@@ -185,2 +193,21 @@ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));

}
// Is positive bigint
const isPosBig = (n) => typeof n === 'bigint' && _0n <= n;
function inRange(n, min, max) {
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
}
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
function aInRange(title, n, min, max) {
// Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
// consider P=256n, min=0n, max=P
// - a for min=0 would require -1: `inRange('x', x, -1n, P)`
// - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
// - our way is the cleanest: `inRange('x', x, 0n, P)
if (!inRange(n, min, max))
throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`);
}
// Bit operations

@@ -317,2 +344,24 @@ /**

// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
/**
* throws not implemented error
*/
const notImplemented = () => {
throw new Error('not implemented');
};
exports.notImplemented = notImplemented;
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
function memoized(fn) {
const map = new WeakMap();
return (arg, ...args) => {
const val = map.get(arg);
if (val !== undefined)
return val;
const computed = fn(arg, ...args);
map.set(arg, computed);
return computed;
};
}
//# sourceMappingURL=utils.js.map

@@ -210,2 +210,9 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

};
/**
* Creates short weierstrass curve and ECDSA signature methods for it.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
*/
export declare function weierstrass(curveDef: CurveType): CurveFn;

@@ -212,0 +219,0 @@ /**

@@ -14,2 +14,8 @@ "use strict";

const utils_js_1 = require("./utils.js");
function validateSigVerOpts(opts) {
if (opts.lowS !== undefined)
(0, utils_js_1.abool)('lowS', opts.lowS);
if (opts.prehash !== undefined)
(0, utils_js_1.abool)('prehash', opts.prehash);
}
function validatePointOpts(curve) {

@@ -139,12 +145,8 @@ const opts = (0, curve_js_1.validateBasic)(curve);

function isWithinCurveOrder(num) {
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
return ut.inRange(num, _1n, CURVE.n);
}
function assertGE(num) {
if (!isWithinCurveOrder(num))
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
}
// Validates if priv key is valid and converts it to bigint.
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
function normPrivateKeyToScalar(key) {
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
if (lengths && typeof key !== 'bigint') {

@@ -169,7 +171,6 @@ if (ut.isBytes(key))

if (wrapPrivateKey)
num = mod.mod(num, n); // disabled by default, enabled for BLS
assertGE(num); // num in range [1..N-1]
num = mod.mod(num, N); // disabled by default, enabled for BLS
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
return num;
}
const pointPrecomputes = new Map();
function assertPrjPoint(other) {

@@ -179,2 +180,49 @@ if (!(other instanceof Point))

}
// Memoized toAffine / validity check. They are heavy. Points are immutable.
// Converts Projective point to affine (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
// (x, y, z) ∋ (x=x/z, y=y/z)
const toAffineMemo = (0, utils_js_1.memoized)((p, iz) => {
const { px: x, py: y, pz: z } = p;
// Fast-path for normalized points
if (Fp.eql(z, Fp.ONE))
return { x, y };
const is0 = p.is0();
// 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.
if (iz == null)
iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0)
return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE))
throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
// NOTE: on exception this will crash 'cached' and no value will be set.
// Otherwise true will be return
const assertValidMemo = (0, utils_js_1.memoized)((p) => {
if (p.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = p.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y))
throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right))
throw new Error('bad point: equation left != right');
if (!p.isTorsionFree())
throw new Error('bad point: not in prime-order subgroup');
return true;
});
/**

@@ -196,2 +244,3 @@ * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)

throw new Error('z required');
Object.freeze(this);
}

@@ -243,26 +292,7 @@ // Does not validate if the point is on-curve.

_setWindowSize(windowSize) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}
// A point on curve is valid if it conforms to equation.
assertValidity() {
if (this.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(this.py))
return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = this.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y))
throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right))
throw new Error('bad point: equation left != right');
if (!this.isTorsionFree())
throw new Error('bad point: not in prime-order subgroup');
assertValidMemo(this);
}

@@ -394,6 +424,3 @@ hasEvenY() {

wNAF(n) {
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
});
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}

@@ -405,14 +432,14 @@ /**

*/
multiplyUnsafe(n) {
multiplyUnsafe(sc) {
ut.aInRange('scalar', sc, _0n, CURVE.n);
const I = Point.ZERO;
if (n === _0n)
if (sc === _0n)
return I;
assertGE(n); // Will throw on 0
if (n === _1n)
if (sc === _1n)
return this;
const { endo } = CURVE;
if (!endo)
return wnaf.unsafeLadder(this, n);
return wnaf.unsafeLadder(this, sc);
// Apply endomorphism
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
let k1p = I;

@@ -447,8 +474,7 @@ let k2p = I;

multiply(scalar) {
assertGE(scalar);
let n = scalar;
const { endo, n: N } = CURVE;
ut.aInRange('scalar', scalar, _1n, N);
let point, fake; // Fake point is used to const-time mult
const { endo } = CURVE;
if (endo) {
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
let { p: k1p, f: f1p } = this.wNAF(k1);

@@ -463,3 +489,3 @@ let { p: k2p, f: f2p } = this.wNAF(k2);

else {
const { p, f } = this.wNAF(n);
const { p, f } = this.wNAF(scalar);
point = p;

@@ -488,16 +514,3 @@ fake = f;

toAffine(iz) {
const { px: x, py: y, pz: z } = this;
const is0 = this.is0();
// 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.
if (iz == null)
iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0)
return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE))
throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -521,2 +534,3 @@ isTorsionFree() {

toRawBytes(isCompressed = true) {
(0, utils_js_1.abool)('isCompressed', isCompressed);
this.assertValidity();

@@ -526,2 +540,3 @@ return toBytes(Point, this, isCompressed);

toHex(isCompressed = true) {
(0, utils_js_1.abool)('isCompressed', isCompressed);
return ut.bytesToHex(this.toRawBytes(isCompressed));

@@ -556,2 +571,9 @@ }

}
/**
* Creates short weierstrass curve and ECDSA signature methods for it.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
*/
function weierstrass(curveDef) {

@@ -562,5 +584,2 @@ const CURVE = validateOpts(curveDef);

const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
function isValidFieldElement(num) {
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
}
function modN(a) {

@@ -578,2 +597,3 @@ return mod.mod(a, CURVE_ORDER);

const cat = ut.concatBytes;
(0, utils_js_1.abool)('isCompressed', isCompressed);
if (isCompressed) {

@@ -593,3 +613,3 @@ return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);

const x = ut.bytesToNumberBE(tail);
if (!isValidFieldElement(x))
if (!ut.inRange(x, _1n, Fp.ORDER))
throw new Error('Point is not on curve');

@@ -655,7 +675,4 @@ const y2 = weierstrassEquation(x); // y² = x³ + ax + b

assertValidity() {
// can use assertGE here
if (!isWithinCurveOrder(this.r))
throw new Error('r must be 0 < r < CURVE.n');
if (!isWithinCurveOrder(this.s))
throw new Error('s must be 0 < s < CURVE.n');
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
}

@@ -803,6 +820,3 @@ addRecoveryBit(recovery) {

function int2octets(num) {
if (typeof num !== 'bigint')
throw new Error('bigint expected');
if (!(_0n <= num && num < ORDER_MASK))
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
// works with order, can have different size than numToField!

@@ -824,2 +838,3 @@ return ut.numberToBytesBE(num, CURVE.nByteLength);

msgHash = (0, utils_js_1.ensureBytes)('msgHash', msgHash);
validateSigVerOpts(opts);
if (prehash)

@@ -911,2 +926,3 @@ msgHash = (0, utils_js_1.ensureBytes)('prehashed msgHash', hash(msgHash));

throw new Error('options.strict was renamed to lowS');
validateSigVerOpts(opts);
const { lowS, prehash } = opts;

@@ -913,0 +929,0 @@ let _sig = undefined;

import { CurveFn } from './abstract/bls.js';
import * as mod from './abstract/modular.js';
declare const Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, "isOdd">>>;
type Fp = bigint;
type BigintTuple = [bigint, bigint];
type Fp2 = {
c0: bigint;
c1: bigint;
};
type Fp2Utils = {
fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
reim: (num: Fp2) => {
re: bigint;
im: bigint;
};
mulByNonresidue: (num: Fp2) => Fp2;
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
declare const Fp2: mod.IField<Fp2> & Fp2Utils;
type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
type Fp6 = {
c0: Fp2;
c1: Fp2;
c2: Fp2;
};
type Fp6Utils = {
fromBigSix: (tuple: BigintSix) => Fp6;
mulByNonresidue: (num: Fp6) => Fp6;
frobeniusMap(num: Fp6, power: number): Fp6;
multiplyBy1(num: Fp6, b1: Fp2): Fp6;
multiplyBy01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
};
declare const Fp6: mod.IField<Fp6> & Fp6Utils;
type Fp12 = {
c0: Fp6;
c1: Fp6;
};
type BigintTwelve = [
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint
];
type Fp12Utils = {
fromBigTwelve: (t: BigintTwelve) => Fp12;
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
multiplyByFp2(lhs: Fp12, rhs: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
_cyclotomicSquare(num: Fp12): Fp12;
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
};
declare const Fp12: mod.IField<Fp12> & Fp12Utils;
export declare const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12>;
export {};
export declare const bls12_381: CurveFn;
//# sourceMappingURL=bls12-381.d.ts.map

@@ -13,2 +13,3 @@ "use strict";

const weierstrass_js_1 = require("./abstract/weierstrass.js");
const tower_js_1 = require("./abstract/tower.js");
/*

@@ -49,496 +50,36 @@ bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:

const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
// prettier-ignore
const _8n = BigInt(8), _16n = BigInt(16);
/*
Embedding degree (k): 12
Seed (X): -15132376222941642752
Fr: (x⁴-x²+1)
Fp: ((x-1)² ⋅ r(x)/3+x)
(E/Fp): Y²=X³+4
(Eₜ/Fp²): Y² = X³+4(u+1) (M-type twist)
Ate loop size: X
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-1-u
- Fp¹²[w] = Fp⁶/w²-v
TODO: BLS & BN Fp/Fr can be constructed from seed.
*/
// The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = (0, utils_js_1.bitLen)(BLS_X);
// CURVE FIELDS
// Finite field over p.
const Fp_raw = BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab');
const Fp = mod.Field(Fp_raw);
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
const Fp2Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp.add(c0, r0),
c1: Fp.add(c1, r1),
});
const Fp2Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp.sub(c0, r0),
c1: Fp.sub(c1, r1),
});
const Fp2Multiply = ({ c0, c1 }, rhs) => {
if (typeof rhs === 'bigint')
return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
const { c0: r0, c1: r1 } = rhs;
let t1 = Fp.mul(c0, r0); // c0 * o0
let t2 = Fp.mul(c1, r1); // c1 * o1
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
const o0 = Fp.sub(t1, t2);
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
return { c0: o0, c1: o1 };
};
const Fp2Square = ({ c0, c1 }) => {
const a = Fp.add(c0, c1);
const b = Fp.sub(c0, c1);
const c = Fp.add(c0, c0);
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
};
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
// G² - 1
// h2q
// NOTE: ORDER was wrong!
const FP2_ORDER = Fp_raw * Fp_raw;
const Fp2 = {
ORDER: FP2_ORDER,
BITS: (0, utils_js_1.bitLen)(FP2_ORDER),
BYTES: Math.ceil((0, utils_js_1.bitLen)(FP2_ORDER) / 8),
MASK: (0, utils_js_1.bitMask)((0, utils_js_1.bitLen)(FP2_ORDER)),
ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
pow: (num, power) => mod.FpPow(Fp2, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
// Normalized
add: Fp2Add,
sub: Fp2Subtract,
mul: Fp2Multiply,
sqr: Fp2Square,
// NonNormalized stuff
addN: Fp2Add,
subN: Fp2Subtract,
mulN: Fp2Multiply,
sqrN: Fp2Square,
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
inv: ({ c0: a, c1: b }) => {
// We wish to find the multiplicative inverse of a nonzero
// element a + bu in Fp2. We leverage an identity
//
// (a + bu)(a - bu) = a² + b²
//
// which holds because u² = -1. This can be rewritten as
//
// (a + bu)(a - bu)/(a² + b²) = 1
//
// because a² + b² = 0 has no nonzero solutions for (a, b).
// This gives that (a - bu)/(a² + b²) is the inverse
// of (a + bu). Importantly, this can be computing using
// only a single inversion in Fp.
const factor = Fp.inv(Fp.create(a * a + b * b));
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
},
sqrt: (num) => {
if (Fp2.eql(num, Fp2.ZERO))
return Fp2.ZERO; // Algo doesn't handles this case
// TODO: Optimize this line. It's extremely slow.
// Speeding this up would boost aggregateSignatures.
// https://eprint.iacr.org/2012/685.pdf applicable?
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
const R = FP2_ROOTS_OF_UNITY;
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
if (!divisor)
throw new Error('No root');
const index = R.indexOf(divisor);
const root = R[index / 2];
if (!root)
throw new Error('Invalid root');
const x1 = Fp2.div(candidateSqrt, root);
const x2 = Fp2.neg(x1);
const { re: re1, im: im1 } = Fp2.reim(x1);
const { re: re2, im: im2 } = Fp2.reim(x2);
if (im1 > im2 || (im1 === im2 && re1 > re2))
return x1;
return x2;
},
// Same as sgn0_m_eq_2 in RFC 9380
isOdd: (x) => {
const { re: x0, im: x1 } = Fp2.reim(x);
const sign_0 = x0 % _2n;
const zero_0 = x0 === _0n;
const sign_1 = x1 % _2n;
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
},
// Bytes util
fromBytes(b) {
if (b.length !== Fp2.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
},
toBytes: ({ c0, c1 }) => (0, utils_js_1.concatBytes)(Fp.toBytes(c0), Fp.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp.cmov(c0, r0, c),
c1: Fp.cmov(c1, r1, c),
}),
// Specific utils
// toString() {
// return `Fp2(${this.c0} + ${this.c1}×i)`;
// }
reim: ({ c0, c1 }) => ({ re: c0, im: c1 }),
// multiply by u + 1
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
multiplyByB: ({ c0, c1 }) => {
let t0 = Fp.mul(c0, _4n); // 4 * c0
let t1 = Fp.mul(c1, _4n); // 4 * c1
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = (0, tower_js_1.tower12)({
// Order of Fp
ORDER: BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'),
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
FP2_NONRESIDUE: [_1n, _1n],
Fp2mulByB: ({ c0, c1 }) => {
const t0 = Fp.mul(c0, _4n); // 4 * c0
const t1 = Fp.mul(c1, _4n); // 4 * c1
// (T0-T1) + (T0+T1)*i
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
},
fromBigTuple: (tuple) => {
if (tuple.length !== 2)
throw new Error('Invalid tuple');
const fps = tuple.map((n) => Fp.create(n));
return { c0: fps[0], c1: fps[1] };
},
frobeniusMap: ({ c0, c1 }, power) => ({
c0,
c1: Fp.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]),
}),
};
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
const FP2_FROBENIUS_COEFFICIENTS = [
BigInt('0x1'),
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
].map((item) => Fp.create(item));
// For Fp2 roots of unity.
const rv1 = BigInt('0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09');
// const ev1 =
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
// const ev2 =
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
// const ev3 =
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
// const ev4 =
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
// Eighth roots of unity, used for computing square roots in Fp2.
// To verify or re-calculate:
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
const FP2_ROOTS_OF_UNITY = [
[_1n, _0n],
[rv1, -rv1],
[_0n, _1n],
[rv1, rv1],
[-_1n, _0n],
[-rv1, rv1],
[_0n, -_1n],
[-rv1, -rv1],
].map((pair) => Fp2.fromBigTuple(pair));
const Fp6Add = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
c0: Fp2.add(c0, r0),
c1: Fp2.add(c1, r1),
c2: Fp2.add(c2, r2),
});
const Fp6Subtract = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
c0: Fp2.sub(c0, r0),
c1: Fp2.sub(c1, r1),
c2: Fp2.sub(c2, r2),
});
const Fp6Multiply = ({ c0, c1, c2 }, rhs) => {
if (typeof rhs === 'bigint') {
return {
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
};
}
const { c0: r0, c1: r1, c2: r2 } = rhs;
const t0 = Fp2.mul(c0, r0); // c0 * o0
const t1 = Fp2.mul(c1, r1); // c1 * o1
const t2 = Fp2.mul(c2, r2); // c2 * o2
return {
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
};
};
const Fp6Square = ({ c0, c1, c2 }) => {
let t0 = Fp2.sqr(c0); // c0²
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
let t4 = Fp2.sqr(c2); // c2²
return {
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
};
};
const Fp6 = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 3 * Fp2.BITS,
BYTES: 3 * Fp2.BYTES,
MASK: (0, utils_js_1.bitMask)(3 * Fp2.BITS),
ZERO: { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO },
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
create: (num) => num,
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
sqrt: () => {
throw new Error('Not implemented');
},
// Do we need division by bigint at all? Should be done via order:
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp6, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
// Normalized
add: Fp6Add,
sub: Fp6Subtract,
mul: Fp6Multiply,
sqr: Fp6Square,
// NonNormalized stuff
addN: Fp6Add,
subN: Fp6Subtract,
mulN: Fp6Multiply,
sqrN: Fp6Square,
inv: ({ c0, c1, c2 }) => {
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
let t4 = Fp2.inv(Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0)));
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
},
// Bytes utils
fromBytes: (b) => {
if (b.length !== Fp6.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp2.fromBytes(b.subarray(0, Fp2.BYTES)),
c1: Fp2.fromBytes(b.subarray(Fp2.BYTES, 2 * Fp2.BYTES)),
c2: Fp2.fromBytes(b.subarray(2 * Fp2.BYTES)),
};
},
toBytes: ({ c0, c1, c2 }) => (0, utils_js_1.concatBytes)(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
cmov: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) => ({
c0: Fp2.cmov(c0, r0, c),
c1: Fp2.cmov(c1, r1, c),
c2: Fp2.cmov(c2, r2, c),
}),
// Utils
// fromTriple(triple: [Fp2, Fp2, Fp2]) {
// return new Fp6(...triple);
// }
// toString() {
// return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`;
// }
fromBigSix: (t) => {
if (!Array.isArray(t) || t.length !== 6)
throw new Error('Invalid Fp6 usage');
return {
c0: Fp2.fromBigTuple(t.slice(0, 2)),
c1: Fp2.fromBigTuple(t.slice(2, 4)),
c2: Fp2.fromBigTuple(t.slice(4, 6)),
};
},
frobeniusMap: ({ c0, c1, c2 }, power) => ({
c0: Fp2.frobeniusMap(c0, power),
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]),
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]),
}),
mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 }),
// Sparse multiplication
multiplyBy1: ({ c0, c1, c2 }, b1) => ({
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
c1: Fp2.mul(c0, b1),
c2: Fp2.mul(c1, b1),
}),
// Sparse multiplication
multiplyBy01({ c0, c1, c2 }, b0, b1) {
let t0 = Fp2.mul(c0, b0); // c0 * b0
let t1 = Fp2.mul(c1, b1); // c1 * b1
return {
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
// (b0 + b1) * (c0 + c1) - T0 - T1
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
// (c0 + c2) * b0 - T0 + T1
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
};
},
multiplyByFp2: ({ c0, c1, c2 }, rhs) => ({
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
}),
};
const FP6_FROBENIUS_COEFFICIENTS_1 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x0'),
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[BigInt('0x0'), BigInt('0x1')],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x0'),
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
],
].map((pair) => Fp2.fromBigTuple(pair));
const FP6_FROBENIUS_COEFFICIENTS_2 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
BigInt('0x0'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
BigInt('0x0'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
BigInt('0x0'),
],
].map((pair) => Fp2.fromBigTuple(pair));
// The BLS parameter x for BLS12-381
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = (0, utils_js_1.bitLen)(BLS_X);
const Fp12Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp6.add(c0, r0),
c1: Fp6.add(c1, r1),
});
const Fp12Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp6.sub(c0, r0),
c1: Fp6.sub(c1, r1),
});
const Fp12Multiply = ({ c0, c1 }, rhs) => {
if (typeof rhs === 'bigint')
return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
let { c0: r0, c1: r1 } = rhs;
let t1 = Fp6.mul(c0, r0); // c0 * r0
let t2 = Fp6.mul(c1, r1); // c1 * r1
return {
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
// (c0 + c1) * (r0 + r1) - (T1 + T2)
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
};
};
const Fp12Square = ({ c0, c1 }) => {
let ab = Fp6.mul(c0, c1); // c0 * c1
return {
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
c1: Fp6.add(ab, ab),
}; // AB + AB
};
function Fp4Square(a, b) {
const a2 = Fp2.sqr(a);
const b2 = Fp2.sqr(b);
return {
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
};
}
const Fp12 = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 2 * Fp2.BITS,
BYTES: 2 * Fp2.BYTES,
MASK: (0, utils_js_1.bitMask)(2 * Fp2.BITS),
ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
sqrt: () => {
throw new Error('Not implemented');
},
inv: ({ c0, c1 }) => {
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
},
div: (lhs, rhs) => Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp12, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
// Normalized
add: Fp12Add,
sub: Fp12Subtract,
mul: Fp12Multiply,
sqr: Fp12Square,
// NonNormalized stuff
addN: Fp12Add,
subN: Fp12Subtract,
mulN: Fp12Multiply,
sqrN: Fp12Square,
// Bytes utils
fromBytes: (b) => {
if (b.length !== Fp12.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
};
},
toBytes: ({ c0, c1 }) => (0, utils_js_1.concatBytes)(Fp6.toBytes(c0), Fp6.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp6.cmov(c0, r0, c),
c1: Fp6.cmov(c1, r1, c),
}),
// Utils
// toString() {
// return `Fp12(${this.c0} + ${this.c1} * w)`;
// },
// fromTuple(c: [Fp6, Fp6]) {
// return new Fp12(...c);
// }
fromBigTwelve: (t) => ({
c0: Fp6.fromBigSix(t.slice(0, 6)),
c1: Fp6.fromBigSix(t.slice(6, 12)),
}),
// Raises to q**i -th power
frobeniusMap(lhs, power) {
const r0 = Fp6.frobeniusMap(lhs.c0, power);
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12];
return {
c0: r0,
c1: Fp6.create({
c0: Fp2.mul(c0, coeff),
c1: Fp2.mul(c1, coeff),
c2: Fp2.mul(c2, coeff),
}),
};
},
// Sparse multiplication
multiplyBy014: ({ c0, c1 }, o0, o1, o4) => {
let t0 = Fp6.multiplyBy01(c0, o0, o1);
let t1 = Fp6.multiplyBy1(c1, o4);
return {
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
// (c1 + c0) * [o0, o1+o4] - T0 - T1
c1: Fp6.sub(Fp6.sub(Fp6.multiplyBy01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
};
},
multiplyByFp2: ({ c0, c1 }, rhs) => ({
c0: Fp6.multiplyByFp2(c0, rhs),
c1: Fp6.multiplyByFp2(c1, rhs),
}),
conjugate: ({ c0, c1 }) => ({ c0, c1: Fp6.neg(c1) }),
// Fp12
// A cyclotomic group is a subgroup of Fp^n defined by

@@ -548,3 +89,3 @@ // GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}

// https://eprint.iacr.org/2009/565.pdf
_cyclotomicSquare: ({ c0, c1 }) => {
Fp12cyclotomicSquare: ({ c0, c1 }) => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;

@@ -555,3 +96,3 @@ const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;

const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {

@@ -570,3 +111,3 @@ c0: Fp6.create({

},
_cyclotomicExp(num, n) {
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;

@@ -582,3 +123,3 @@ for (let i = BLS_X_LEN - 1; i >= 0; i--) {

// https://eprint.iacr.org/2009/565.pdf
finalExponentiate: (num) => {
Fp12finalExponentiate: (num) => {
const x = BLS_X;

@@ -602,50 +143,6 @@ // this^(q⁶) / this

},
};
const FP12_FROBENIUS_COEFFICIENTS = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
BigInt('0x0'),
],
[
BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[
BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
],
[
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
BigInt('0x0'),
],
[
BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
BigInt('0x0'),
],
[
BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
],
].map((n) => Fp2.fromBigTuple(n));
});
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
// END OF CURVE FIELDS

@@ -806,29 +303,3 @@ // HashToCurve

// Ψ(P) endomorphism
const ut_root = Fp6.create({ c0: Fp2.ZERO, c1: Fp2.ONE, c2: Fp2.ZERO });
const wsq = Fp12.create({ c0: ut_root, c1: Fp6.ZERO });
const wcu = Fp12.create({ c0: Fp6.ZERO, c1: ut_root });
const [wsq_inv, wcu_inv] = Fp12.invertBatch([wsq, wcu]);
function psi(x, y) {
// Untwist Fp2->Fp12 && frobenius(1) && twist back
const x2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wsq_inv, x), 1), wsq).c0.c0;
const y2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wcu_inv, y), 1), wcu).c0.c0;
return [x2, y2];
}
// Ψ endomorphism
function G2psi(c, P) {
const affine = P.toAffine();
const p = psi(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
// Ψ²(P) endomorphism
// 1 / F2(2)^((p-1)/3) in GF(p²)
const PSI2_C1 = BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac');
function psi2(x, y) {
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
}
function G2psi2(c, P) {
const affine = P.toAffine();
const p = psi2(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
const { G2psi, G2psi2 } = (0, tower_js_1.psiFrobenius)(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
// Default hash_to_field options are for hash to G2.

@@ -960,4 +431,4 @@ //

// todo: unroll
const xP = point.multiplyUnsafe(exports.bls12_381.params.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(exports.bls12_381.params.x); // [u2]P
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
return u2P.equals(phi);

@@ -980,3 +451,3 @@ // https://eprint.iacr.org/2019/814.pdf

// return this.multiplyUnsafe(CURVE.h);
return point.multiplyUnsafe(exports.bls12_381.params.x).add(point); // x*P + P
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
},

@@ -1106,3 +577,3 @@ mapToCurve: (scalars) => {

isTorsionFree: (c, P) => {
return P.multiplyUnsafe(exports.bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
// Older version: https://eprint.iacr.org/2019/814.pdf

@@ -1117,3 +588,3 @@ // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O

clearCofactor: (c, P) => {
const x = exports.bls12_381.params.x;
const x = BLS_X;
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P

@@ -1237,4 +708,6 @@ let t2 = G2psi(c, P); // Ψ(P)

params: {
x: BLS_X, // The BLS parameter x for BLS12-381
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
xNegative: true,
twistType: 'multiplicative',
},

@@ -1241,0 +714,0 @@ htfDefaults,

@@ -0,9 +1,13 @@

import { CurveFn } from './abstract/bls.js';
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
* so the naming has been adjusted to its prime bit count
* https://hal.science/hal-01534101/file/main.pdf
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
* Contains G1 / G2 operations and pairings.
*/
export declare const bn254: import("./abstract/weierstrass.js").CurveFn;
export declare const bn254: CurveFn;
/**
* bn254 weierstrass curve with ECDSA.
* This is very rare and probably not used anywhere.
* Instead, you should use G1 / G2, defined above.
*/
export declare const bn254_weierstrass: import("./abstract/weierstrass.js").CurveFn;
//# sourceMappingURL=bn254.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bn254 = void 0;
exports.bn254_weierstrass = exports.bn254 = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const sha256_1 = require("@noble/hashes/sha256");
const _shortw_utils_js_1 = require("./_shortw_utils.js");
const weierstrass_js_1 = require("./abstract/weierstrass.js");
const utils_1 = require("@noble/hashes/utils");
const bls_js_1 = require("./abstract/bls.js");
const modular_js_1 = require("./abstract/modular.js");
const weierstrass_js_1 = require("./abstract/weierstrass.js");
const utils_js_1 = require("./abstract/utils.js");
const tower_js_1 = require("./abstract/tower.js");
// prettier-ignore
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// prettier-ignore
const _6n = BigInt(6);
/*
bn254, previously known as alt_bn_128, when it had 128-bit security.
Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
so the naming has been adjusted to its prime bit count:
https://hal.science/hal-01534101/file/main.pdf
There are huge compatibility issues in the ecosystem:
1. Different libraries call it in different ways: "bn254", "bn256", "alt_bn128", "bn128".
2. libff has bn128, but it's a different curve with different G2:
https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169
3. halo2curves bn256 is also incompatible and returns different outputs
The goal of our implementation is to support "Ethereum" variant of the curve,
because it at least has specs:
- EIP196 (https://eips.ethereum.org/EIPS/eip-196) describes bn254 ECADD and ECMUL opcodes for EVM
- EIP197 (https://eips.ethereum.org/EIPS/eip-197) describes bn254 pairings
- It's hard: EIPs don't have proper tests. EIP-197 returns boolean output instead of Fp12
- The existing implementations are bad. Some are deprecated:
- https://github.com/paritytech/bn (old version)
- https://github.com/ewasm/ethereum-bn128.rs (uses paritytech/bn)
- https://github.com/zcash-hackworks/bn
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
- Python implementations use different towers and produce different Fp12 outputs:
- https://github.com/ethereum/py_pairing
- https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
- Points are encoded differently in different implementations
*/
/*
Seed (X): 4965661367192848881
Fr: (36x⁴+36x³+18x²+6x+1)
Fp: (36x⁴+36x³+24x²+6x+1)
(E / Fp ): Y² = X³+3
(Et / Fp²): Y² = X³+3/(u+9) (D-type twist)
Ate loop size: 6x+2
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-9-u
- Fp¹²[w] = Fp⁶/w²-v
*/
const BN_X = BigInt('4965661367192848881');
const BN_X_LEN = (0, utils_js_1.bitLen)(BN_X);
const SIX_X_SQUARED = _6n * BN_X ** _2n;
// Finite field over r. It's for convenience and is not used in the code below.
const Fr = (0, modular_js_1.Field)(BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'));
// Fp2.div(Fp2.mul(Fp2.ONE, _3n), Fp2.NONRESIDUE)
const Fp2B = {
c0: BigInt('19485874751759354771024239261021720505790618469301721065564631296452457478373'),
c1: BigInt('266929791119991161246907387137283842545076965332900288569378510910307636690'),
};
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = (0, tower_js_1.tower12)({
ORDER: BigInt('21888242871839275222246405745257275088696311157297823662689037894645226208583'),
FP2_NONRESIDUE: [BigInt(9), _1n],
Fp2mulByB: (num) => Fp2.mul(num, Fp2B),
// The result of any pairing is in a cyclotomic subgroup
// https://eprint.iacr.org/2009/565.pdf
Fp12cyclotomicSquare: ({ c0, c1 }) => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1);
const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2);
const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {
c0: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
}), // 2 * (T7 - c0c2) + T7
c1: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
}),
}; // 2 * (T6 + c1c2) + T6
},
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;
for (let i = BN_X_LEN - 1; i >= 0; i--) {
z = Fp12._cyclotomicSquare(z);
if ((0, utils_js_1.bitGet)(n, i))
z = Fp12.mul(z, num);
}
return z;
},
// https://eprint.iacr.org/2010/354.pdf
// https://eprint.iacr.org/2009/565.pdf
Fp12finalExponentiate: (num) => {
const powMinusX = (num) => Fp12.conjugate(Fp12._cyclotomicExp(num, BN_X));
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num));
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0);
const y1 = Fp12._cyclotomicSquare(powMinusX(r));
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1);
const y4 = powMinusX(y2);
const y6 = powMinusX(Fp12._cyclotomicSquare(y4));
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2));
const y9 = Fp12.mul(y8, y1);
return Fp12.mul(Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3), Fp12.mul(Fp12.frobeniusMap(y8, 2), Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r))));
},
});
// END OF CURVE FIELDS
const { G2psi, psi } = (0, tower_js_1.psiFrobenius)(Fp, Fp2, Fp2.NONRESIDUE);
/*
No hashToCurve for now (and signatures):
- RFC 9380 doesn't mention bn254 and doesn't provide test vectors
- Overall seems like nobody is using BLS signatures on top of bn254
- Seems like it can utilize SVDW, which is not implemented yet
*/
const htfDefaults = Object.freeze({
// DST: a domain separation tag defined in section 2.2.5
DST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
encodeDST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
p: Fp.ORDER,
m: 2,
k: 128,
expand: 'xmd',
hash: sha256_1.sha256,
});
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
* so the naming has been adjusted to its prime bit count
* https://hal.science/hal-01534101/file/main.pdf
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
* Contains G1 / G2 operations and pairings.
*/
exports.bn254 = (0, weierstrass_js_1.weierstrass)({
exports.bn254 = (0, bls_js_1.bls)({
// Fields
fields: { Fp, Fp2, Fp6, Fp12, Fr },
G1: {
Fp,
h: BigInt(1),
Gx: BigInt(1),
Gy: BigInt(2),
a: Fp.ZERO,
b: _3n,
htfDefaults: { ...htfDefaults, m: 1, DST: 'BN254G2_XMD:SHA-256_SVDW_RO_' },
wrapPrivateKey: true,
allowInfinityPoint: true,
mapToCurve: utils_js_1.notImplemented,
fromBytes: utils_js_1.notImplemented,
toBytes: utils_js_1.notImplemented,
ShortSignature: {
fromHex: utils_js_1.notImplemented,
toRawBytes: utils_js_1.notImplemented,
toHex: utils_js_1.notImplemented,
},
},
G2: {
Fp: Fp2,
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1
h: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
Gx: Fp2.fromBigTuple([
BigInt('10857046999023057135944570762232829481370756359578518086990519993285655852781'),
BigInt('11559732032986387107991004021392285783925812861821192530917403151452391805634'),
]),
Gy: Fp2.fromBigTuple([
BigInt('8495653923123431417604973247489272438418190587263600148770280649306958101930'),
BigInt('4082367875863433681332203403145435568316851327593401208105741076214120093531'),
]),
a: Fp2.ZERO,
b: Fp2B,
hEff: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
htfDefaults: { ...htfDefaults },
wrapPrivateKey: true,
allowInfinityPoint: true,
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), // [p]P = [6X^2]P
mapToCurve: utils_js_1.notImplemented,
fromBytes: utils_js_1.notImplemented,
toBytes: utils_js_1.notImplemented,
Signature: {
fromHex: utils_js_1.notImplemented,
toRawBytes: utils_js_1.notImplemented,
toHex: utils_js_1.notImplemented,
},
},
params: {
ateLoopSize: BN_X * _6n + _2n,
r: Fr.ORDER,
xNegative: false,
twistType: 'divisive',
},
htfDefaults,
hash: sha256_1.sha256,
randomBytes: utils_1.randomBytes,
postPrecompute: (Rx, Ry, Rz, Qx, Qy, pointAdd) => {
const q = psi(Qx, Qy);
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
const q2 = psi(q[0], q[1]);
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1]));
},
});
/**
* bn254 weierstrass curve with ECDSA.
* This is very rare and probably not used anywhere.
* Instead, you should use G1 / G2, defined above.
*/
exports.bn254_weierstrass = (0, weierstrass_js_1.weierstrass)({
a: BigInt(0),
b: BigInt(3),
Fp: (0, modular_js_1.Field)(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
Fp,
n: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
Gx: BigInt(1),

@@ -22,0 +219,0 @@ Gy: BigInt(2),

import { AffinePoint, Group } from './abstract/curve.js';
import { ExtPointType } from './abstract/edwards.js';
import { CurveFn, ExtPointType } from './abstract/edwards.js';
import { htfBasicOpts } from './abstract/hash-to-curve.js';
import { Hex } from './abstract/utils.js';
export declare const ED25519_TORSION_SUBGROUP: string[];
export declare const ed25519: import("./abstract/edwards.js").CurveFn;
export declare const ed25519ctx: import("./abstract/edwards.js").CurveFn;
export declare const ed25519ph: import("./abstract/edwards.js").CurveFn;
/**
* ed25519 curve with EdDSA signatures.
*/
export declare const ed25519: CurveFn;
export declare const ed25519ctx: CurveFn;
export declare const ed25519ph: CurveFn;
export declare const x25519: import("./abstract/montgomery.js").CurveFn;

@@ -10,0 +13,0 @@ /**

@@ -114,2 +114,5 @@ "use strict";

}))();
/**
* ed25519 curve with EdDSA signatures.
*/
exports.ed25519 = (() => (0, edwards_js_1.twistedEdwards)(ed25519Defaults))();

@@ -116,0 +119,0 @@ function ed25519_domain(data, ctx, phflag) {

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { AffinePoint } from './curve.js';
import { IField } from './modular.js';

@@ -7,5 +6,8 @@ import { Hex, PrivKey, CHash } from './utils.js';

import { CurvePointsType, ProjPointType as ProjPointType, CurvePointsRes } from './weierstrass.js';
import type { Fp2, Fp6, Fp12, Fp2Bls, Fp12Bls } from './tower.js';
/**
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
* Implements BLS (Boneh-Lynn-Shacham) signatures.
* BLS != BLS.
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
* families of pairing-friendly curves.
* Consists of two curves: G1 and G2:

@@ -17,6 +19,8 @@ * - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.

* Pairing is used to aggregate and verify signatures.
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
* Some projects may prefer to swap this relation, it is not supported for now.
* There are two main ways to use it:
* 1. Fp for short private keys, Fp₂ for signatures
* 2. Fp for short signatures, Fp₂ for private keys
**/
type Fp = bigint;
export type TwistType = 'multiplicative' | 'divisive';
export type ShortSignatureCoder<Fp> = {

@@ -27,22 +31,8 @@ fromHex(hex: Hex): ProjPointType<Fp>;

};
export type SignatureCoder<Fp2> = {
fromHex(hex: Hex): ProjPointType<Fp2>;
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
export type SignatureCoder<Fp> = {
fromHex(hex: Hex): ProjPointType<Fp>;
toRawBytes(point: ProjPointType<Fp>): Uint8Array;
toHex(point: ProjPointType<Fp>): string;
};
type Fp2Bls<Fp, Fp2> = IField<Fp2> & {
reim: (num: Fp2) => {
re: Fp;
im: Fp;
};
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
type Fp12Bls<Fp2, Fp12> = IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
export type CurveType = {
G1: Omit<CurvePointsType<Fp>, 'n'> & {

@@ -61,9 +51,11 @@ ShortSignature: SignatureCoder<Fp>;

Fr: IField<bigint>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
};
params: {
x: bigint;
ateLoopSize: bigint;
xNegative: boolean;
r: bigint;
twistType: TwistType;
};

@@ -73,4 +65,11 @@ htfDefaults: HTFOpts;

randomBytes: (bytesLength?: number) => Uint8Array;
postPrecompute?: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2, pointAdd: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) => {
Rx: Fp2;
Ry: Fp2;
Rz: Fp2;
}) => void;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
type Precompute = PrecomputeSingle[];
export type CurveFn = {
getPublicKey: (privateKey: PrivKey) => Uint8Array;

@@ -101,4 +100,8 @@ getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;

};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
pairingBatch: (pairs: {
g1: ProjPointType<Fp>;
g2: ProjPointType<Fp2>;
}[], withFinalExponent?: boolean) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof createHasher<Fp>>;

@@ -109,3 +112,3 @@ G2: CurvePointsRes<Fp2> & ReturnType<typeof createHasher<Fp2>>;

params: {
x: bigint;
ateLoopSize: bigint;
r: bigint;

@@ -117,5 +120,5 @@ G1b: bigint;

Fp: IField<Fp>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
Fr: IField<bigint>;

@@ -125,7 +128,7 @@ };

randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
calcPairingPrecomputes: (p: ProjPointType<Fp2>) => Precompute;
};
};
export declare function bls<Fp2, Fp6, Fp12>(CURVE: CurveType<Fp, Fp2, Fp6, Fp12>): CurveFn<Fp, Fp2, Fp6, Fp12>;
export declare function bls(CURVE: CurveType): CurveFn;
export {};
//# sourceMappingURL=bls.d.ts.map

@@ -0,3 +1,6 @@

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
// TODO: import { AffinePoint } from './curve.js';
import { getMinHashLength, mapHashToField } from './modular.js';
import { bitLen, bitGet, ensureBytes } from './utils.js';
import { ensureBytes, memoized } from './utils.js';
// prettier-ignore

@@ -7,69 +10,146 @@ import { createHasher } from './hash-to-curve.js';

// prettier-ignore
const _2n = BigInt(2), _3n = BigInt(3);
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// Not used with BLS12-381 (no sequential `11` in X). Useful for other curves.
function NAfDecomposition(a) {
const res = [];
// a>1 because of marker bit
for (; a > _1n; a >>= _1n) {
if ((a & _1n) === _0n)
res.unshift(0);
else if ((a & _3n) === _3n) {
res.unshift(-1);
a += _1n;
}
else
res.unshift(1);
}
return res;
}
export function bls(CURVE) {
// Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
const BLS_X_LEN = bitLen(CURVE.params.x);
const BLS_X_IS_NEGATIVE = CURVE.params.xNegative;
const TWIST = CURVE.params.twistType;
// Point on G1 curve: (x, y)
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
const G1 = Object.assign(G1_, createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
}));
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(G2_, createHasher(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
}));
// Applies sparse multiplication as line function
let lineFunction;
if (TWIST === 'multiplicative') {
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul014(f, c0, Fp2.mul(c1, Px), Fp2.mul(c2, Py));
}
else if (TWIST === 'divisive') {
// NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
// precompute calculations.
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul034(f, Fp2.mul(c2, Py), Fp2.mul(c1, Px), c0);
}
else
throw new Error('bls: unknown twist type');
const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n));
function pointDouble(ell, Rx, Ry, Rz) {
const t0 = Fp2.sqr(Ry); // Ry²
const t1 = Fp2.sqr(Rz); // Rz²
const t2 = Fp2.mulByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
const t3 = Fp2.mul(t2, _3n); // 3 * T2
const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
const c1 = Fp2.mul(Fp2.sqr(Rx), _3n); // 3 * Rx²
const c2 = Fp2.neg(t4); // -T4 (-h)
ell.push([c0, c1, c2]);
Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
return { Rx, Ry, Rz };
}
function pointAdd(ell, Rx, Ry, Rz, Qx, Qy) {
// Addition
const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
const c2 = t1; // == Rx - Qx * Rz
ell.push([c0, c1, c2]);
const t2 = Fp2.sqr(t1); // T1²
const t3 = Fp2.mul(t2, t1); // T2 * T1
const t4 = Fp2.mul(t2, Rx); // T2 * Rx
const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
return { Rx, Ry, Rz };
}
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
function calcPairingPrecomputes(p) {
const { x, y } = p;
// pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
// add + double in windowed precomputes here, otherwise it would be single op (since X is static)
const ATE_NAF = NAfDecomposition(CURVE.params.ateLoopSize);
const calcPairingPrecomputes = memoized((point) => {
const p = point;
const { x, y } = p.toAffine();
// prettier-ignore
const Qx = x, Qy = y, Qz = Fp2.ONE;
const Qx = x, Qy = y, negQy = Fp2.neg(y);
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Qz;
let ell_coeff = [];
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
// Double
let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, _3n); // 3 * T2
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([
Fp2.sub(t2, t0), // T2 - T0
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
Fp2.neg(t4), // -T4
]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
if (bitGet(CURVE.params.x, i)) {
// Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
ell_coeff.push([
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
Fp2.neg(t0), // -T0
t1, // T1
]);
let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
}
let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
const ell = [];
for (const bit of ATE_NAF) {
const cur = [];
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
if (bit)
({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
ell.push(cur);
}
return ell_coeff;
}
function millerLoop(ell, g1) {
const { x } = CURVE.params;
const Px = g1[0];
const Py = g1[1];
if (CURVE.postPrecompute) {
const last = ell[ell.length - 1];
CURVE.postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
}
return ell;
});
function millerLoopBatch(pairs, withFinalExponent = false) {
let f12 = Fp12.ONE;
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
const E = ell[j];
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
if (bitGet(x, i)) {
j += 1;
const F = ell[j];
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
if (pairs.length) {
const ellLen = pairs[0][0].length;
for (let i = 0; i < ellLen; i++) {
f12 = Fp12.sqr(f12); // This allows us to do sqr only one time for all pairings
// NOTE: we apply multiple pairings in parallel here
for (const [ell, Px, Py] of pairs) {
for (const [c0, c1, c2] of ell[i])
f12 = lineFunction(c0, c1, c2, f12, Px, Py);
}
}
if (i !== 0)
f12 = Fp12.sqr(f12);
}
return Fp12.conjugate(f12);
if (BLS_X_IS_NEGATIVE)
f12 = Fp12.conjugate(f12);
return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
}
// Calculates product of multiple pairings
// This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
function pairingBatch(pairs, withFinalExponent = true) {
const res = [];
// This cache precomputed toAffine for all points
G1.ProjectivePoint.normalizeZ(pairs.map(({ g1 }) => g1));
G2.ProjectivePoint.normalizeZ(pairs.map(({ g2 }) => g2));
for (const { g1, g2 } of pairs) {
if (g1.equals(G1.ProjectivePoint.ZERO) || g2.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
// This uses toAffine inside
g1.assertValidity();
g2.assertValidity();
const Qa = g1.toAffine();
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
}
return millerLoopBatch(res, withFinalExponent);
}
// Calculates bilinear pairing
function pairing(Q, P, withFinalExponent = true) {
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
}
const utils = {

@@ -82,39 +162,4 @@ randomPrivateKey: () => {

};
// Point on G1 curve: (x, y)
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
const G1 = Object.assign(G1_, createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
}));
function pairingPrecomputes(point) {
const p = point;
if (p._PPRECOMPUTES)
return p._PPRECOMPUTES;
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
return p._PPRECOMPUTES;
}
// TODO: export
// function clearPairingPrecomputes(point: G2) {
// const p = point as G2 & withPairingPrecomputes;
// p._PPRECOMPUTES = undefined;
// }
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(G2_, createHasher(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
}));
const { ShortSignature } = CURVE.G1;
const { Signature } = CURVE.G2;
// Calculates bilinear pairing
function pairing(Q, P, withFinalExponent = true) {
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
Q.assertValidity();
P.assertValidity();
// Performance: 9ms for millerLoop and ~14ms for exp.
const Qa = Q.toAffine();
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
}
function normP1(point) {

@@ -169,7 +214,6 @@ return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);

const S = normP2(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P.negate(), Hm, false);
const eGS = pairing(G, S, false);
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
const exp = pairingBatch([
{ g1: P.negate(), g2: Hm }, // ePHM = pairing(P.negate(), Hm, false);
{ g1: G, g2: S }, // eGS = pairing(G, S, false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -184,7 +228,6 @@ }

const S = normP1(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const eHmP = pairing(Hm, P, false);
const eSG = pairing(S, G.negate(), false);
const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
const exp = pairingBatch([
{ g1: Hm, g2: P }, // eHmP = pairing(Hm, P, false);
{ g1: S, g2: G.negate() }, // eSG = pairing(S, G.negate(), false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -228,5 +271,5 @@ }

// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
function verifyBatch(signature, messages, publicKeys, htfOpts) {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
function verifyBatch(signature,
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
messages, publicKeys, htfOpts) {
if (!messages.length)

@@ -239,14 +282,22 @@ throw new Error('Expected non-empty messages array');

const nPublicKeys = publicKeys.map(normP1);
// NOTE: this works only for exact same object
const messagePubKeyMap = new Map();
for (let i = 0; i < nPublicKeys.length; i++) {
const pub = nPublicKeys[i];
const msg = nMessages[i];
let keys = messagePubKeyMap.get(msg);
if (keys === undefined) {
keys = [];
messagePubKeyMap.set(msg, keys);
}
keys.push(pub);
}
const paired = [];
try {
const paired = [];
for (const message of new Set(nMessages)) {
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
// Possible to batch pairing for same msg with different groupPublicKey here
paired.push(pairing(groupPublicKey, message, false));
for (const [msg, keys] of messagePubKeyMap) {
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
paired.push({ g1: groupPublicKey, g2: msg });
}
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
const exp = Fp12.finalExponentiate(product);
return Fp12.eql(exp, Fp12.ONE);
paired.push({ g1: G1.ProjectivePoint.BASE.negate(), g2: sig });
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
}

@@ -269,4 +320,5 @@ catch {

aggregateShortSignatures,
millerLoop,
millerLoopBatch,
pairing,
pairingBatch,
G1,

@@ -284,3 +336,3 @@ G2,

params: {
x: CURVE.params.x,
ateLoopSize: CURVE.params.ateLoopSize,
r: CURVE.params.r,

@@ -287,0 +339,0 @@ G1b: CURVE.G1.b,

@@ -48,6 +48,7 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

};
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): {
wNAFCached(P: T, n: bigint, transform: Mapper<T>): {
p: T;
f: T;
};
setWindowSize(P: T, W: number): void;
};

@@ -54,0 +55,0 @@ export type BasicCurve<T> = {

@@ -7,2 +7,6 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

const _1n = BigInt(1);
// Since points in different groups cannot be equal (different object constructor),
// we can have single place to store precomputes
const pointPrecomputes = new WeakMap();
const pointWindowSizes = new WeakMap(); // This allows use make points immutable (nothing changes inside)
// Elliptic curve multiplication of Point by scalar. Fragile.

@@ -24,3 +28,8 @@ // Scalars should always be less than curve order: this should be checked inside of a curve itself.

};
const validateW = (W) => {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
};
const opts = (W) => {
validateW(W);
const windows = Math.ceil(bits / W) + 1; // +1, because

@@ -125,15 +134,21 @@ const windowSize = 2 ** (W - 1); // -1 because we skip zero

},
wNAFCached(P, precomputesMap, n, transform) {
// @ts-ignore
const W = P._WINDOW_SIZE || 1;
wNAFCached(P, n, transform) {
const W = pointWindowSizes.get(P) || 1;
// Calculate precomputes on a first run, reuse them after
let comp = precomputesMap.get(P);
let comp = pointPrecomputes.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W);
if (W !== 1) {
precomputesMap.set(P, transform(comp));
}
if (W !== 1)
pointPrecomputes.set(P, transform(comp));
}
return this.wNAF(W, comp, n);
},
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
setWindowSize(P, W) {
validateW(W);
pointWindowSizes.set(P, W);
pointPrecomputes.delete(P);
},
};

@@ -140,0 +155,0 @@ }

@@ -65,2 +65,6 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}
/**
* Edwards Curve interface.
* Main methods: `getPublicKey(priv)`, `sign(msg, priv)`, `verify(sig, msg, pub)`.
*/
export type CurveFn = {

@@ -88,4 +92,11 @@ CURVE: ReturnType<typeof validateOpts>;

};
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
export declare function twistedEdwards(curveDef: CurveType): CurveFn;
export {};
//# sourceMappingURL=edwards.d.ts.map

@@ -6,3 +6,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import * as ut from './utils.js';
import { ensureBytes } from './utils.js';
import { ensureBytes, memoized, abool } from './utils.js';
// Be friendly to bad ECMAScript parsers by not using bigint literals

@@ -29,3 +29,9 @@ // prettier-ignore

}
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
export function twistedEdwards(curveDef) {

@@ -49,2 +55,3 @@ const CURVE = validateOpts(curveDef);

((data, ctx, phflag) => {
abool('phflag', phflag);
if (ctx.length || phflag)

@@ -54,20 +61,50 @@ throw new Error('Contexts/pre-hash are not supported');

}); // NOOP
const inBig = (n) => typeof n === 'bigint' && _0n < n; // n in [1..]
const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
function assertInRange(n, max) {
// n in [1..max-1]
if (inRange(n, max))
return n;
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
// 0 <= n < MASK
// Coordinates larger than Fp.ORDER are allowed for zip215
function aCoordinate(title, n) {
ut.aInRange('coordinate ' + title, n, _0n, MASK);
}
function assertGE0(n) {
// n in [0..CURVE_ORDER-1]
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
}
const pointPrecomputes = new Map();
function isPoint(other) {
function assertPoint(other) {
if (!(other instanceof Point))
throw new Error('ExtendedPoint expected');
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
const toAffineMemo = memoized((p, iz) => {
const { ex: x, ey: y, ez: z } = p;
const is0 = p.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
const assertValidMemo = memoized((p) => {
const { a, d } = CURVE;
if (p.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = p;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
return true;
});
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).

@@ -81,10 +118,7 @@ // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates

this.et = et;
if (!in0MaskRange(ex))
throw new Error('x required');
if (!in0MaskRange(ey))
throw new Error('y required');
if (!in0MaskRange(ez))
throw new Error('z required');
if (!in0MaskRange(et))
throw new Error('t required');
aCoordinate('x', ex);
aCoordinate('y', ey);
aCoordinate('z', ez);
aCoordinate('t', et);
Object.freeze(this);
}

@@ -101,4 +135,4 @@ get x() {

const { x, y } = p || {};
if (!in0MaskRange(x) || !in0MaskRange(y))
throw new Error('invalid affine point');
aCoordinate('x', x);
aCoordinate('y', y);
return new Point(x, y, _1n, modP(x * y));

@@ -112,4 +146,3 @@ }

_setWindowSize(windowSize) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}

@@ -119,26 +152,7 @@ // Not required for fromHex(), which always creates valid points.

assertValidity() {
const { a, d } = CURVE;
if (this.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = this;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
assertValidMemo(this);
}
// Compare one point to another.
equals(other) {
isPoint(other);
assertPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;

@@ -184,3 +198,3 @@ const { ex: X2, ey: Y2, ez: Z2 } = other;

add(other) {
isPoint(other);
assertPoint(other);
const { a, d } = CURVE;

@@ -228,7 +242,9 @@ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;

wNAF(n) {
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}
// Constant-time multiplication.
multiply(scalar) {
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
const n = scalar;
ut.aInRange('scalar', n, _1n, CURVE_ORDER); // 1 <= scalar < L
const { p, f } = this.wNAF(n);
return Point.normalizeZ([p, f])[0];

@@ -241,3 +257,4 @@ }

multiplyUnsafe(scalar) {
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
const n = scalar;
ut.aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
if (n === _0n)

@@ -266,14 +283,3 @@ return I;

toAffine(iz) {
const { ex: x, ey: y, ez: z } = this;
const is0 = this.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -292,2 +298,3 @@ clearCofactor() {

hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
abool('zip215', zip215);
const normed = hex.slice(); // copy again, we'll manipulate it

@@ -297,12 +304,7 @@ const lastByte = hex[len - 1]; // select last byte

const y = ut.bytesToNumberLE(normed);
if (y === _0n) {
// y=0 is allowed
}
else {
// RFC8032 prohibits >= p, but ZIP215 doesn't
if (zip215)
assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
else
assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
}
// RFC8032 prohibits >= p, but ZIP215 doesn't
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
const max = zip215 ? MASK : Fp.ORDER;
ut.aInRange('pointHex.y', y, _0n, max);
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:

@@ -382,3 +384,3 @@ // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)

const s = modN(r + k * scalar); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
ut.aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));

@@ -393,2 +395,4 @@ return ensureBytes('result', res, nByteLength * 2); // 64-byte signature

msg = ensureBytes('message', msg);
if (zip215 !== undefined)
abool('zip215', zip215);
if (prehash)

@@ -395,0 +399,0 @@ msg = prehash(msg); // for ed25519ph, etc

@@ -65,2 +65,3 @@ export declare function mod(a: bigint, b: bigint): bigint;

export declare function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T;
export declare function FpLegendre(order: bigint): <T>(f: IField<T>, x: T) => T;
export declare function FpIsSquare<T>(f: IField<T>): (x: T) => boolean;

@@ -79,2 +80,5 @@ export declare function nLength(n: bigint, nBitLength?: number): {

* * c) Object.freeze
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you doing.
* @param ORDER prime positive bigint

@@ -81,0 +85,0 @@ * @param bitLen how many bits the field consumes

@@ -264,7 +264,14 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}
export function FpLegendre(order) {
// (a | p) ≡ 1 if a is a square (mod p), quadratic residue
// (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
// (a | p) ≡ 0 if a ≡ 0 (mod p)
const legendreConst = (order - _1n) / _2n; // Integer arithmetic
return (f, x) => f.pow(x, legendreConst);
}
// This function returns True whenever the value x is a square in the field F.
export function FpIsSquare(f) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
const legendre = FpLegendre(f.ORDER);
return (x) => {
const p = f.pow(x, legendreConst);
const p = legendre(f, x);
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);

@@ -287,2 +294,5 @@ };

* * c) Object.freeze
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you doing.
* @param ORDER prime positive bigint

@@ -289,0 +299,0 @@ * @param bitLen how many bits the field consumes

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { mod, pow } from './modular.js';
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
import { aInRange, bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject, } from './utils.js';
const _0n = BigInt(0);

@@ -47,8 +47,2 @@ const _1n = BigInt(1);

}
// Accepts 0 as well
function assertFieldElement(n) {
if (typeof n === 'bigint' && _0n <= n && n < P)
return n;
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
}
// x25519 from 4

@@ -63,7 +57,8 @@ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519

*/
function montgomeryLadder(pointU, scalar) {
const u = assertFieldElement(pointU);
function montgomeryLadder(u, scalar) {
aInRange('u', u, _0n, P);
aInRange('scalar', scalar, _0n, P);
// Section 5: Implementations MUST accept non-canonical values and process them as
// if they had been reduced modulo the field prime.
const k = assertFieldElement(scalar);
const k = scalar;
const x_1 = u;

@@ -70,0 +65,0 @@ let x_2 = _1n;

@@ -14,2 +14,3 @@ export type Hex = Uint8Array | string;

export declare function abytes(item: unknown): void;
export declare function abool(title: string, value: boolean): void;
/**

@@ -49,3 +50,10 @@ * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'

export declare function utf8ToBytes(str: string): Uint8Array;
export declare function inRange(n: bigint, min: bigint, max: bigint): boolean;
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
export declare function aInRange(title: string, n: bigint, min: bigint, max: bigint): void;
/**
* Calculates amount of bits in a bigint.

@@ -95,3 +103,12 @@ * Same as `n.toString(2).length`

export declare function validateObject<T extends Record<string, any>>(object: T, validators: ValMap<T>, optValidators?: ValMap<T>): T;
/**
* throws not implemented error
*/
export declare const notImplemented: () => never;
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
export declare function memoized<T extends object, R, O extends any[]>(fn: (arg: T, ...args: O) => R): (arg: T, ...args: O) => R;
export {};
//# sourceMappingURL=utils.d.ts.map

@@ -17,2 +17,6 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}
export function abool(title, value) {
if (typeof value !== 'boolean')
throw new Error(`${title} must be valid boolean, got "${value}".`);
}
// Array where index 0xf0 (240) is mapped to string 'f0'

@@ -160,2 +164,21 @@ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));

}
// Is positive bigint
const isPosBig = (n) => typeof n === 'bigint' && _0n <= n;
export function inRange(n, min, max) {
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
}
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
export function aInRange(title, n, min, max) {
// Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
// consider P=256n, min=0n, max=P
// - a for min=0 would require -1: `inRange('x', x, -1n, P)`
// - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
// - our way is the cleanest: `inRange('x', x, 0n, P)
if (!inRange(n, min, max))
throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`);
}
// Bit operations

@@ -291,2 +314,23 @@ /**

// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
/**
* throws not implemented error
*/
export const notImplemented = () => {
throw new Error('not implemented');
};
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
export function memoized(fn) {
const map = new WeakMap();
return (arg, ...args) => {
const val = map.get(arg);
if (val !== undefined)
return val;
const computed = fn(arg, ...args);
map.set(arg, computed);
return computed;
};
}
//# sourceMappingURL=utils.js.map

@@ -210,2 +210,9 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

};
/**
* Creates short weierstrass curve and ECDSA signature methods for it.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
*/
export declare function weierstrass(curveDef: CurveType): CurveFn;

@@ -212,0 +219,0 @@ /**

@@ -6,3 +6,9 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import * as ut from './utils.js';
import { ensureBytes } from './utils.js';
import { ensureBytes, memoized, abool } from './utils.js';
function validateSigVerOpts(opts) {
if (opts.lowS !== undefined)
abool('lowS', opts.lowS);
if (opts.prehash !== undefined)
abool('prehash', opts.prehash);
}
function validatePointOpts(curve) {

@@ -132,12 +138,8 @@ const opts = validateBasic(curve);

function isWithinCurveOrder(num) {
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
return ut.inRange(num, _1n, CURVE.n);
}
function assertGE(num) {
if (!isWithinCurveOrder(num))
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
}
// Validates if priv key is valid and converts it to bigint.
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
function normPrivateKeyToScalar(key) {
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
if (lengths && typeof key !== 'bigint') {

@@ -162,7 +164,6 @@ if (ut.isBytes(key))

if (wrapPrivateKey)
num = mod.mod(num, n); // disabled by default, enabled for BLS
assertGE(num); // num in range [1..N-1]
num = mod.mod(num, N); // disabled by default, enabled for BLS
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
return num;
}
const pointPrecomputes = new Map();
function assertPrjPoint(other) {

@@ -172,2 +173,49 @@ if (!(other instanceof Point))

}
// Memoized toAffine / validity check. They are heavy. Points are immutable.
// Converts Projective point to affine (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
// (x, y, z) ∋ (x=x/z, y=y/z)
const toAffineMemo = memoized((p, iz) => {
const { px: x, py: y, pz: z } = p;
// Fast-path for normalized points
if (Fp.eql(z, Fp.ONE))
return { x, y };
const is0 = p.is0();
// 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.
if (iz == null)
iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0)
return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE))
throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
// NOTE: on exception this will crash 'cached' and no value will be set.
// Otherwise true will be return
const assertValidMemo = memoized((p) => {
if (p.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = p.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y))
throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right))
throw new Error('bad point: equation left != right');
if (!p.isTorsionFree())
throw new Error('bad point: not in prime-order subgroup');
return true;
});
/**

@@ -189,2 +237,3 @@ * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)

throw new Error('z required');
Object.freeze(this);
}

@@ -236,26 +285,7 @@ // Does not validate if the point is on-curve.

_setWindowSize(windowSize) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}
// A point on curve is valid if it conforms to equation.
assertValidity() {
if (this.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(this.py))
return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = this.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y))
throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right))
throw new Error('bad point: equation left != right');
if (!this.isTorsionFree())
throw new Error('bad point: not in prime-order subgroup');
assertValidMemo(this);
}

@@ -387,6 +417,3 @@ hasEvenY() {

wNAF(n) {
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
});
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}

@@ -398,14 +425,14 @@ /**

*/
multiplyUnsafe(n) {
multiplyUnsafe(sc) {
ut.aInRange('scalar', sc, _0n, CURVE.n);
const I = Point.ZERO;
if (n === _0n)
if (sc === _0n)
return I;
assertGE(n); // Will throw on 0
if (n === _1n)
if (sc === _1n)
return this;
const { endo } = CURVE;
if (!endo)
return wnaf.unsafeLadder(this, n);
return wnaf.unsafeLadder(this, sc);
// Apply endomorphism
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
let k1p = I;

@@ -440,8 +467,7 @@ let k2p = I;

multiply(scalar) {
assertGE(scalar);
let n = scalar;
const { endo, n: N } = CURVE;
ut.aInRange('scalar', scalar, _1n, N);
let point, fake; // Fake point is used to const-time mult
const { endo } = CURVE;
if (endo) {
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
let { p: k1p, f: f1p } = this.wNAF(k1);

@@ -456,3 +482,3 @@ let { p: k2p, f: f2p } = this.wNAF(k2);

else {
const { p, f } = this.wNAF(n);
const { p, f } = this.wNAF(scalar);
point = p;

@@ -481,16 +507,3 @@ fake = f;

toAffine(iz) {
const { px: x, py: y, pz: z } = this;
const is0 = this.is0();
// 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.
if (iz == null)
iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0)
return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE))
throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -514,2 +527,3 @@ isTorsionFree() {

toRawBytes(isCompressed = true) {
abool('isCompressed', isCompressed);
this.assertValidity();

@@ -519,2 +533,3 @@ return toBytes(Point, this, isCompressed);

toHex(isCompressed = true) {
abool('isCompressed', isCompressed);
return ut.bytesToHex(this.toRawBytes(isCompressed));

@@ -549,2 +564,9 @@ }

}
/**
* Creates short weierstrass curve and ECDSA signature methods for it.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
*/
export function weierstrass(curveDef) {

@@ -555,5 +577,2 @@ const CURVE = validateOpts(curveDef);

const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
function isValidFieldElement(num) {
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
}
function modN(a) {

@@ -571,2 +590,3 @@ return mod.mod(a, CURVE_ORDER);

const cat = ut.concatBytes;
abool('isCompressed', isCompressed);
if (isCompressed) {

@@ -586,3 +606,3 @@ return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);

const x = ut.bytesToNumberBE(tail);
if (!isValidFieldElement(x))
if (!ut.inRange(x, _1n, Fp.ORDER))
throw new Error('Point is not on curve');

@@ -648,7 +668,4 @@ const y2 = weierstrassEquation(x); // y² = x³ + ax + b

assertValidity() {
// can use assertGE here
if (!isWithinCurveOrder(this.r))
throw new Error('r must be 0 < r < CURVE.n');
if (!isWithinCurveOrder(this.s))
throw new Error('s must be 0 < s < CURVE.n');
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
}

@@ -796,6 +813,3 @@ addRecoveryBit(recovery) {

function int2octets(num) {
if (typeof num !== 'bigint')
throw new Error('bigint expected');
if (!(_0n <= num && num < ORDER_MASK))
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
// works with order, can have different size than numToField!

@@ -817,2 +831,3 @@ return ut.numberToBytesBE(num, CURVE.nByteLength);

msgHash = ensureBytes('msgHash', msgHash);
validateSigVerOpts(opts);
if (prehash)

@@ -904,2 +919,3 @@ msgHash = ensureBytes('prehashed msgHash', hash(msgHash));

throw new Error('options.strict was renamed to lowS');
validateSigVerOpts(opts);
const { lowS, prehash } = opts;

@@ -906,0 +922,0 @@ let _sig = undefined;

import { CurveFn } from './abstract/bls.js';
import * as mod from './abstract/modular.js';
declare const Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, "isOdd">>>;
type Fp = bigint;
type BigintTuple = [bigint, bigint];
type Fp2 = {
c0: bigint;
c1: bigint;
};
type Fp2Utils = {
fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
reim: (num: Fp2) => {
re: bigint;
im: bigint;
};
mulByNonresidue: (num: Fp2) => Fp2;
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
declare const Fp2: mod.IField<Fp2> & Fp2Utils;
type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
type Fp6 = {
c0: Fp2;
c1: Fp2;
c2: Fp2;
};
type Fp6Utils = {
fromBigSix: (tuple: BigintSix) => Fp6;
mulByNonresidue: (num: Fp6) => Fp6;
frobeniusMap(num: Fp6, power: number): Fp6;
multiplyBy1(num: Fp6, b1: Fp2): Fp6;
multiplyBy01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
};
declare const Fp6: mod.IField<Fp6> & Fp6Utils;
type Fp12 = {
c0: Fp6;
c1: Fp6;
};
type BigintTwelve = [
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint,
bigint
];
type Fp12Utils = {
fromBigTwelve: (t: BigintTwelve) => Fp12;
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
multiplyByFp2(lhs: Fp12, rhs: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
_cyclotomicSquare(num: Fp12): Fp12;
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
};
declare const Fp12: mod.IField<Fp12> & Fp12Utils;
export declare const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12>;
export {};
export declare const bls12_381: CurveFn;
//# sourceMappingURL=bls12-381.d.ts.map

@@ -6,6 +6,7 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import * as mod from './abstract/modular.js';
import { bitGet, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes as concatB, ensureBytes, numberToBytesBE, } from './abstract/utils.js';
import { bitGet, bitLen, bytesToHex, bytesToNumberBE, concatBytes as concatB, ensureBytes, numberToBytesBE, } from './abstract/utils.js';
// Types
import { isogenyMap } from './abstract/hash-to-curve.js';
import { mapToCurveSimpleSWU, } from './abstract/weierstrass.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import { tower12, psiFrobenius } from './abstract/tower.js';
/*

@@ -46,496 +47,36 @@ bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:

const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
// prettier-ignore
const _8n = BigInt(8), _16n = BigInt(16);
/*
Embedding degree (k): 12
Seed (X): -15132376222941642752
Fr: (x⁴-x²+1)
Fp: ((x-1)² ⋅ r(x)/3+x)
(E/Fp): Y²=X³+4
(Eₜ/Fp²): Y² = X³+4(u+1) (M-type twist)
Ate loop size: X
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-1-u
- Fp¹²[w] = Fp⁶/w²-v
TODO: BLS & BN Fp/Fr can be constructed from seed.
*/
// The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = bitLen(BLS_X);
// CURVE FIELDS
// Finite field over p.
const Fp_raw = BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab');
const Fp = mod.Field(Fp_raw);
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
const Fp2Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp.add(c0, r0),
c1: Fp.add(c1, r1),
});
const Fp2Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp.sub(c0, r0),
c1: Fp.sub(c1, r1),
});
const Fp2Multiply = ({ c0, c1 }, rhs) => {
if (typeof rhs === 'bigint')
return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
const { c0: r0, c1: r1 } = rhs;
let t1 = Fp.mul(c0, r0); // c0 * o0
let t2 = Fp.mul(c1, r1); // c1 * o1
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
const o0 = Fp.sub(t1, t2);
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
return { c0: o0, c1: o1 };
};
const Fp2Square = ({ c0, c1 }) => {
const a = Fp.add(c0, c1);
const b = Fp.sub(c0, c1);
const c = Fp.add(c0, c0);
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
};
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
// G² - 1
// h2q
// NOTE: ORDER was wrong!
const FP2_ORDER = Fp_raw * Fp_raw;
const Fp2 = {
ORDER: FP2_ORDER,
BITS: bitLen(FP2_ORDER),
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
MASK: bitMask(bitLen(FP2_ORDER)),
ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
pow: (num, power) => mod.FpPow(Fp2, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
// Normalized
add: Fp2Add,
sub: Fp2Subtract,
mul: Fp2Multiply,
sqr: Fp2Square,
// NonNormalized stuff
addN: Fp2Add,
subN: Fp2Subtract,
mulN: Fp2Multiply,
sqrN: Fp2Square,
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
inv: ({ c0: a, c1: b }) => {
// We wish to find the multiplicative inverse of a nonzero
// element a + bu in Fp2. We leverage an identity
//
// (a + bu)(a - bu) = a² + b²
//
// which holds because u² = -1. This can be rewritten as
//
// (a + bu)(a - bu)/(a² + b²) = 1
//
// because a² + b² = 0 has no nonzero solutions for (a, b).
// This gives that (a - bu)/(a² + b²) is the inverse
// of (a + bu). Importantly, this can be computing using
// only a single inversion in Fp.
const factor = Fp.inv(Fp.create(a * a + b * b));
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
},
sqrt: (num) => {
if (Fp2.eql(num, Fp2.ZERO))
return Fp2.ZERO; // Algo doesn't handles this case
// TODO: Optimize this line. It's extremely slow.
// Speeding this up would boost aggregateSignatures.
// https://eprint.iacr.org/2012/685.pdf applicable?
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
const R = FP2_ROOTS_OF_UNITY;
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
if (!divisor)
throw new Error('No root');
const index = R.indexOf(divisor);
const root = R[index / 2];
if (!root)
throw new Error('Invalid root');
const x1 = Fp2.div(candidateSqrt, root);
const x2 = Fp2.neg(x1);
const { re: re1, im: im1 } = Fp2.reim(x1);
const { re: re2, im: im2 } = Fp2.reim(x2);
if (im1 > im2 || (im1 === im2 && re1 > re2))
return x1;
return x2;
},
// Same as sgn0_m_eq_2 in RFC 9380
isOdd: (x) => {
const { re: x0, im: x1 } = Fp2.reim(x);
const sign_0 = x0 % _2n;
const zero_0 = x0 === _0n;
const sign_1 = x1 % _2n;
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
},
// Bytes util
fromBytes(b) {
if (b.length !== Fp2.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
},
toBytes: ({ c0, c1 }) => concatB(Fp.toBytes(c0), Fp.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp.cmov(c0, r0, c),
c1: Fp.cmov(c1, r1, c),
}),
// Specific utils
// toString() {
// return `Fp2(${this.c0} + ${this.c1}×i)`;
// }
reim: ({ c0, c1 }) => ({ re: c0, im: c1 }),
// multiply by u + 1
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
multiplyByB: ({ c0, c1 }) => {
let t0 = Fp.mul(c0, _4n); // 4 * c0
let t1 = Fp.mul(c1, _4n); // 4 * c1
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
// Order of Fp
ORDER: BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'),
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
FP2_NONRESIDUE: [_1n, _1n],
Fp2mulByB: ({ c0, c1 }) => {
const t0 = Fp.mul(c0, _4n); // 4 * c0
const t1 = Fp.mul(c1, _4n); // 4 * c1
// (T0-T1) + (T0+T1)*i
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
},
fromBigTuple: (tuple) => {
if (tuple.length !== 2)
throw new Error('Invalid tuple');
const fps = tuple.map((n) => Fp.create(n));
return { c0: fps[0], c1: fps[1] };
},
frobeniusMap: ({ c0, c1 }, power) => ({
c0,
c1: Fp.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]),
}),
};
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
const FP2_FROBENIUS_COEFFICIENTS = [
BigInt('0x1'),
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
].map((item) => Fp.create(item));
// For Fp2 roots of unity.
const rv1 = BigInt('0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09');
// const ev1 =
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
// const ev2 =
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
// const ev3 =
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
// const ev4 =
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
// Eighth roots of unity, used for computing square roots in Fp2.
// To verify or re-calculate:
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
const FP2_ROOTS_OF_UNITY = [
[_1n, _0n],
[rv1, -rv1],
[_0n, _1n],
[rv1, rv1],
[-_1n, _0n],
[-rv1, rv1],
[_0n, -_1n],
[-rv1, -rv1],
].map((pair) => Fp2.fromBigTuple(pair));
const Fp6Add = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
c0: Fp2.add(c0, r0),
c1: Fp2.add(c1, r1),
c2: Fp2.add(c2, r2),
});
const Fp6Subtract = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
c0: Fp2.sub(c0, r0),
c1: Fp2.sub(c1, r1),
c2: Fp2.sub(c2, r2),
});
const Fp6Multiply = ({ c0, c1, c2 }, rhs) => {
if (typeof rhs === 'bigint') {
return {
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
};
}
const { c0: r0, c1: r1, c2: r2 } = rhs;
const t0 = Fp2.mul(c0, r0); // c0 * o0
const t1 = Fp2.mul(c1, r1); // c1 * o1
const t2 = Fp2.mul(c2, r2); // c2 * o2
return {
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
};
};
const Fp6Square = ({ c0, c1, c2 }) => {
let t0 = Fp2.sqr(c0); // c0²
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
let t4 = Fp2.sqr(c2); // c2²
return {
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
};
};
const Fp6 = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 3 * Fp2.BITS,
BYTES: 3 * Fp2.BYTES,
MASK: bitMask(3 * Fp2.BITS),
ZERO: { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO },
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
create: (num) => num,
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
sqrt: () => {
throw new Error('Not implemented');
},
// Do we need division by bigint at all? Should be done via order:
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp6, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
// Normalized
add: Fp6Add,
sub: Fp6Subtract,
mul: Fp6Multiply,
sqr: Fp6Square,
// NonNormalized stuff
addN: Fp6Add,
subN: Fp6Subtract,
mulN: Fp6Multiply,
sqrN: Fp6Square,
inv: ({ c0, c1, c2 }) => {
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
let t4 = Fp2.inv(Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0)));
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
},
// Bytes utils
fromBytes: (b) => {
if (b.length !== Fp6.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp2.fromBytes(b.subarray(0, Fp2.BYTES)),
c1: Fp2.fromBytes(b.subarray(Fp2.BYTES, 2 * Fp2.BYTES)),
c2: Fp2.fromBytes(b.subarray(2 * Fp2.BYTES)),
};
},
toBytes: ({ c0, c1, c2 }) => concatB(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
cmov: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) => ({
c0: Fp2.cmov(c0, r0, c),
c1: Fp2.cmov(c1, r1, c),
c2: Fp2.cmov(c2, r2, c),
}),
// Utils
// fromTriple(triple: [Fp2, Fp2, Fp2]) {
// return new Fp6(...triple);
// }
// toString() {
// return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`;
// }
fromBigSix: (t) => {
if (!Array.isArray(t) || t.length !== 6)
throw new Error('Invalid Fp6 usage');
return {
c0: Fp2.fromBigTuple(t.slice(0, 2)),
c1: Fp2.fromBigTuple(t.slice(2, 4)),
c2: Fp2.fromBigTuple(t.slice(4, 6)),
};
},
frobeniusMap: ({ c0, c1, c2 }, power) => ({
c0: Fp2.frobeniusMap(c0, power),
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]),
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]),
}),
mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 }),
// Sparse multiplication
multiplyBy1: ({ c0, c1, c2 }, b1) => ({
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
c1: Fp2.mul(c0, b1),
c2: Fp2.mul(c1, b1),
}),
// Sparse multiplication
multiplyBy01({ c0, c1, c2 }, b0, b1) {
let t0 = Fp2.mul(c0, b0); // c0 * b0
let t1 = Fp2.mul(c1, b1); // c1 * b1
return {
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
// (b0 + b1) * (c0 + c1) - T0 - T1
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
// (c0 + c2) * b0 - T0 + T1
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
};
},
multiplyByFp2: ({ c0, c1, c2 }, rhs) => ({
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
}),
};
const FP6_FROBENIUS_COEFFICIENTS_1 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x0'),
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[BigInt('0x0'), BigInt('0x1')],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x0'),
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
],
].map((pair) => Fp2.fromBigTuple(pair));
const FP6_FROBENIUS_COEFFICIENTS_2 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
BigInt('0x0'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
BigInt('0x0'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
BigInt('0x0'),
],
].map((pair) => Fp2.fromBigTuple(pair));
// The BLS parameter x for BLS12-381
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = bitLen(BLS_X);
const Fp12Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp6.add(c0, r0),
c1: Fp6.add(c1, r1),
});
const Fp12Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
c0: Fp6.sub(c0, r0),
c1: Fp6.sub(c1, r1),
});
const Fp12Multiply = ({ c0, c1 }, rhs) => {
if (typeof rhs === 'bigint')
return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
let { c0: r0, c1: r1 } = rhs;
let t1 = Fp6.mul(c0, r0); // c0 * r0
let t2 = Fp6.mul(c1, r1); // c1 * r1
return {
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
// (c0 + c1) * (r0 + r1) - (T1 + T2)
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
};
};
const Fp12Square = ({ c0, c1 }) => {
let ab = Fp6.mul(c0, c1); // c0 * c1
return {
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
c1: Fp6.add(ab, ab),
}; // AB + AB
};
function Fp4Square(a, b) {
const a2 = Fp2.sqr(a);
const b2 = Fp2.sqr(b);
return {
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
};
}
const Fp12 = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 2 * Fp2.BITS,
BYTES: 2 * Fp2.BYTES,
MASK: bitMask(2 * Fp2.BITS),
ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
sqrt: () => {
throw new Error('Not implemented');
},
inv: ({ c0, c1 }) => {
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
},
div: (lhs, rhs) => Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp12, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
// Normalized
add: Fp12Add,
sub: Fp12Subtract,
mul: Fp12Multiply,
sqr: Fp12Square,
// NonNormalized stuff
addN: Fp12Add,
subN: Fp12Subtract,
mulN: Fp12Multiply,
sqrN: Fp12Square,
// Bytes utils
fromBytes: (b) => {
if (b.length !== Fp12.BYTES)
throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
};
},
toBytes: ({ c0, c1 }) => concatB(Fp6.toBytes(c0), Fp6.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp6.cmov(c0, r0, c),
c1: Fp6.cmov(c1, r1, c),
}),
// Utils
// toString() {
// return `Fp12(${this.c0} + ${this.c1} * w)`;
// },
// fromTuple(c: [Fp6, Fp6]) {
// return new Fp12(...c);
// }
fromBigTwelve: (t) => ({
c0: Fp6.fromBigSix(t.slice(0, 6)),
c1: Fp6.fromBigSix(t.slice(6, 12)),
}),
// Raises to q**i -th power
frobeniusMap(lhs, power) {
const r0 = Fp6.frobeniusMap(lhs.c0, power);
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12];
return {
c0: r0,
c1: Fp6.create({
c0: Fp2.mul(c0, coeff),
c1: Fp2.mul(c1, coeff),
c2: Fp2.mul(c2, coeff),
}),
};
},
// Sparse multiplication
multiplyBy014: ({ c0, c1 }, o0, o1, o4) => {
let t0 = Fp6.multiplyBy01(c0, o0, o1);
let t1 = Fp6.multiplyBy1(c1, o4);
return {
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
// (c1 + c0) * [o0, o1+o4] - T0 - T1
c1: Fp6.sub(Fp6.sub(Fp6.multiplyBy01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
};
},
multiplyByFp2: ({ c0, c1 }, rhs) => ({
c0: Fp6.multiplyByFp2(c0, rhs),
c1: Fp6.multiplyByFp2(c1, rhs),
}),
conjugate: ({ c0, c1 }) => ({ c0, c1: Fp6.neg(c1) }),
// Fp12
// A cyclotomic group is a subgroup of Fp^n defined by

@@ -545,3 +86,3 @@ // GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}

// https://eprint.iacr.org/2009/565.pdf
_cyclotomicSquare: ({ c0, c1 }) => {
Fp12cyclotomicSquare: ({ c0, c1 }) => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;

@@ -552,3 +93,3 @@ const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;

const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {

@@ -567,3 +108,3 @@ c0: Fp6.create({

},
_cyclotomicExp(num, n) {
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;

@@ -579,3 +120,3 @@ for (let i = BLS_X_LEN - 1; i >= 0; i--) {

// https://eprint.iacr.org/2009/565.pdf
finalExponentiate: (num) => {
Fp12finalExponentiate: (num) => {
const x = BLS_X;

@@ -599,50 +140,6 @@ // this^(q⁶) / this

},
};
const FP12_FROBENIUS_COEFFICIENTS = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
BigInt('0x0'),
],
[
BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
],
[
BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
BigInt('0x0'),
],
[
BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
],
[
BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
BigInt('0x0'),
],
[
BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
BigInt('0x0'),
],
[
BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
],
[
BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
BigInt('0x0'),
],
[
BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
],
].map((n) => Fp2.fromBigTuple(n));
});
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
// END OF CURVE FIELDS

@@ -803,29 +300,3 @@ // HashToCurve

// Ψ(P) endomorphism
const ut_root = Fp6.create({ c0: Fp2.ZERO, c1: Fp2.ONE, c2: Fp2.ZERO });
const wsq = Fp12.create({ c0: ut_root, c1: Fp6.ZERO });
const wcu = Fp12.create({ c0: Fp6.ZERO, c1: ut_root });
const [wsq_inv, wcu_inv] = Fp12.invertBatch([wsq, wcu]);
function psi(x, y) {
// Untwist Fp2->Fp12 && frobenius(1) && twist back
const x2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wsq_inv, x), 1), wsq).c0.c0;
const y2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wcu_inv, y), 1), wcu).c0.c0;
return [x2, y2];
}
// Ψ endomorphism
function G2psi(c, P) {
const affine = P.toAffine();
const p = psi(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
// Ψ²(P) endomorphism
// 1 / F2(2)^((p-1)/3) in GF(p²)
const PSI2_C1 = BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac');
function psi2(x, y) {
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
}
function G2psi2(c, P) {
const affine = P.toAffine();
const p = psi2(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
// Default hash_to_field options are for hash to G2.

@@ -957,4 +428,4 @@ //

// todo: unroll
const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
return u2P.equals(phi);

@@ -977,3 +448,3 @@ // https://eprint.iacr.org/2019/814.pdf

// return this.multiplyUnsafe(CURVE.h);
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
},

@@ -1103,3 +574,3 @@ mapToCurve: (scalars) => {

isTorsionFree: (c, P) => {
return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
// Older version: https://eprint.iacr.org/2019/814.pdf

@@ -1114,3 +585,3 @@ // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O

clearCofactor: (c, P) => {
const x = bls12_381.params.x;
const x = BLS_X;
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P

@@ -1234,4 +705,6 @@ let t2 = G2psi(c, P); // Ψ(P)

params: {
x: BLS_X, // The BLS parameter x for BLS12-381
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
xNegative: true,
twistType: 'multiplicative',
},

@@ -1238,0 +711,0 @@ htfDefaults,

@@ -0,9 +1,13 @@

import { CurveFn } from './abstract/bls.js';
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
* so the naming has been adjusted to its prime bit count
* https://hal.science/hal-01534101/file/main.pdf
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
* Contains G1 / G2 operations and pairings.
*/
export declare const bn254: import("./abstract/weierstrass.js").CurveFn;
export declare const bn254: CurveFn;
/**
* bn254 weierstrass curve with ECDSA.
* This is very rare and probably not used anywhere.
* Instead, you should use G1 / G2, defined above.
*/
export declare const bn254_weierstrass: import("./abstract/weierstrass.js").CurveFn;
//# sourceMappingURL=bn254.d.ts.map
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256';
import { getHash } from './_shortw_utils.js';
import { weierstrass } from './abstract/weierstrass.js';
import { randomBytes } from '@noble/hashes/utils';
import { bls } from './abstract/bls.js';
import { Field } from './abstract/modular.js';
import { weierstrass } from './abstract/weierstrass.js';
import { bitGet, bitLen, notImplemented } from './abstract/utils.js';
import { tower12, psiFrobenius } from './abstract/tower.js';
// prettier-ignore
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// prettier-ignore
const _6n = BigInt(6);
/*
bn254, previously known as alt_bn_128, when it had 128-bit security.
Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
so the naming has been adjusted to its prime bit count:
https://hal.science/hal-01534101/file/main.pdf
There are huge compatibility issues in the ecosystem:
1. Different libraries call it in different ways: "bn254", "bn256", "alt_bn128", "bn128".
2. libff has bn128, but it's a different curve with different G2:
https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169
3. halo2curves bn256 is also incompatible and returns different outputs
The goal of our implementation is to support "Ethereum" variant of the curve,
because it at least has specs:
- EIP196 (https://eips.ethereum.org/EIPS/eip-196) describes bn254 ECADD and ECMUL opcodes for EVM
- EIP197 (https://eips.ethereum.org/EIPS/eip-197) describes bn254 pairings
- It's hard: EIPs don't have proper tests. EIP-197 returns boolean output instead of Fp12
- The existing implementations are bad. Some are deprecated:
- https://github.com/paritytech/bn (old version)
- https://github.com/ewasm/ethereum-bn128.rs (uses paritytech/bn)
- https://github.com/zcash-hackworks/bn
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
- Python implementations use different towers and produce different Fp12 outputs:
- https://github.com/ethereum/py_pairing
- https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
- Points are encoded differently in different implementations
*/
/*
Seed (X): 4965661367192848881
Fr: (36x⁴+36x³+18x²+6x+1)
Fp: (36x⁴+36x³+24x²+6x+1)
(E / Fp ): Y² = X³+3
(Et / Fp²): Y² = X³+3/(u+9) (D-type twist)
Ate loop size: 6x+2
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-9-u
- Fp¹²[w] = Fp⁶/w²-v
*/
const BN_X = BigInt('4965661367192848881');
const BN_X_LEN = bitLen(BN_X);
const SIX_X_SQUARED = _6n * BN_X ** _2n;
// Finite field over r. It's for convenience and is not used in the code below.
const Fr = Field(BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'));
// Fp2.div(Fp2.mul(Fp2.ONE, _3n), Fp2.NONRESIDUE)
const Fp2B = {
c0: BigInt('19485874751759354771024239261021720505790618469301721065564631296452457478373'),
c1: BigInt('266929791119991161246907387137283842545076965332900288569378510910307636690'),
};
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
ORDER: BigInt('21888242871839275222246405745257275088696311157297823662689037894645226208583'),
FP2_NONRESIDUE: [BigInt(9), _1n],
Fp2mulByB: (num) => Fp2.mul(num, Fp2B),
// The result of any pairing is in a cyclotomic subgroup
// https://eprint.iacr.org/2009/565.pdf
Fp12cyclotomicSquare: ({ c0, c1 }) => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1);
const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2);
const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {
c0: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
}), // 2 * (T7 - c0c2) + T7
c1: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
}),
}; // 2 * (T6 + c1c2) + T6
},
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;
for (let i = BN_X_LEN - 1; i >= 0; i--) {
z = Fp12._cyclotomicSquare(z);
if (bitGet(n, i))
z = Fp12.mul(z, num);
}
return z;
},
// https://eprint.iacr.org/2010/354.pdf
// https://eprint.iacr.org/2009/565.pdf
Fp12finalExponentiate: (num) => {
const powMinusX = (num) => Fp12.conjugate(Fp12._cyclotomicExp(num, BN_X));
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num));
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0);
const y1 = Fp12._cyclotomicSquare(powMinusX(r));
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1);
const y4 = powMinusX(y2);
const y6 = powMinusX(Fp12._cyclotomicSquare(y4));
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2));
const y9 = Fp12.mul(y8, y1);
return Fp12.mul(Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3), Fp12.mul(Fp12.frobeniusMap(y8, 2), Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r))));
},
});
// END OF CURVE FIELDS
const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE);
/*
No hashToCurve for now (and signatures):
- RFC 9380 doesn't mention bn254 and doesn't provide test vectors
- Overall seems like nobody is using BLS signatures on top of bn254
- Seems like it can utilize SVDW, which is not implemented yet
*/
const htfDefaults = Object.freeze({
// DST: a domain separation tag defined in section 2.2.5
DST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
encodeDST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
p: Fp.ORDER,
m: 2,
k: 128,
expand: 'xmd',
hash: sha256,
});
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
* so the naming has been adjusted to its prime bit count
* https://hal.science/hal-01534101/file/main.pdf
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
* Contains G1 / G2 operations and pairings.
*/
export const bn254 = weierstrass({
export const bn254 = bls({
// Fields
fields: { Fp, Fp2, Fp6, Fp12, Fr },
G1: {
Fp,
h: BigInt(1),
Gx: BigInt(1),
Gy: BigInt(2),
a: Fp.ZERO,
b: _3n,
htfDefaults: { ...htfDefaults, m: 1, DST: 'BN254G2_XMD:SHA-256_SVDW_RO_' },
wrapPrivateKey: true,
allowInfinityPoint: true,
mapToCurve: notImplemented,
fromBytes: notImplemented,
toBytes: notImplemented,
ShortSignature: {
fromHex: notImplemented,
toRawBytes: notImplemented,
toHex: notImplemented,
},
},
G2: {
Fp: Fp2,
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1
h: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
Gx: Fp2.fromBigTuple([
BigInt('10857046999023057135944570762232829481370756359578518086990519993285655852781'),
BigInt('11559732032986387107991004021392285783925812861821192530917403151452391805634'),
]),
Gy: Fp2.fromBigTuple([
BigInt('8495653923123431417604973247489272438418190587263600148770280649306958101930'),
BigInt('4082367875863433681332203403145435568316851327593401208105741076214120093531'),
]),
a: Fp2.ZERO,
b: Fp2B,
hEff: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
htfDefaults: { ...htfDefaults },
wrapPrivateKey: true,
allowInfinityPoint: true,
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), // [p]P = [6X^2]P
mapToCurve: notImplemented,
fromBytes: notImplemented,
toBytes: notImplemented,
Signature: {
fromHex: notImplemented,
toRawBytes: notImplemented,
toHex: notImplemented,
},
},
params: {
ateLoopSize: BN_X * _6n + _2n,
r: Fr.ORDER,
xNegative: false,
twistType: 'divisive',
},
htfDefaults,
hash: sha256,
randomBytes,
postPrecompute: (Rx, Ry, Rz, Qx, Qy, pointAdd) => {
const q = psi(Qx, Qy);
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
const q2 = psi(q[0], q[1]);
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1]));
},
});
/**
* bn254 weierstrass curve with ECDSA.
* This is very rare and probably not used anywhere.
* Instead, you should use G1 / G2, defined above.
*/
export const bn254_weierstrass = weierstrass({
a: BigInt(0),
b: BigInt(3),
Fp: Field(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
Fp,
n: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
Gx: BigInt(1),

@@ -19,0 +216,0 @@ Gy: BigInt(2),

import { AffinePoint, Group } from './abstract/curve.js';
import { ExtPointType } from './abstract/edwards.js';
import { CurveFn, ExtPointType } from './abstract/edwards.js';
import { htfBasicOpts } from './abstract/hash-to-curve.js';
import { Hex } from './abstract/utils.js';
export declare const ED25519_TORSION_SUBGROUP: string[];
export declare const ed25519: import("./abstract/edwards.js").CurveFn;
export declare const ed25519ctx: import("./abstract/edwards.js").CurveFn;
export declare const ed25519ph: import("./abstract/edwards.js").CurveFn;
/**
* ed25519 curve with EdDSA signatures.
*/
export declare const ed25519: CurveFn;
export declare const ed25519ctx: CurveFn;
export declare const ed25519ph: CurveFn;
export declare const x25519: import("./abstract/montgomery.js").CurveFn;

@@ -10,0 +13,0 @@ /**

@@ -109,2 +109,5 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}))();
/**
* ed25519 curve with EdDSA signatures.
*/
export const ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();

@@ -111,0 +114,0 @@ function ed25519_domain(data, ctx, phflag) {

@@ -5,2 +5,5 @@ import { mod } from './abstract/modular.js';

import { ProjPointType as PointType } from './abstract/weierstrass.js';
/**
* secp256k1 short weierstrass curve and ECDSA signatures over it.
*/
export declare const secp256k1: Readonly<{

@@ -77,2 +80,5 @@ create: (hash: import("./abstract/utils.js").CHash) => import("./abstract/weierstrass.js").CurveFn;

declare function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean;
/**
* Schnorr signatures over secp256k1.
*/
export declare const schnorr: {

@@ -79,0 +85,0 @@ getPublicKey: typeof schnorrGetPublicKey;

@@ -7,3 +7,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import { Field, mod, pow2 } from './abstract/modular.js';
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
import { inRange, aInRange, bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE, } from './abstract/utils.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';

@@ -44,2 +44,5 @@ const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');

const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
/**
* secp256k1 short weierstrass curve and ECDSA signatures over it.
*/
export const secp256k1 = createCurve({

@@ -90,4 +93,2 @@ a: BigInt(0), // equation params: a, b

const _0n = BigInt(0);
const fe = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
const ge = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */

@@ -123,4 +124,3 @@ const TAGGED_HASH_PREFIXES = {};

function lift_x(x) {
if (!fe(x))
throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
aInRange('x', x, _1n, secp256k1P); // Fail if x ≥ p.
const xx = modP(x * x);

@@ -135,2 +135,3 @@ const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.

}
const num = bytesToNumberBE;
/**

@@ -140,3 +141,3 @@ * Create tagged hash, convert it to bigint, reduce modulo-n.

function challenge(...args) {
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
return modN(num(taggedHash('BIP0340/challenge', ...args)));
}

@@ -157,5 +158,5 @@ /**

const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const t = numTo32b(d ^ num(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
const k_ = modN(num(rand)); // Let k' = int(rand) mod n
if (k_ === _0n)

@@ -182,8 +183,8 @@ throw new Error('sign failed: k is zero'); // Fail if k' = 0.

try {
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!fe(r))
const P = lift_x(num(pub)); // P = lift_x(int(pk)); fail if that fails
const r = num(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!inRange(r, _1n, secp256k1P))
return false;
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!ge(s))
const s = num(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!inRange(s, _1n, secp256k1N))
return false;

@@ -200,2 +201,5 @@ const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n

}
/**
* Schnorr signatures over secp256k1.
*/
export const schnorr = /* @__PURE__ */ (() => ({

@@ -202,0 +206,0 @@ getPublicKey: schnorrGetPublicKey,

{
"name": "@noble/curves",
"version": "1.4.2",
"version": "1.5.0",
"description": "Audited & minimal JS implementation of elliptic curve cryptography",

@@ -148,2 +148,3 @@ "files": [

"bn254",
"alt_bn128",
"bls",

@@ -150,0 +151,0 @@ "noble",

@@ -52,3 +52,3 @@ # noble-curves

- [Implementations](#implementations)
- [ECDSA signature scheme](#ecdsa-signature-scheme)
- [ECDSA signatures over secp256k1 and others](#ecdsa-signatures-over-secp256k1-and-others)
- [ECDSA public key recovery & extra entropy](#ecdsa-public-key-recovery--extra-entropy)

@@ -60,2 +60,3 @@ - [ECDH: Elliptic Curve Diffie-Hellman](#ecdh-elliptic-curve-diffie-hellman)

- [bls12-381](#bls12-381)
- [bn254 aka alt_bn128](#bn254-aka-alt_bn128)
- [All available imports](#all-available-imports)

@@ -67,3 +68,3 @@ - [Accessing a curve's variables](#accessing-a-curves-variables)

- [montgomery: Montgomery curve](#montgomery-montgomery-curve)
- [bls: Barreto-Lynn-Scott curves](#bls-barreto-lynn-scott-curves)
- [bls: Boneh-Lynn-Shacham signatures](#bls-boneh-lynn-shacham-signatures)
- [hash-to-curve: Hashing strings to curve points](#hash-to-curve-hashing-strings-to-curve-points)

@@ -85,8 +86,8 @@ - [poseidon: Poseidon hash](#poseidon-poseidon-hash)

#### ECDSA signature scheme
#### ECDSA signatures over secp256k1 and others
Generic example that works for all curves, shown for secp256k1:
```ts
import { secp256k1 } from '@noble/curves/secp256k1';
// import { p256 } from '@noble/curves/p256'; // or p384 / p521
const priv = secp256k1.utils.randomPrivateKey();

@@ -98,3 +99,3 @@ const pub = secp256k1.getPublicKey(priv);

// hex strings are also supported besides Uint8Arrays:
// hex strings are also supported besides Uint8Array-s:
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';

@@ -104,3 +105,3 @@ const pub2 = secp256k1.getPublicKey(privHex);

We support P256 (secp256r1), P384 (secp384r1), P521 (secp521r1).
The same code would work for NIST P256 (secp256r1), P384 (secp384r1) & P521 (secp521r1).

@@ -255,4 +256,66 @@ #### ECDSA public key recovery & extra entropy

```ts
import { bls12_381 as bls } from '@noble/curves/bls12-381';
// G1 keys, G2 signatures
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const message = '64726e3da8';
const publicKey = bls.getPublicKey(privateKey);
const signature = bls.sign(message, privateKey);
const isValid = bls.verify(signature, message, publicKey);
console.log({ publicKey, signature, isValid });
// G2 signatures, G1 keys
// getPublicKeyForShortSignatures(privateKey)
// signShortSignature(message, privateKey)
// verifyShortSignature(signature, message, publicKey)
// aggregateShortSignatures(signatures)
// Custom DST
const htfEthereum = { DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_' };
const signatureEth = bls.sign(message, privateKey, htfEthereum);
const isValidEth = bls.verify(signature, message, publicKey, htfEthereum);
// Aggregation
const aggregatedKey = bls.aggregatePublicKeys([bls.utils.randomPrivateKey(), bls.utils.randomPrivateKey()])
// const aggregatedSig = bls.aggregateSignatures(sigs)
// Pairings, with and without final exponentiation
// bls.pairing(PointG1, PointG2);
// bls.pairing(PointG1, PointG2, false);
// bls.fields.Fp12.finalExponentiate(bls.fields.Fp12.mul(PointG1, PointG2));
// Others
// bls.G1.ProjectivePoint.BASE, bls.G2.ProjectivePoint.BASE;
// bls.fields.Fp, bls.fields.Fp2, bls.fields.Fp12, bls.fields.Fr;
```
See [abstract/bls](#bls-barreto-lynn-scott-curves).
For example usage, check out [the implementation of BLS EVM precompiles](https://github.com/ethereumjs/ethereumjs-monorepo/blob/361f4edbc239e795a411ac2da7e5567298b9e7e5/packages/evm/src/precompiles/bls12_381/noble.ts).
#### bn254 aka alt_bn128
```ts
import { bn254 } from '@noble/curves/bn254';
console.log(
bn254.G1,
bn254.G2,
bn254.pairing
)
```
The API mirrors [BLS](#bls12-381). The curve was previously called alt_bn128.
The implementation is compatible with [EIP-196](https://eips.ethereum.org/EIPS/eip-196) and
[EIP-197](https://eips.ethereum.org/EIPS/eip-197).
Keep in mind that we don't implement Point methods toHex / toRawBytes. It's because
different implementations of bn254 do it differently - there is no standard. Points of divergence:
- Endianness: LE vs BE (byte-swapped)
- Flags as first hex bits (similar to BLS) vs no-flags
- Imaginary part last in G2 vs first (c0, c1 vs c1, c0)
For example usage, check out [the implementation of bn254 EVM precompiles](https://github.com/paulmillr/noble-curves/blob/3ed792f8ad9932765b84d1064afea8663a255457/test/bn254.test.js#L697).
#### All available imports

@@ -600,72 +663,8 @@

The module doesn't expose `CURVE` property: use `G1.CURVE`, `G2.CURVE` instead.
Only BLS12-381 is implemented currently.
Only BLS12-381 is currently implemented.
Defining BLS12-377 and BLS24 should be straightforward.
Main methods and properties are:
- `getPublicKey(privateKey)`
- `sign(message, privateKey)`
- `verify(signature, message, publicKey)`
- `aggregatePublicKeys(publicKeys)`
- `aggregateSignatures(signatures)`
- `G1` and `G2` curves containing `CURVE` and `ProjectivePoint`
- `Signature` property with `fromHex`, `toHex` methods
- `fields` containing `Fp`, `Fp2`, `Fp6`, `Fp12`, `Fr`
The default BLS uses short public keys (with public keys in G1 and signatures in G2).
Short signatures (public keys in G2 and signatures in G1) is also supported, using:
Short signatures (public keys in G2 and signatures in G1) are also supported.
- `getPublicKeyForShortSignatures(privateKey)`
- `signShortSignature(message, privateKey)`
- `verifyShortSignature(signature, message, publicKey)`
- `aggregateShortSignatures(signatures)`
```ts
import { bls12_381 as bls } from '@noble/curves/bls12-381';
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const message = '64726e3da8';
const publicKey = bls.getPublicKey(privateKey);
const signature = bls.sign(message, privateKey);
const isValid = bls.verify(signature, message, publicKey);
console.log({ publicKey, signature, isValid });
// Use custom DST, e.g. for Ethereum consensus layer
const htfEthereum = { DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_' };
const signatureEth = bls.sign(message, privateKey, htfEthereum);
const isValidEth = bls.verify(signature, message, publicKey, htfEthereum);
console.log({ signatureEth, isValidEth });
// Sign 1 msg with 3 keys
const privateKeys = [
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5',
];
const messages = ['d2', '0d98', '05caf3'];
const publicKeys = privateKeys.map(bls.getPublicKey);
const signatures2 = privateKeys.map((p) => bls.sign(message, p));
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
const aggSignature2 = bls.aggregateSignatures(signatures2);
const isValid2 = bls.verify(aggSignature2, message, aggPubKey2);
console.log({ signatures2, aggSignature2, isValid2 });
// Sign 3 msgs with 3 keys
const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
const aggSignature3 = bls.aggregateSignatures(signatures3);
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
// Pairings, with and without final exponentiation
bls.pairing(PointG1, PointG2);
bls.pairing(PointG1, PointG2, false);
bls.fields.Fp12.finalExponentiate(bls.fields.Fp12.mul(PointG1, PointG2));
// Others
bls.G1.ProjectivePoint.BASE, bls.G2.ProjectivePoint.BASE;
bls.fields.Fp, bls.fields.Fp2, bls.fields.Fp12, bls.fields.Fr;
bls.params.x, bls.params.r, bls.params.G1b, bls.params.G2b;
// hash-to-curve examples can be seen below
```
### hash-to-curve: Hashing strings to curve points

@@ -882,3 +881,3 @@

Benchmark results on Apple M2 with node v20:
Benchmark results on Apple M2 with node v22:

@@ -888,65 +887,78 @@ ```

init x 68 ops/sec @ 14ms/op
getPublicKey x 6,750 ops/sec @ 148μs/op
sign x 5,206 ops/sec @ 192μs/op
verify x 880 ops/sec @ 1ms/op
getSharedSecret x 536 ops/sec @ 1ms/op
recoverPublicKey x 852 ops/sec @ 1ms/op
schnorr.sign x 685 ops/sec @ 1ms/op
schnorr.verify x 908 ops/sec @ 1ms/op
getPublicKey x 6,839 ops/sec @ 146μs/op
sign x 5,226 ops/sec @ 191μs/op
verify x 893 ops/sec @ 1ms/op
getSharedSecret x 538 ops/sec @ 1ms/op
recoverPublicKey x 923 ops/sec @ 1ms/op
schnorr.sign x 700 ops/sec @ 1ms/op
schnorr.verify x 919 ops/sec @ 1ms/op
ed25519
init x 51 ops/sec @ 19ms/op
getPublicKey x 9,809 ops/sec @ 101μs/op
sign x 4,976 ops/sec @ 200μs/op
verify x 1,018 ops/sec @ 981μs/op
ed448
init x 19 ops/sec @ 50ms/op
getPublicKey x 3,723 ops/sec @ 268μs/op
sign x 1,759 ops/sec @ 568μs/op
verify x 344 ops/sec @ 2ms/op
p256
init x 38 ops/sec @ 26ms/op
getPublicKey x 6,530 ops/sec @ 153μs/op
sign x 5,074 ops/sec @ 197μs/op
verify x 626 ops/sec @ 1ms/op
init x 39 ops/sec @ 25ms/op
getPublicKey x 6,518 ops/sec @ 153μs/op
sign x 5,148 ops/sec @ 194μs/op
verify x 609 ops/sec @ 1ms/op
p384
init x 17 ops/sec @ 57ms/op
getPublicKey x 2,883 ops/sec @ 346μs/op
sign x 2,358 ops/sec @ 424μs/op
verify x 245 ops/sec @ 4ms/op
getPublicKey x 2,933 ops/sec @ 340μs/op
sign x 2,327 ops/sec @ 429μs/op
verify x 244 ops/sec @ 4ms/op
p521
init x 9 ops/sec @ 109ms/op
getPublicKey x 1,516 ops/sec @ 659μs/op
sign x 1,271 ops/sec @ 786μs/op
verify x 123 ops/sec @ 8ms/op
init x 8 ops/sec @ 112ms/op
getPublicKey x 1,484 ops/sec @ 673μs/op
sign x 1,264 ops/sec @ 790μs/op
verify x 124 ops/sec @ 8ms/op
ed25519
init x 54 ops/sec @ 18ms/op
getPublicKey x 10,269 ops/sec @ 97μs/op
sign x 5,110 ops/sec @ 195μs/op
verify x 1,049 ops/sec @ 952μs/op
ristretto255
add x 680,735 ops/sec @ 1μs/op
multiply x 10,766 ops/sec @ 92μs/op
encode x 15,835 ops/sec @ 63μs/op
decode x 15,972 ops/sec @ 62μs/op
ed448
init x 19 ops/sec @ 51ms/op
getPublicKey x 3,775 ops/sec @ 264μs/op
sign x 1,771 ops/sec @ 564μs/op
verify x 351 ops/sec @ 2ms/op
decaf448
add x 345,303 ops/sec @ 2μs/op
multiply x 300 ops/sec @ 3ms/op
encode x 5,987 ops/sec @ 167μs/op
decode x 5,892 ops/sec @ 169μs/op
ecdh
├─x25519 x 1,466 ops/sec @ 682μs/op
├─secp256k1 x 539 ops/sec @ 1ms/op
├─p256 x 511 ops/sec @ 1ms/op
├─p384 x 199 ops/sec @ 5ms/op
├─p521 x 103 ops/sec @ 9ms/op
└─x448 x 548 ops/sec @ 1ms/op
├─x25519 x 1,477 ops/sec @ 676μs/op
├─secp256k1 x 537 ops/sec @ 1ms/op
├─p256 x 512 ops/sec @ 1ms/op
├─p384 x 198 ops/sec @ 5ms/op
├─p521 x 99 ops/sec @ 10ms/op
└─x448 x 504 ops/sec @ 1ms/op
bls12-381
init x 36 ops/sec @ 27ms/op
getPublicKey 1-bit x 973 ops/sec @ 1ms/op
getPublicKey x 970 ops/sec @ 1ms/op
sign x 55 ops/sec @ 17ms/op
verify x 39 ops/sec @ 25ms/op
pairing x 106 ops/sec @ 9ms/op
getPublicKey x 960 ops/sec @ 1ms/op
sign x 60 ops/sec @ 16ms/op
verify x 47 ops/sec @ 21ms/op
pairing x 125 ops/sec @ 7ms/op
pairing10 x 40 ops/sec @ 24ms/op ± 23.27% (min: 21ms, max: 48ms)
MSM 4096 scalars x points x 0 ops/sec @ 4655ms/op
aggregatePublicKeys/8 x 129 ops/sec @ 7ms/op
aggregatePublicKeys/32 x 34 ops/sec @ 28ms/op
aggregatePublicKeys/128 x 8 ops/sec @ 112ms/op
aggregatePublicKeys/512 x 2 ops/sec @ 446ms/op
aggregatePublicKeys/2048 x 0 ops/sec @ 1778ms/op
aggregateSignatures/8 x 50 ops/sec @ 19ms/op
aggregateSignatures/32 x 13 ops/sec @ 74ms/op
aggregateSignatures/128 x 3 ops/sec @ 296ms/op
aggregateSignatures/512 x 0 ops/sec @ 1180ms/op
aggregateSignatures/2048 x 0 ops/sec @ 4715ms/op
aggregatePublicKeys/128 x 8 ops/sec @ 113ms/op
aggregatePublicKeys/512 x 2 ops/sec @ 449ms/op
aggregatePublicKeys/2048 x 0 ops/sec @ 1792ms/op
aggregateSignatures/8 x 62 ops/sec @ 15ms/op
aggregateSignatures/32 x 16 ops/sec @ 60ms/op
aggregateSignatures/128 x 4 ops/sec @ 238ms/op
aggregateSignatures/512 x 1 ops/sec @ 946ms/op
aggregateSignatures/2048 x 0 ops/sec @ 3774ms/op

@@ -953,0 +965,0 @@ hash-to-curve

@@ -5,2 +5,5 @@ import { mod } from './abstract/modular.js';

import { ProjPointType as PointType } from './abstract/weierstrass.js';
/**
* secp256k1 short weierstrass curve and ECDSA signatures over it.
*/
export declare const secp256k1: Readonly<{

@@ -77,2 +80,5 @@ create: (hash: import("./abstract/utils.js").CHash) => import("./abstract/weierstrass.js").CurveFn;

declare function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean;
/**
* Schnorr signatures over secp256k1.
*/
export declare const schnorr: {

@@ -79,0 +85,0 @@ getPublicKey: typeof schnorrGetPublicKey;

@@ -46,2 +46,5 @@ "use strict";

const Fp = (0, modular_js_1.Field)(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
/**
* secp256k1 short weierstrass curve and ECDSA signatures over it.
*/
exports.secp256k1 = (0, _shortw_utils_js_1.createCurve)({

@@ -92,4 +95,2 @@ a: BigInt(0), // equation params: a, b

const _0n = BigInt(0);
const fe = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
const ge = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */

@@ -125,4 +126,3 @@ const TAGGED_HASH_PREFIXES = {};

function lift_x(x) {
if (!fe(x))
throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
(0, utils_js_1.aInRange)('x', x, _1n, secp256k1P); // Fail if x ≥ p.
const xx = modP(x * x);

@@ -137,2 +137,3 @@ const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.

}
const num = utils_js_1.bytesToNumberBE;
/**

@@ -142,3 +143,3 @@ * Create tagged hash, convert it to bigint, reduce modulo-n.

function challenge(...args) {
return modN((0, utils_js_1.bytesToNumberBE)(taggedHash('BIP0340/challenge', ...args)));
return modN(num(taggedHash('BIP0340/challenge', ...args)));
}

@@ -159,5 +160,5 @@ /**

const a = (0, utils_js_1.ensureBytes)('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
const t = numTo32b(d ^ (0, utils_js_1.bytesToNumberBE)(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const t = numTo32b(d ^ num(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
const k_ = modN((0, utils_js_1.bytesToNumberBE)(rand)); // Let k' = int(rand) mod n
const k_ = modN(num(rand)); // Let k' = int(rand) mod n
if (k_ === _0n)

@@ -184,8 +185,8 @@ throw new Error('sign failed: k is zero'); // Fail if k' = 0.

try {
const P = lift_x((0, utils_js_1.bytesToNumberBE)(pub)); // P = lift_x(int(pk)); fail if that fails
const r = (0, utils_js_1.bytesToNumberBE)(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!fe(r))
const P = lift_x(num(pub)); // P = lift_x(int(pk)); fail if that fails
const r = num(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!(0, utils_js_1.inRange)(r, _1n, secp256k1P))
return false;
const s = (0, utils_js_1.bytesToNumberBE)(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!ge(s))
const s = num(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!(0, utils_js_1.inRange)(s, _1n, secp256k1N))
return false;

@@ -202,2 +203,5 @@ const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n

}
/**
* Schnorr signatures over secp256k1.
*/
exports.schnorr = (() => ({

@@ -204,0 +208,0 @@ getPublicKey: schnorrGetPublicKey,

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
import { AffinePoint } from './curve.js';
// TODO: import { AffinePoint } from './curve.js';
import { IField, getMinHashLength, mapHashToField } from './modular.js';
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
import { Hex, PrivKey, CHash, ensureBytes, memoized } from './utils.js';
// prettier-ignore

@@ -17,6 +17,9 @@ import {

} from './weierstrass.js';
import type { Fp2, Fp6, Fp12, Fp2Bls, Fp12Bls } from './tower.js';
/**
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
* Implements BLS (Boneh-Lynn-Shacham) signatures.
* BLS != BLS.
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
* families of pairing-friendly curves.
* Consists of two curves: G1 and G2:

@@ -28,4 +31,5 @@ * - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.

* Pairing is used to aggregate and verify signatures.
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
* Some projects may prefer to swap this relation, it is not supported for now.
* There are two main ways to use it:
* 1. Fp for short private keys, Fp₂ for signatures
* 2. Fp for short signatures, Fp₂ for private keys
**/

@@ -36,4 +40,6 @@

// prettier-ignore
const _2n = BigInt(2), _3n = BigInt(3);
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
export type TwistType = 'multiplicative' | 'divisive';
export type ShortSignatureCoder<Fp> = {

@@ -45,22 +51,9 @@ fromHex(hex: Hex): ProjPointType<Fp>;

export type SignatureCoder<Fp2> = {
fromHex(hex: Hex): ProjPointType<Fp2>;
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
export type SignatureCoder<Fp> = {
fromHex(hex: Hex): ProjPointType<Fp>;
toRawBytes(point: ProjPointType<Fp>): Uint8Array;
toHex(point: ProjPointType<Fp>): string;
};
type Fp2Bls<Fp, Fp2> = IField<Fp2> & {
reim: (num: Fp2) => { re: Fp; im: Fp };
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
type Fp12Bls<Fp2, Fp12> = IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
export type CurveType = {
G1: Omit<CurvePointsType<Fp>, 'n'> & {

@@ -79,9 +72,14 @@ ShortSignature: SignatureCoder<Fp>;

Fr: IField<bigint>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
};
params: {
x: bigint;
// NOTE: MSB is always ignored and used as marker for length,
// otherwise leading zeros will be lost.
// Can be different from 'X' (seed) param!
ateLoopSize: bigint;
xNegative: boolean;
r: bigint;
twistType: TwistType; // BLS12-381: Multiplicative, BN254: Divisive
};

@@ -91,5 +89,17 @@ htfDefaults: HTFOpts;

randomBytes: (bytesLength?: number) => Uint8Array;
// This is super ugly hack for untwist point in BN254 after miller loop
postPrecompute?: (
Rx: Fp2,
Ry: Fp2,
Rz: Fp2,
Qx: Fp2,
Qy: Fp2,
pointAdd: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) => { Rx: Fp2; Ry: Fp2; Rz: Fp2 }
) => void;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
type Precompute = PrecomputeSingle[];
export type CurveFn = {
getPublicKey: (privateKey: PrivKey) => Uint8Array;

@@ -135,4 +145,8 @@ getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;

};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
pairingBatch: (
pairs: { g1: ProjPointType<Fp>; g2: ProjPointType<Fp2> }[],
withFinalExponent?: boolean
) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof createHasher<Fp>>;

@@ -143,3 +157,3 @@ G2: CurvePointsRes<Fp2> & ReturnType<typeof createHasher<Fp2>>;

params: {
x: bigint;
ateLoopSize: bigint;
r: bigint;

@@ -151,5 +165,5 @@ G1b: bigint;

Fp: IField<Fp>;
Fp2: Fp2Bls<Fp, Fp2>;
Fp2: Fp2Bls;
Fp6: IField<Fp6>;
Fp12: Fp12Bls<Fp2, Fp12>;
Fp12: Fp12Bls;
Fr: IField<bigint>;

@@ -159,84 +173,25 @@ };

randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
calcPairingPrecomputes: (p: ProjPointType<Fp2>) => Precompute;
};
};
export function bls<Fp2, Fp6, Fp12>(
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
): CurveFn<Fp, Fp2, Fp6, Fp12> {
// Not used with BLS12-381 (no sequential `11` in X). Useful for other curves.
function NAfDecomposition(a: bigint) {
const res = [];
// a>1 because of marker bit
for (; a > _1n; a >>= _1n) {
if ((a & _1n) === _0n) res.unshift(0);
else if ((a & _3n) === _3n) {
res.unshift(-1);
a += _1n;
} else res.unshift(1);
}
return res;
}
export function bls(CURVE: CurveType): CurveFn {
// Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
const BLS_X_LEN = bitLen(CURVE.params.x);
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
const { x, y } = p;
// prettier-ignore
const Qx = x, Qy = y, Qz = Fp2.ONE;
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Qz;
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
// Double
let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, _3n); // 3 * T2
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([
Fp2.sub(t2, t0), // T2 - T0
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
Fp2.neg(t4), // -T4
]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
if (bitGet(CURVE.params.x, i)) {
// Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
ell_coeff.push([
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
Fp2.neg(t0), // -T0
t1, // T1
]);
let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
}
}
return ell_coeff;
}
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
const { x } = CURVE.params;
const Px = g1[0];
const Py = g1[1];
let f12 = Fp12.ONE;
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
const E = ell[j];
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
if (bitGet(x, i)) {
j += 1;
const F = ell[j];
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
}
if (i !== 0) f12 = Fp12.sqr(f12);
}
return Fp12.conjugate(f12);
}
const utils = {
randomPrivateKey: (): Uint8Array => {
const length = getMinHashLength(Fr.ORDER);
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
},
calcPairingPrecomputes,
};
const BLS_X_IS_NEGATIVE = CURVE.params.xNegative;
const TWIST: TwistType = CURVE.params.twistType;
// Point on G1 curve: (x, y)

@@ -251,19 +206,2 @@ const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });

);
// Sparse multiplication against precomputed coefficients
// TODO: replace with weakmap?
type withPairingPrecomputes = { _PPRECOMPUTES: [Fp2, Fp2, Fp2][] | undefined };
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
const p = point as G2 & withPairingPrecomputes;
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
return p._PPRECOMPUTES;
}
// TODO: export
// function clearPairingPrecomputes(point: G2) {
// const p = point as G2 & withPairingPrecomputes;
// p._PPRECOMPUTES = undefined;
// }
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)

@@ -278,20 +216,135 @@ const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });

);
type G1 = typeof G1.ProjectivePoint.BASE;
type G2 = typeof G2.ProjectivePoint.BASE;
const { ShortSignature } = CURVE.G1;
const { Signature } = CURVE.G2;
// Applies sparse multiplication as line function
let lineFunction: (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) => Fp12;
if (TWIST === 'multiplicative') {
lineFunction = (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) =>
Fp12.mul014(f, c0, Fp2.mul(c1, Px), Fp2.mul(c2, Py));
} else if (TWIST === 'divisive') {
// NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
// precompute calculations.
lineFunction = (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) =>
Fp12.mul034(f, Fp2.mul(c2, Py), Fp2.mul(c1, Px), c0);
} else throw new Error('bls: unknown twist type');
const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n));
function pointDouble(ell: PrecomputeSingle, Rx: Fp2, Ry: Fp2, Rz: Fp2) {
const t0 = Fp2.sqr(Ry); // Ry²
const t1 = Fp2.sqr(Rz); // Rz²
const t2 = Fp2.mulByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
const t3 = Fp2.mul(t2, _3n); // 3 * T2
const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
const c1 = Fp2.mul(Fp2.sqr(Rx), _3n); // 3 * Rx²
const c2 = Fp2.neg(t4); // -T4 (-h)
ell.push([c0, c1, c2]);
Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
return { Rx, Ry, Rz };
}
function pointAdd(ell: PrecomputeSingle, Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) {
// Addition
const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
const c2 = t1; // == Rx - Qx * Rz
ell.push([c0, c1, c2]);
const t2 = Fp2.sqr(t1); // T1²
const t3 = Fp2.mul(t2, t1); // T2 * T1
const t4 = Fp2.mul(t2, Rx); // T2 * Rx
const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
return { Rx, Ry, Rz };
}
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
// pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
// add + double in windowed precomputes here, otherwise it would be single op (since X is static)
const ATE_NAF = NAfDecomposition(CURVE.params.ateLoopSize);
const calcPairingPrecomputes = memoized((point: G2) => {
const p = point;
const { x, y } = p.toAffine();
// prettier-ignore
const Qx = x, Qy = y, negQy = Fp2.neg(y);
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
const ell: Precompute = [];
for (const bit of ATE_NAF) {
const cur: PrecomputeSingle = [];
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
if (bit) ({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
ell.push(cur);
}
if (CURVE.postPrecompute) {
const last = ell[ell.length - 1];
CURVE.postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
}
return ell;
});
// Main pairing logic is here. Computes product of miller loops + final exponentiate
// Applies calculated precomputes
type MillerInput = [Precompute, Fp, Fp][];
function millerLoopBatch(pairs: MillerInput, withFinalExponent: boolean = false) {
let f12 = Fp12.ONE;
if (pairs.length) {
const ellLen = pairs[0][0].length;
for (let i = 0; i < ellLen; i++) {
f12 = Fp12.sqr(f12); // This allows us to do sqr only one time for all pairings
// NOTE: we apply multiple pairings in parallel here
for (const [ell, Px, Py] of pairs) {
for (const [c0, c1, c2] of ell[i]) f12 = lineFunction(c0, c1, c2, f12, Px, Py);
}
}
}
if (BLS_X_IS_NEGATIVE) f12 = Fp12.conjugate(f12);
return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
}
type PairingInput = { g1: G1; g2: G2 };
// Calculates product of multiple pairings
// This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
function pairingBatch(pairs: PairingInput[], withFinalExponent: boolean = true) {
const res: MillerInput = [];
// This cache precomputed toAffine for all points
G1.ProjectivePoint.normalizeZ(pairs.map(({ g1 }) => g1));
G2.ProjectivePoint.normalizeZ(pairs.map(({ g2 }) => g2));
for (const { g1, g2 } of pairs) {
if (g1.equals(G1.ProjectivePoint.ZERO) || g2.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
// This uses toAffine inside
g1.assertValidity();
g2.assertValidity();
const Qa = g1.toAffine();
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
}
return millerLoopBatch(res, withFinalExponent);
}
// Calculates bilinear pairing
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
Q.assertValidity();
P.assertValidity();
// Performance: 9ms for millerLoop and ~14ms for exp.
const Qa = Q.toAffine();
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
}
type G1 = typeof G1.ProjectivePoint.BASE;
type G2 = typeof G2.ProjectivePoint.BASE;
const utils = {
randomPrivateKey: (): Uint8Array => {
const length = getMinHashLength(Fr.ORDER);
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
},
calcPairingPrecomputes,
};
const { ShortSignature } = CURVE.G1;
const { Signature } = CURVE.G2;
type G1Hex = Hex | G1;

@@ -370,7 +423,6 @@ type G2Hex = Hex | G2;

const S = normP2(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P.negate(), Hm, false);
const eGS = pairing(G, S, false);
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
const exp = pairingBatch([
{ g1: P.negate(), g2: Hm }, // ePHM = pairing(P.negate(), Hm, false);
{ g1: G, g2: S }, // eGS = pairing(G, S, false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -391,7 +443,6 @@ }

const S = normP1(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const eHmP = pairing(Hm, P, false);
const eSG = pairing(S, G.negate(), false);
const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
const exp = pairingBatch([
{ g1: Hm, g2: P }, // eHmP = pairing(Hm, P, false);
{ g1: S, g2: G.negate() }, // eSG = pairing(S, G.negate(), false);
]);
return Fp12.eql(exp, Fp12.ONE);

@@ -448,2 +499,3 @@ }

signature: G2Hex,
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
messages: G2Hex[],

@@ -453,5 +505,2 @@ publicKeys: G1Hex[],

): boolean {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
if (!messages.length) throw new Error('Expected non-empty messages array');

@@ -463,18 +512,22 @@ if (publicKeys.length !== messages.length)

const nPublicKeys = publicKeys.map(normP1);
// NOTE: this works only for exact same object
const messagePubKeyMap = new Map<G2, G1[]>();
for (let i = 0; i < nPublicKeys.length; i++) {
const pub = nPublicKeys[i];
const msg = nMessages[i];
let keys = messagePubKeyMap.get(msg);
if (keys === undefined) {
keys = [];
messagePubKeyMap.set(msg, keys);
}
keys.push(pub);
}
const paired = [];
try {
const paired = [];
for (const message of new Set(nMessages)) {
const groupPublicKey = nMessages.reduce(
(groupPublicKey, subMessage, i) =>
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
G1.ProjectivePoint.ZERO
);
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
// Possible to batch pairing for same msg with different groupPublicKey here
paired.push(pairing(groupPublicKey, message, false));
for (const [msg, keys] of messagePubKeyMap) {
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
paired.push({ g1: groupPublicKey, g2: msg });
}
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
const exp = Fp12.finalExponentiate(product);
return Fp12.eql(exp, Fp12.ONE);
paired.push({ g1: G1.ProjectivePoint.BASE.negate(), g2: sig });
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
} catch {

@@ -498,4 +551,5 @@ return false;

aggregateShortSignatures,
millerLoop,
millerLoopBatch,
pairing,
pairingBatch,
G1,

@@ -513,3 +567,3 @@ G2,

params: {
x: CURVE.params.x,
ateLoopSize: CURVE.params.ateLoopSize,
r: CURVE.params.r,

@@ -516,0 +570,0 @@ G1b: CURVE.G1.b,

@@ -28,2 +28,7 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

// Since points in different groups cannot be equal (different object constructor),
// we can have single place to store precomputes
const pointPrecomputes = new WeakMap<any, any[]>();
const pointWindowSizes = new WeakMap<any, number>(); // This allows use make points immutable (nothing changes inside)
// Elliptic curve multiplication of Point by scalar. Fragile.

@@ -45,3 +50,8 @@ // Scalars should always be less than curve order: this should be checked inside of a curve itself.

};
const validateW = (W: number) => {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
};
const opts = (W: number) => {
validateW(W);
const windows = Math.ceil(bits / W) + 1; // +1, because

@@ -154,15 +164,21 @@ const windowSize = 2 ** (W - 1); // -1 because we skip zero

wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
// @ts-ignore
const W: number = P._WINDOW_SIZE || 1;
wNAFCached(P: T, n: bigint, transform: Mapper<T>): { p: T; f: T } {
const W: number = pointWindowSizes.get(P) || 1;
// Calculate precomputes on a first run, reuse them after
let comp = precomputesMap.get(P);
let comp = pointPrecomputes.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W) as T[];
if (W !== 1) {
precomputesMap.set(P, transform(comp));
}
if (W !== 1) pointPrecomputes.set(P, transform(comp));
}
return this.wNAF(W, comp, n);
},
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
setWindowSize(P: T, W: number) {
validateW(W);
pointWindowSizes.set(P, W);
pointPrecomputes.delete(P);
},
};

@@ -169,0 +185,0 @@ }

@@ -6,3 +6,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import * as ut from './utils.js';
import { ensureBytes, FHash, Hex } from './utils.js';
import { ensureBytes, FHash, Hex, memoized, abool } from './utils.js';

@@ -76,2 +76,6 @@ // Be friendly to bad ECMAScript parsers by not using bigint literals

/**
* Edwards Curve interface.
* Main methods: `getPublicKey(priv)`, `sign(msg, priv)`, `verify(sig, msg, pub)`.
*/
export type CurveFn = {

@@ -100,3 +104,9 @@ CURVE: ReturnType<typeof validateOpts>;

// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
export function twistedEdwards(curveDef: CurveType): CurveFn {

@@ -130,21 +140,49 @@ const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;

((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
abool('phflag', phflag);
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
return data;
}); // NOOP
const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
function assertInRange(n: bigint, max: bigint) {
// n in [1..max-1]
if (inRange(n, max)) return n;
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
// 0 <= n < MASK
// Coordinates larger than Fp.ORDER are allowed for zip215
function aCoordinate(title: string, n: bigint) {
ut.aInRange('coordinate ' + title, n, _0n, MASK);
}
function assertGE0(n: bigint) {
// n in [0..CURVE_ORDER-1]
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
}
const pointPrecomputes = new Map<Point, Point[]>();
function isPoint(other: unknown) {
function assertPoint(other: unknown) {
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
const toAffineMemo = memoized((p: Point, iz?: bigint): AffinePoint<bigint> => {
const { ex: x, ey: y, ez: z } = p;
const is0 = p.is0();
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0) return { x: _0n, y: _1n };
if (zz !== _1n) throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
const assertValidMemo = memoized((p: Point) => {
const { a, d } = CURVE;
if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = p;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right) throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
return true;
});
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).

@@ -162,6 +200,7 @@ // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates

) {
if (!in0MaskRange(ex)) throw new Error('x required');
if (!in0MaskRange(ey)) throw new Error('y required');
if (!in0MaskRange(ez)) throw new Error('z required');
if (!in0MaskRange(et)) throw new Error('t required');
aCoordinate('x', ex);
aCoordinate('y', ey);
aCoordinate('z', ez);
aCoordinate('t', et);
Object.freeze(this);
}

@@ -179,3 +218,4 @@

const { x, y } = p || {};
if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
aCoordinate('x', x);
aCoordinate('y', y);
return new Point(x, y, _1n, modP(x * y));

@@ -188,11 +228,5 @@ }

// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
_WINDOW_SIZE?: number;
// "Private method", don't use it directly
_setWindowSize(windowSize: number) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}

@@ -202,19 +236,3 @@ // Not required for fromHex(), which always creates valid points.

assertValidity(): void {
const { a, d } = CURVE;
if (this.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = this;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right) throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
assertValidMemo(this);
}

@@ -224,3 +242,3 @@

equals(other: Point): boolean {
isPoint(other);
assertPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;

@@ -235,3 +253,3 @@ const { ex: X2, ey: Y2, ez: Z2 } = other;

protected is0(): boolean {
is0(): boolean {
return this.equals(Point.ZERO);

@@ -271,3 +289,3 @@ }

add(other: Point) {
isPoint(other);
assertPoint(other);
const { a, d } = CURVE;

@@ -317,3 +335,3 @@ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;

private wNAF(n: bigint): { p: Point; f: Point } {
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}

@@ -323,3 +341,5 @@

multiply(scalar: bigint): Point {
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
const n = scalar;
ut.aInRange('scalar', n, _1n, CURVE_ORDER); // 1 <= scalar < L
const { p, f } = this.wNAF(n);
return Point.normalizeZ([p, f])[0];

@@ -333,3 +353,4 @@ }

multiplyUnsafe(scalar: bigint): Point {
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
const n = scalar;
ut.aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
if (n === _0n) return I;

@@ -358,11 +379,3 @@ if (this.equals(I) || n === _1n) return this;

toAffine(iz?: bigint): AffinePoint<bigint> {
const { ex: x, ey: y, ez: z } = this;
const is0 = this.is0();
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0) return { x: _0n, y: _1n };
if (zz !== _1n) throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -382,2 +395,3 @@

hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
abool('zip215', zip215);
const normed = hex.slice(); // copy again, we'll manipulate it

@@ -387,11 +401,9 @@ const lastByte = hex[len - 1]; // select last byte

const y = ut.bytesToNumberLE(normed);
if (y === _0n) {
// y=0 is allowed
} else {
// RFC8032 prohibits >= p, but ZIP215 doesn't
if (zip215)
assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
else assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
}
// RFC8032 prohibits >= p, but ZIP215 doesn't
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
const max = zip215 ? MASK : Fp.ORDER;
ut.aInRange('pointHex.y', y, _0n, max);
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:

@@ -471,3 +483,3 @@ // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)

const s = modN(r + k * scalar); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
ut.aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));

@@ -483,2 +495,3 @@ return ensureBytes('result', res, nByteLength * 2); // 64-byte signature

msg = ensureBytes('message', msg);
if (zip215 !== undefined) abool('zip215', zip215);
if (prehash) msg = prehash(msg); // for ed25519ph, etc

@@ -485,0 +498,0 @@

@@ -198,3 +198,2 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

}
// Other cases: Tonelli-Shanks algorithm

@@ -318,7 +317,15 @@ return tonelliShanks(P);

export function FpLegendre(order: bigint) {
// (a | p) ≡ 1 if a is a square (mod p), quadratic residue
// (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
// (a | p) ≡ 0 if a ≡ 0 (mod p)
const legendreConst = (order - _1n) / _2n; // Integer arithmetic
return <T>(f: IField<T>, x: T): T => f.pow(x, legendreConst);
}
// This function returns True whenever the value x is a square in the field F.
export function FpIsSquare<T>(f: IField<T>) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
const legendre = FpLegendre(f.ORDER);
return (x: T): boolean => {
const p = f.pow(x, legendreConst);
const p = legendre(f, x);
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);

@@ -344,2 +351,5 @@ };

* * c) Object.freeze
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you doing.
* @param ORDER prime positive bigint

@@ -346,0 +356,0 @@ * @param bitLen how many bits the field consumes

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { mod, pow } from './modular.js';
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
import {
aInRange,
bytesToNumberLE,
ensureBytes,
numberToBytesLE,
validateObject,
} from './utils.js';

@@ -78,8 +84,2 @@ const _0n = BigInt(0);

// Accepts 0 as well
function assertFieldElement(n: bigint): bigint {
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
}
// x25519 from 4

@@ -94,7 +94,8 @@ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519

*/
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
const u = assertFieldElement(pointU);
function montgomeryLadder(u: bigint, scalar: bigint): bigint {
aInRange('u', u, _0n, P);
aInRange('scalar', scalar, _0n, P);
// Section 5: Implementations MUST accept non-canonical values and process them as
// if they had been reduced modulo the field prime.
const k = assertFieldElement(scalar);
const k = scalar;
const x_1 = u;

@@ -101,0 +102,0 @@ let x_2 = _1n;

@@ -30,2 +30,7 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

export function abool(title: string, value: boolean): void {
if (typeof value !== 'boolean')
throw new Error(`${title} must be valid boolean, got "${value}".`);
}
// Array where index 0xf0 (240) is mapped to string 'f0'

@@ -178,2 +183,24 @@ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>

// Is positive bigint
const isPosBig = (n: bigint) => typeof n === 'bigint' && _0n <= n;
export function inRange(n: bigint, min: bigint, max: bigint) {
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
}
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
export function aInRange(title: string, n: bigint, min: bigint, max: bigint) {
// Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
// consider P=256n, min=0n, max=P
// - a for min=0 would require -1: `inRange('x', x, -1n, P)`
// - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
// - our way is the cleanest: `inRange('x', x, 0n, P)
if (!inRange(n, min, max))
throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`);
}
// Bit operations

@@ -322,1 +349,23 @@

// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
/**
* throws not implemented error
*/
export const notImplemented = () => {
throw new Error('not implemented');
};
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
export function memoized<T extends object, R, O extends any[]>(fn: (arg: T, ...args: O) => R) {
const map = new WeakMap<T, R>();
return (arg: T, ...args: O): R => {
const val = map.get(arg);
if (val !== undefined) return val;
const computed = fn(arg, ...args);
map.set(arg, computed);
return computed;
};
}

@@ -6,3 +6,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import * as ut from './utils.js';
import { CHash, Hex, PrivKey, ensureBytes } from './utils.js';
import { CHash, Hex, PrivKey, ensureBytes, memoized, abool } from './utils.js';

@@ -35,2 +35,7 @@ export type { AffinePoint };

function validateSigVerOpts(opts: SignOpts | VerOpts) {
if (opts.lowS !== undefined) abool('lowS', opts.lowS);
if (opts.prehash !== undefined) abool('prehash', opts.prehash);
}
/**

@@ -233,11 +238,8 @@ * ### Design rationale for types

function isWithinCurveOrder(num: bigint): boolean {
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
return ut.inRange(num, _1n, CURVE.n);
}
function assertGE(num: bigint) {
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
}
// Validates if priv key is valid and converts it to bigint.
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
function normPrivateKeyToScalar(key: PrivKey): bigint {
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
if (lengths && typeof key !== 'bigint') {

@@ -258,11 +260,52 @@ if (ut.isBytes(key)) key = ut.bytesToHex(key);

}
if (wrapPrivateKey) num = mod.mod(num, n); // disabled by default, enabled for BLS
assertGE(num); // num in range [1..N-1]
if (wrapPrivateKey) num = mod.mod(num, N); // disabled by default, enabled for BLS
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
return num;
}
const pointPrecomputes = new Map<Point, Point[]>();
function assertPrjPoint(other: unknown) {
if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
}
// Memoized toAffine / validity check. They are heavy. Points are immutable.
// Converts Projective point to affine (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
// (x, y, z) ∋ (x=x/z, y=y/z)
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
const { px: x, py: y, pz: z } = p;
// Fast-path for normalized points
if (Fp.eql(z, Fp.ONE)) return { x, y };
const is0 = p.is0();
// 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.
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
// NOTE: on exception this will crash 'cached' and no value will be set.
// Otherwise true will be return
const assertValidMemo = memoized((p: Point) => {
if (p.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = p.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
return true;
});
/**

@@ -285,2 +328,3 @@ * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)

if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
Object.freeze(this);
}

@@ -333,11 +377,5 @@

// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
_WINDOW_SIZE?: number;
// "Private method", don't use it directly
_setWindowSize(windowSize: number) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
wnaf.setWindowSize(this, windowSize);
}

@@ -347,18 +385,5 @@

assertValidity(): void {
if (this.is0()) {
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
const { x, y } = this.toAffine();
// Check if x, y are valid field elements
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
const left = Fp.sqr(y); // y²
const right = weierstrassEquation(x); // x³ + ax + b
if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
if (!this.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
assertValidMemo(this);
}
hasEvenY(): boolean {

@@ -490,10 +515,7 @@ const { y } = this.toAffine();

private is0() {
is0() {
return this.equals(Point.ZERO);
}
private wNAF(n: bigint): { p: Point; f: Point } {
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp: Point[]) => {
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
});
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}

@@ -506,12 +528,12 @@

*/
multiplyUnsafe(n: bigint): Point {
multiplyUnsafe(sc: bigint): Point {
ut.aInRange('scalar', sc, _0n, CURVE.n);
const I = Point.ZERO;
if (n === _0n) return I;
assertGE(n); // Will throw on 0
if (n === _1n) return this;
if (sc === _0n) return I;
if (sc === _1n) return this;
const { endo } = CURVE;
if (!endo) return wnaf.unsafeLadder(this, n);
if (!endo) return wnaf.unsafeLadder(this, sc);
// Apply endomorphism
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
let k1p = I;

@@ -543,8 +565,7 @@ let k2p = I;

multiply(scalar: bigint): Point {
assertGE(scalar);
let n = scalar;
const { endo, n: N } = CURVE;
ut.aInRange('scalar', scalar, _1n, N);
let point: Point, fake: Point; // Fake point is used to const-time mult
const { endo } = CURVE;
if (endo) {
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
let { p: k1p, f: f1p } = this.wNAF(k1);

@@ -558,3 +579,3 @@ let { p: k2p, f: f2p } = this.wNAF(k2);

} else {
const { p, f } = this.wNAF(n);
const { p, f } = this.wNAF(scalar);
point = p;

@@ -587,13 +608,3 @@ fake = f;

toAffine(iz?: T): AffinePoint<T> {
const { px: x, py: y, pz: z } = this;
const is0 = this.is0();
// 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.
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
const ax = Fp.mul(x, iz);
const ay = Fp.mul(y, iz);
const zz = Fp.mul(z, iz);
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
return { x: ax, y: ay };
return toAffineMemo(this, iz);
}

@@ -614,2 +625,3 @@ isTorsionFree(): boolean {

toRawBytes(isCompressed = true): Uint8Array {
abool('isCompressed', isCompressed);
this.assertValidity();

@@ -620,2 +632,3 @@ return toBytes(Point, this, isCompressed);

toHex(isCompressed = true): string {
abool('isCompressed', isCompressed);
return ut.bytesToHex(this.toRawBytes(isCompressed));

@@ -708,2 +721,9 @@ }

/**
* Creates short weierstrass curve and ECDSA signature methods for it.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
*/
export function weierstrass(curveDef: CurveType): CurveFn {

@@ -715,5 +735,2 @@ const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;

function isValidFieldElement(num: bigint): boolean {
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
}
function modN(a: bigint) {

@@ -737,2 +754,3 @@ return mod.mod(a, CURVE_ORDER);

const cat = ut.concatBytes;
abool('isCompressed', isCompressed);
if (isCompressed) {

@@ -751,3 +769,3 @@ return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);

const x = ut.bytesToNumberBE(tail);
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
if (!ut.inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
const y2 = weierstrassEquation(x); // y² = x³ + ax + b

@@ -818,5 +836,4 @@ let y: bigint;

assertValidity(): void {
// can use assertGE here
if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < CURVE.n');
if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
}

@@ -971,5 +988,3 @@

function int2octets(num: bigint): Uint8Array {
if (typeof num !== 'bigint') throw new Error('bigint expected');
if (!(_0n <= num && num < ORDER_MASK))
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
// works with order, can have different size than numToField!

@@ -991,2 +1006,3 @@ return ut.numberToBytesBE(num, CURVE.nByteLength);

msgHash = ensureBytes('msgHash', msgHash);
validateSigVerOpts(opts);
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));

@@ -1082,2 +1098,3 @@

if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
validateSigVerOpts(opts);
const { lowS, prehash } = opts;

@@ -1084,0 +1101,0 @@

@@ -9,3 +9,2 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

bitLen,
bitMask,
bytesToHex,

@@ -20,8 +19,5 @@ bytesToNumberBE,

import { isogenyMap } from './abstract/hash-to-curve.js';
import {
AffinePoint,
mapToCurveSimpleSWU,
ProjConstructor,
ProjPointType,
} from './abstract/weierstrass.js';
import { AffinePoint, mapToCurveSimpleSWU, ProjPointType } from './abstract/weierstrass.js';
import { tower12, psiFrobenius } from './abstract/tower.js';
import type { Fp, Fp2, Fp6, Fp12 } from './abstract/tower.js';

@@ -64,588 +60,41 @@ /*

const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
// prettier-ignore
const _8n = BigInt(8), _16n = BigInt(16);
// CURVE FIELDS
// Finite field over p.
const Fp_raw = BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
);
const Fp = mod.Field(Fp_raw);
type Fp = bigint;
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
/*
Embedding degree (k): 12
Seed (X): -15132376222941642752
Fr: (x⁴-x²+1)
Fp: ((x-1)² ⋅ r(x)/3+x)
(E/Fp): Y²=X³+4
(Eₜ/Fp²): Y² = X³+4(u+1) (M-type twist)
Ate loop size: X
// Fp₂ over complex plane
type BigintTuple = [bigint, bigint];
type Fp2 = { c0: bigint; c1: bigint };
const Fp2Add = ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => ({
c0: Fp.add(c0, r0),
c1: Fp.add(c1, r1),
});
const Fp2Subtract = ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => ({
c0: Fp.sub(c0, r0),
c1: Fp.sub(c1, r1),
});
const Fp2Multiply = ({ c0, c1 }: Fp2, rhs: Fp2) => {
if (typeof rhs === 'bigint') return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
const { c0: r0, c1: r1 } = rhs;
let t1 = Fp.mul(c0, r0); // c0 * o0
let t2 = Fp.mul(c1, r1); // c1 * o1
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
const o0 = Fp.sub(t1, t2);
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
return { c0: o0, c1: o1 };
};
const Fp2Square = ({ c0, c1 }: Fp2) => {
const a = Fp.add(c0, c1);
const b = Fp.sub(c0, c1);
const c = Fp.add(c0, c0);
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
};
type Fp2Utils = {
fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
reim: (num: Fp2) => { re: bigint; im: bigint };
mulByNonresidue: (num: Fp2) => Fp2;
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
// G² - 1
// h2q
// NOTE: ORDER was wrong!
const FP2_ORDER = Fp_raw * Fp_raw;
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-1-u
- Fp¹²[w] = Fp⁶/w²-v
const Fp2: mod.IField<Fp2> & Fp2Utils = {
ORDER: FP2_ORDER,
BITS: bitLen(FP2_ORDER),
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
MASK: bitMask(bitLen(FP2_ORDER)),
ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
pow: (num, power) => mod.FpPow(Fp2, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
// Normalized
add: Fp2Add,
sub: Fp2Subtract,
mul: Fp2Multiply,
sqr: Fp2Square,
// NonNormalized stuff
addN: Fp2Add,
subN: Fp2Subtract,
mulN: Fp2Multiply,
sqrN: Fp2Square,
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
inv: ({ c0: a, c1: b }) => {
// We wish to find the multiplicative inverse of a nonzero
// element a + bu in Fp2. We leverage an identity
//
// (a + bu)(a - bu) = a² + b²
//
// which holds because u² = -1. This can be rewritten as
//
// (a + bu)(a - bu)/(a² + b²) = 1
//
// because a² + b² = 0 has no nonzero solutions for (a, b).
// This gives that (a - bu)/(a² + b²) is the inverse
// of (a + bu). Importantly, this can be computing using
// only a single inversion in Fp.
const factor = Fp.inv(Fp.create(a * a + b * b));
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
},
sqrt: (num) => {
if (Fp2.eql(num, Fp2.ZERO)) return Fp2.ZERO; // Algo doesn't handles this case
// TODO: Optimize this line. It's extremely slow.
// Speeding this up would boost aggregateSignatures.
// https://eprint.iacr.org/2012/685.pdf applicable?
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
const R = FP2_ROOTS_OF_UNITY;
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
if (!divisor) throw new Error('No root');
const index = R.indexOf(divisor);
const root = R[index / 2];
if (!root) throw new Error('Invalid root');
const x1 = Fp2.div(candidateSqrt, root);
const x2 = Fp2.neg(x1);
const { re: re1, im: im1 } = Fp2.reim(x1);
const { re: re2, im: im2 } = Fp2.reim(x2);
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
return x2;
},
// Same as sgn0_m_eq_2 in RFC 9380
isOdd: (x: Fp2) => {
const { re: x0, im: x1 } = Fp2.reim(x);
const sign_0 = x0 % _2n;
const zero_0 = x0 === _0n;
const sign_1 = x1 % _2n;
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
},
// Bytes util
fromBytes(b: Uint8Array): Fp2 {
if (b.length !== Fp2.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
},
toBytes: ({ c0, c1 }) => concatB(Fp.toBytes(c0), Fp.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp.cmov(c0, r0, c),
c1: Fp.cmov(c1, r1, c),
}),
// Specific utils
// toString() {
// return `Fp2(${this.c0} + ${this.c1}×i)`;
// }
reim: ({ c0, c1 }) => ({ re: c0, im: c1 }),
// multiply by u + 1
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
multiplyByB: ({ c0, c1 }) => {
let t0 = Fp.mul(c0, _4n); // 4 * c0
let t1 = Fp.mul(c1, _4n); // 4 * c1
// (T0-T1) + (T0+T1)*i
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
},
fromBigTuple: (tuple: BigintTuple | bigint[]) => {
if (tuple.length !== 2) throw new Error('Invalid tuple');
const fps = tuple.map((n) => Fp.create(n)) as [Fp, Fp];
return { c0: fps[0], c1: fps[1] };
},
frobeniusMap: ({ c0, c1 }, power: number): Fp2 => ({
c0,
c1: Fp.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]),
}),
};
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
const FP2_FROBENIUS_COEFFICIENTS = [
BigInt('0x1'),
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
].map((item) => Fp.create(item));
// For Fp2 roots of unity.
const rv1 = BigInt(
'0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
);
// const ev1 =
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
// const ev2 =
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
// const ev3 =
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
// const ev4 =
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
TODO: BLS & BN Fp/Fr can be constructed from seed.
*/
// Eighth roots of unity, used for computing square roots in Fp2.
// To verify or re-calculate:
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
const FP2_ROOTS_OF_UNITY = [
[_1n, _0n],
[rv1, -rv1],
[_0n, _1n],
[rv1, rv1],
[-_1n, _0n],
[-rv1, rv1],
[_0n, -_1n],
[-rv1, -rv1],
].map((pair) => Fp2.fromBigTuple(pair));
// eta values, used for computing sqrt(g(X1(t)))
// const FP2_ETAs = [
// [ev1, ev2],
// [-ev2, ev1],
// [ev3, ev4],
// [-ev4, ev3],
// ].map((pair) => Fp2.fromBigTuple(pair));
// Finite extension field over irreducible polynominal.
// Fp2(v) / (v³ - ξ) where ξ = u + 1
type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
type Fp6 = { c0: Fp2; c1: Fp2; c2: Fp2 };
const Fp6Add = ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) => ({
c0: Fp2.add(c0, r0),
c1: Fp2.add(c1, r1),
c2: Fp2.add(c2, r2),
});
const Fp6Subtract = ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) => ({
c0: Fp2.sub(c0, r0),
c1: Fp2.sub(c1, r1),
c2: Fp2.sub(c2, r2),
});
const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
if (typeof rhs === 'bigint') {
return {
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
};
}
const { c0: r0, c1: r1, c2: r2 } = rhs;
const t0 = Fp2.mul(c0, r0); // c0 * o0
const t1 = Fp2.mul(c1, r1); // c1 * o1
const t2 = Fp2.mul(c2, r2); // c2 * o2
return {
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
c0: Fp2.add(
t0,
Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))
),
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
c1: Fp2.add(
Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)),
Fp2.mulByNonresidue(t2)
),
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
};
};
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
let t0 = Fp2.sqr(c0); // c0²
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
let t4 = Fp2.sqr(c2); // c2²
return {
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
};
};
type Fp6Utils = {
fromBigSix: (tuple: BigintSix) => Fp6;
mulByNonresidue: (num: Fp6) => Fp6;
frobeniusMap(num: Fp6, power: number): Fp6;
multiplyBy1(num: Fp6, b1: Fp2): Fp6;
multiplyBy01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
};
const Fp6: mod.IField<Fp6> & Fp6Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 3 * Fp2.BITS,
BYTES: 3 * Fp2.BYTES,
MASK: bitMask(3 * Fp2.BITS),
ZERO: { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO },
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
create: (num) => num,
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
sqrt: () => {
throw new Error('Not implemented');
},
// Do we need division by bigint at all? Should be done via order:
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp6, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
// Normalized
add: Fp6Add,
sub: Fp6Subtract,
mul: Fp6Multiply,
sqr: Fp6Square,
// NonNormalized stuff
addN: Fp6Add,
subN: Fp6Subtract,
mulN: Fp6Multiply,
sqrN: Fp6Square,
inv: ({ c0, c1, c2 }) => {
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
let t4 = Fp2.inv(
Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0))
);
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
},
// Bytes utils
fromBytes: (b: Uint8Array): Fp6 => {
if (b.length !== Fp6.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp2.fromBytes(b.subarray(0, Fp2.BYTES)),
c1: Fp2.fromBytes(b.subarray(Fp2.BYTES, 2 * Fp2.BYTES)),
c2: Fp2.fromBytes(b.subarray(2 * Fp2.BYTES)),
};
},
toBytes: ({ c0, c1, c2 }): Uint8Array =>
concatB(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
cmov: ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c) => ({
c0: Fp2.cmov(c0, r0, c),
c1: Fp2.cmov(c1, r1, c),
c2: Fp2.cmov(c2, r2, c),
}),
// Utils
// fromTriple(triple: [Fp2, Fp2, Fp2]) {
// return new Fp6(...triple);
// }
// toString() {
// return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`;
// }
fromBigSix: (t: BigintSix): Fp6 => {
if (!Array.isArray(t) || t.length !== 6) throw new Error('Invalid Fp6 usage');
return {
c0: Fp2.fromBigTuple(t.slice(0, 2)),
c1: Fp2.fromBigTuple(t.slice(2, 4)),
c2: Fp2.fromBigTuple(t.slice(4, 6)),
};
},
frobeniusMap: ({ c0, c1, c2 }, power: number) => ({
c0: Fp2.frobeniusMap(c0, power),
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]),
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]),
}),
mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 }),
// Sparse multiplication
multiplyBy1: ({ c0, c1, c2 }, b1: Fp2): Fp6 => ({
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
c1: Fp2.mul(c0, b1),
c2: Fp2.mul(c1, b1),
}),
// Sparse multiplication
multiplyBy01({ c0, c1, c2 }, b0: Fp2, b1: Fp2): Fp6 {
let t0 = Fp2.mul(c0, b0); // c0 * b0
let t1 = Fp2.mul(c1, b1); // c1 * b1
return {
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
// (b0 + b1) * (c0 + c1) - T0 - T1
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
// (c0 + c2) * b0 - T0 + T1
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
};
},
multiplyByFp2: ({ c0, c1, c2 }, rhs: Fp2): Fp6 => ({
c0: Fp2.mul(c0, rhs),
c1: Fp2.mul(c1, rhs),
c2: Fp2.mul(c2, rhs),
}),
};
const FP6_FROBENIUS_COEFFICIENTS_1 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt('0x0'),
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
],
[
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[BigInt('0x0'), BigInt('0x1')],
[
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
BigInt('0x0'),
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
],
].map((pair) => Fp2.fromBigTuple(pair));
const FP6_FROBENIUS_COEFFICIENTS_2 = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
),
BigInt('0x0'),
],
[
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
BigInt('0x0'),
],
[
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
),
BigInt('0x0'),
],
].map((pair) => Fp2.fromBigTuple(pair));
// Finite extension field over irreducible polynominal.
// Fp₁₂ = Fp₆² => Fp₂³
// Fp₆(w) / (w² - γ) where γ = v
type Fp12 = { c0: Fp6; c1: Fp6 };
// The BLS parameter x for BLS12-381
// The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = bitLen(BLS_X);
// prettier-ignore
type BigintTwelve = [
bigint, bigint, bigint, bigint, bigint, bigint,
bigint, bigint, bigint, bigint, bigint, bigint
];
const Fp12Add = ({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) => ({
c0: Fp6.add(c0, r0),
c1: Fp6.add(c1, r1),
});
const Fp12Subtract = ({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) => ({
c0: Fp6.sub(c0, r0),
c1: Fp6.sub(c1, r1),
});
const Fp12Multiply = ({ c0, c1 }: Fp12, rhs: Fp12 | bigint) => {
if (typeof rhs === 'bigint') return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
let { c0: r0, c1: r1 } = rhs;
let t1 = Fp6.mul(c0, r0); // c0 * r0
let t2 = Fp6.mul(c1, r1); // c1 * r1
return {
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
// (c0 + c1) * (r0 + r1) - (T1 + T2)
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
};
};
const Fp12Square = ({ c0, c1 }: Fp12) => {
let ab = Fp6.mul(c0, c1); // c0 * c1
return {
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
c0: Fp6.sub(
Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab),
Fp6.mulByNonresidue(ab)
),
c1: Fp6.add(ab, ab),
}; // AB + AB
};
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
const a2 = Fp2.sqr(a);
const b2 = Fp2.sqr(b);
return {
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
};
}
type Fp12Utils = {
fromBigTwelve: (t: BigintTwelve) => Fp12;
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
multiplyByFp2(lhs: Fp12, rhs: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
_cyclotomicSquare(num: Fp12): Fp12;
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
};
const Fp12: mod.IField<Fp12> & Fp12Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 2 * Fp2.BITS,
BYTES: 2 * Fp2.BYTES,
MASK: bitMask(2 * Fp2.BITS),
ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
create: (num) => num,
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
sqrt: () => {
throw new Error('Not implemented');
// CURVE FIELDS
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
// Order of Fp
ORDER: BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
),
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
FP2_NONRESIDUE: [_1n, _1n],
Fp2mulByB: ({ c0, c1 }) => {
const t0 = Fp.mul(c0, _4n); // 4 * c0
const t1 = Fp.mul(c1, _4n); // 4 * c1
// (T0-T1) + (T0+T1)*i
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
},
inv: ({ c0, c1 }) => {
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
},
div: (lhs, rhs) =>
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
pow: (num, power) => mod.FpPow(Fp12, num, power),
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
// Normalized
add: Fp12Add,
sub: Fp12Subtract,
mul: Fp12Multiply,
sqr: Fp12Square,
// NonNormalized stuff
addN: Fp12Add,
subN: Fp12Subtract,
mulN: Fp12Multiply,
sqrN: Fp12Square,
// Bytes utils
fromBytes: (b: Uint8Array): Fp12 => {
if (b.length !== Fp12.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
return {
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
};
},
toBytes: ({ c0, c1 }): Uint8Array => concatB(Fp6.toBytes(c0), Fp6.toBytes(c1)),
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
c0: Fp6.cmov(c0, r0, c),
c1: Fp6.cmov(c1, r1, c),
}),
// Utils
// toString() {
// return `Fp12(${this.c0} + ${this.c1} * w)`;
// },
// fromTuple(c: [Fp6, Fp6]) {
// return new Fp12(...c);
// }
fromBigTwelve: (t: BigintTwelve): Fp12 => ({
c0: Fp6.fromBigSix(t.slice(0, 6) as BigintSix),
c1: Fp6.fromBigSix(t.slice(6, 12) as BigintSix),
}),
// Raises to q**i -th power
frobeniusMap(lhs, power: number) {
const r0 = Fp6.frobeniusMap(lhs.c0, power);
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12];
return {
c0: r0,
c1: Fp6.create({
c0: Fp2.mul(c0, coeff),
c1: Fp2.mul(c1, coeff),
c2: Fp2.mul(c2, coeff),
}),
};
},
// Sparse multiplication
multiplyBy014: ({ c0, c1 }, o0: Fp2, o1: Fp2, o4: Fp2) => {
let t0 = Fp6.multiplyBy01(c0, o0, o1);
let t1 = Fp6.multiplyBy1(c1, o4);
return {
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
// (c1 + c0) * [o0, o1+o4] - T0 - T1
c1: Fp6.sub(Fp6.sub(Fp6.multiplyBy01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
};
},
multiplyByFp2: ({ c0, c1 }, rhs: Fp2): Fp12 => ({
c0: Fp6.multiplyByFp2(c0, rhs),
c1: Fp6.multiplyByFp2(c1, rhs),
}),
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.neg(c1) }),
// Fp12
// A cyclotomic group is a subgroup of Fp^n defined by

@@ -655,3 +104,3 @@ // GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}

// https://eprint.iacr.org/2009/565.pdf
_cyclotomicSquare: ({ c0, c1 }): Fp12 => {
Fp12cyclotomicSquare: ({ c0, c1 }): Fp12 => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;

@@ -662,3 +111,3 @@ const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;

const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {

@@ -677,3 +126,3 @@ c0: Fp6.create({

},
_cyclotomicExp(num, n) {
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;

@@ -688,3 +137,3 @@ for (let i = BLS_X_LEN - 1; i >= 0; i--) {

// https://eprint.iacr.org/2009/565.pdf
finalExponentiate: (num) => {
Fp12finalExponentiate: (num) => {
const x = BLS_X;

@@ -708,84 +157,8 @@ // this^(q⁶) / this

},
};
const FP12_FROBENIUS_COEFFICIENTS = [
[BigInt('0x1'), BigInt('0x0')],
[
BigInt(
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
),
BigInt(
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
),
],
[
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
),
BigInt('0x0'),
],
[
BigInt(
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
),
BigInt(
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
),
],
[
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[
BigInt(
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
),
BigInt(
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
),
],
[
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
BigInt('0x0'),
],
[
BigInt(
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
),
BigInt(
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
),
],
[
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
BigInt(
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
),
BigInt(
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
),
],
[
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
),
BigInt('0x0'),
],
[
BigInt(
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
),
BigInt(
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
),
],
].map((n) => Fp2.fromBigTuple(n));
});
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
// END OF CURVE FIELDS

@@ -964,33 +337,4 @@

// Ψ(P) endomorphism
const ut_root = Fp6.create({ c0: Fp2.ZERO, c1: Fp2.ONE, c2: Fp2.ZERO });
const wsq = Fp12.create({ c0: ut_root, c1: Fp6.ZERO });
const wcu = Fp12.create({ c0: Fp6.ZERO, c1: ut_root });
const [wsq_inv, wcu_inv] = Fp12.invertBatch([wsq, wcu]);
function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
// Untwist Fp2->Fp12 && frobenius(1) && twist back
const x2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wsq_inv, x), 1), wsq).c0.c0;
const y2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wcu_inv, y), 1), wcu).c0.c0;
return [x2, y2];
}
// Ψ endomorphism
function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
const affine = P.toAffine();
const p = psi(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
// Ψ²(P) endomorphism
// 1 / F2(2)^((p-1)/3) in GF(p²)
const PSI2_C1 = BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
);
const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
}
function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
const affine = P.toAffine();
const p = psi2(affine.x, affine.y);
return new c(p[0], p[1], Fp2.ONE);
}
// Default hash_to_field options are for hash to G2.

@@ -1096,3 +440,3 @@ //

// Here goes constants && point encoding format
export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
export const bls12_381: CurveFn = bls({
// Fields

@@ -1138,4 +482,4 @@ fields: {

// todo: unroll
const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
return u2P.equals(phi);

@@ -1159,3 +503,3 @@

// return this.multiplyUnsafe(CURVE.h);
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
},

@@ -1285,3 +629,3 @@ mapToCurve: (scalars: bigint[]) => {

isTorsionFree: (c, P): boolean => {
return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
// Older version: https://eprint.iacr.org/2019/814.pdf

@@ -1296,3 +640,3 @@ // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O

clearCofactor: (c, P) => {
const x = bls12_381.params.x;
const x = BLS_X;
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P

@@ -1418,4 +762,6 @@ let t2 = G2psi(c, P); // Ψ(P)

params: {
x: BLS_X, // The BLS parameter x for BLS12-381
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
xNegative: true,
twistType: 'multiplicative',
},

@@ -1422,0 +768,0 @@ htfDefaults,

/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256';
import { getHash } from './_shortw_utils.js';
import { weierstrass } from './abstract/weierstrass.js';
import { randomBytes } from '@noble/hashes/utils';
import { bls, CurveFn } from './abstract/bls.js';
import { Field } from './abstract/modular.js';
import { weierstrass } from './abstract/weierstrass.js';
import { bitGet, bitLen, notImplemented } from './abstract/utils.js';
import { tower12, psiFrobenius } from './abstract/tower.js';
// Types
import type { Fp, Fp2, Fp6, Fp12 } from './abstract/tower.js';
// prettier-ignore
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// prettier-ignore
const _6n = BigInt(6);
/*
bn254, previously known as alt_bn_128, when it had 128-bit security.
Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
so the naming has been adjusted to its prime bit count:
https://hal.science/hal-01534101/file/main.pdf
There are huge compatibility issues in the ecosystem:
1. Different libraries call it in different ways: "bn254", "bn256", "alt_bn128", "bn128".
2. libff has bn128, but it's a different curve with different G2:
https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169
3. halo2curves bn256 is also incompatible and returns different outputs
The goal of our implementation is to support "Ethereum" variant of the curve,
because it at least has specs:
- EIP196 (https://eips.ethereum.org/EIPS/eip-196) describes bn254 ECADD and ECMUL opcodes for EVM
- EIP197 (https://eips.ethereum.org/EIPS/eip-197) describes bn254 pairings
- It's hard: EIPs don't have proper tests. EIP-197 returns boolean output instead of Fp12
- The existing implementations are bad. Some are deprecated:
- https://github.com/paritytech/bn (old version)
- https://github.com/ewasm/ethereum-bn128.rs (uses paritytech/bn)
- https://github.com/zcash-hackworks/bn
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
- Python implementations use different towers and produce different Fp12 outputs:
- https://github.com/ethereum/py_pairing
- https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
- Points are encoded differently in different implementations
*/
/*
Seed (X): 4965661367192848881
Fr: (36x⁴+36x³+18x²+6x+1)
Fp: (36x⁴+36x³+24x²+6x+1)
(E / Fp ): Y² = X³+3
(Et / Fp²): Y² = X³+3/(u+9) (D-type twist)
Ate loop size: 6x+2
Towers:
- Fp²[u] = Fp/u²+1
- Fp⁶[v] = Fp²/v³-9-u
- Fp¹²[w] = Fp⁶/w²-v
*/
const BN_X = BigInt('4965661367192848881');
const BN_X_LEN = bitLen(BN_X);
const SIX_X_SQUARED = _6n * BN_X ** _2n;
// Finite field over r. It's for convenience and is not used in the code below.
const Fr = Field(
BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617')
);
// Fp2.div(Fp2.mul(Fp2.ONE, _3n), Fp2.NONRESIDUE)
const Fp2B = {
c0: BigInt('19485874751759354771024239261021720505790618469301721065564631296452457478373'),
c1: BigInt('266929791119991161246907387137283842545076965332900288569378510910307636690'),
};
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
ORDER: BigInt('21888242871839275222246405745257275088696311157297823662689037894645226208583'),
FP2_NONRESIDUE: [BigInt(9), _1n],
Fp2mulByB: (num) => Fp2.mul(num, Fp2B),
// The result of any pairing is in a cyclotomic subgroup
// https://eprint.iacr.org/2009/565.pdf
Fp12cyclotomicSquare: ({ c0, c1 }): Fp12 => {
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1);
const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2);
const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {
c0: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
}), // 2 * (T7 - c0c2) + T7
c1: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
}),
}; // 2 * (T6 + c1c2) + T6
},
Fp12cyclotomicExp(num, n) {
let z = Fp12.ONE;
for (let i = BN_X_LEN - 1; i >= 0; i--) {
z = Fp12._cyclotomicSquare(z);
if (bitGet(n, i)) z = Fp12.mul(z, num);
}
return z;
},
// https://eprint.iacr.org/2010/354.pdf
// https://eprint.iacr.org/2009/565.pdf
Fp12finalExponentiate: (num) => {
const powMinusX = (num: Fp12) => Fp12.conjugate(Fp12._cyclotomicExp(num, BN_X));
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num));
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0);
const y1 = Fp12._cyclotomicSquare(powMinusX(r));
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1);
const y4 = powMinusX(y2);
const y6 = powMinusX(Fp12._cyclotomicSquare(y4));
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2));
const y9 = Fp12.mul(y8, y1);
return Fp12.mul(
Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3),
Fp12.mul(
Fp12.frobeniusMap(y8, 2),
Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r))
)
);
},
});
// END OF CURVE FIELDS
const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE);
/*
No hashToCurve for now (and signatures):
- RFC 9380 doesn't mention bn254 and doesn't provide test vectors
- Overall seems like nobody is using BLS signatures on top of bn254
- Seems like it can utilize SVDW, which is not implemented yet
*/
const htfDefaults = Object.freeze({
// DST: a domain separation tag defined in section 2.2.5
DST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
encodeDST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
p: Fp.ORDER,
m: 2,
k: 128,
expand: 'xmd',
hash: sha256,
} as const);
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
* so the naming has been adjusted to its prime bit count
* https://hal.science/hal-01534101/file/main.pdf
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
* Contains G1 / G2 operations and pairings.
*/
export const bn254 = weierstrass({
export const bn254: CurveFn = bls({
// Fields
fields: { Fp, Fp2, Fp6, Fp12, Fr },
G1: {
Fp,
h: BigInt(1),
Gx: BigInt(1),
Gy: BigInt(2),
a: Fp.ZERO,
b: _3n,
htfDefaults: { ...htfDefaults, m: 1, DST: 'BN254G2_XMD:SHA-256_SVDW_RO_' },
wrapPrivateKey: true,
allowInfinityPoint: true,
mapToCurve: notImplemented,
fromBytes: notImplemented,
toBytes: notImplemented,
ShortSignature: {
fromHex: notImplemented,
toRawBytes: notImplemented,
toHex: notImplemented,
},
},
G2: {
Fp: Fp2,
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1
h: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
Gx: Fp2.fromBigTuple([
BigInt('10857046999023057135944570762232829481370756359578518086990519993285655852781'),
BigInt('11559732032986387107991004021392285783925812861821192530917403151452391805634'),
]),
Gy: Fp2.fromBigTuple([
BigInt('8495653923123431417604973247489272438418190587263600148770280649306958101930'),
BigInt('4082367875863433681332203403145435568316851327593401208105741076214120093531'),
]),
a: Fp2.ZERO,
b: Fp2B,
hEff: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
htfDefaults: { ...htfDefaults },
wrapPrivateKey: true,
allowInfinityPoint: true,
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), // [p]P = [6X^2]P
mapToCurve: notImplemented,
fromBytes: notImplemented,
toBytes: notImplemented,
Signature: {
fromHex: notImplemented,
toRawBytes: notImplemented,
toHex: notImplemented,
},
},
params: {
ateLoopSize: BN_X * _6n + _2n,
r: Fr.ORDER,
xNegative: false,
twistType: 'divisive',
},
htfDefaults,
hash: sha256,
randomBytes,
postPrecompute: (Rx, Ry, Rz, Qx, Qy, pointAdd) => {
const q = psi(Qx, Qy);
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
const q2 = psi(q[0], q[1]);
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1]));
},
});
/**
* bn254 weierstrass curve with ECDSA.
* This is very rare and probably not used anywhere.
* Instead, you should use G1 / G2, defined above.
*/
export const bn254_weierstrass = weierstrass({
a: BigInt(0),
b: BigInt(3),
Fp: Field(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
Fp,
n: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
Gx: BigInt(1),

@@ -19,0 +234,0 @@ Gy: BigInt(2),

@@ -5,3 +5,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import { AffinePoint, Group } from './abstract/curve.js';
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
import { CurveFn, ExtPointType, twistedEdwards } from './abstract/edwards.js';
import { createHasher, expand_message_xmd, htfBasicOpts } from './abstract/hash-to-curve.js';

@@ -130,3 +130,6 @@ import { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';

export const ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
/**
* ed25519 curve with EdDSA signatures.
*/
export const ed25519: CurveFn = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();

@@ -133,0 +136,0 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {

@@ -8,3 +8,10 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */

import type { Hex, PrivKey } from './abstract/utils.js';
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
import {
inRange,
aInRange,
bytesToNumberBE,
concatBytes,
ensureBytes,
numberToBytesBE,
} from './abstract/utils.js';
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';

@@ -48,2 +55,5 @@

/**
* secp256k1 short weierstrass curve and ECDSA signatures over it.
*/
export const secp256k1 = createCurve(

@@ -97,4 +107,2 @@ {

const _0n = BigInt(0);
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */

@@ -133,3 +141,3 @@ const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};

function lift_x(x: bigint): PointType<bigint> {
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
aInRange('x', x, _1n, secp256k1P); // Fail if x ≥ p.
const xx = modP(x * x);

@@ -143,2 +151,3 @@ const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.

}
const num = bytesToNumberBE;
/**

@@ -148,3 +157,3 @@ * Create tagged hash, convert it to bigint, reduce modulo-n.

function challenge(...args: Uint8Array[]): bigint {
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
return modN(num(taggedHash('BIP0340/challenge', ...args)));
}

@@ -171,5 +180,5 @@

const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const t = numTo32b(d ^ num(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
const k_ = modN(num(rand)); // Let k' = int(rand) mod n
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.

@@ -195,7 +204,7 @@ const { bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.

try {
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!fe(r)) return false;
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!ge(s)) return false;
const P = lift_x(num(pub)); // P = lift_x(int(pk)); fail if that fails
const r = num(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!inRange(r, _1n, secp256k1P)) return false;
const s = num(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!inRange(s, _1n, secp256k1N)) return false;
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n

@@ -210,2 +219,5 @@ const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P

/**
* Schnorr signatures over secp256k1.
*/
export const schnorr = /* @__PURE__ */ (() => ({

@@ -212,0 +224,0 @@ getPublicKey: schnorrGetPublicKey,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc