@noble/curves
Version:
Audited & minimal JS implementation of elliptic curve cryptography
601 lines (567 loc) • 21.4 kB
text/typescript
/**
* 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:
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
* Pairing is used to aggregate and verify signatures.
* There are two modes of operation:
* - Long signatures: X-byte keys + 2X-byte sigs (G1 keys + G2 sigs).
* - Short signatures: 2X-byte keys + X-byte sigs (G2 keys + G1 sigs).
* @module
**/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { abytes, memoized, notImplemented, randomBytes } from '../utils.ts';
import { normalizeZ, type CurveLengths } from './curve.ts';
import {
createHasher,
type H2CDSTOpts,
type H2CHasher,
type H2CHashOpts,
type H2COpts,
type MapToCurve,
} from './hash-to-curve.ts';
import { getMinHashLength, mapHashToField, type IField } from './modular.ts';
import type { Fp12, Fp12Bls, Fp2, Fp2Bls, Fp6Bls } from './tower.ts';
import { type WeierstrassPoint, type WeierstrassPointCons } from './weierstrass.ts';
type Fp = bigint; // Can be different field?
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
export type BlsTwistType = 'multiplicative' | 'divisive';
export type BlsShortSignatureCoder<Fp> = {
fromBytes(bytes: Uint8Array): WeierstrassPoint<Fp>;
fromHex(hex: string): WeierstrassPoint<Fp>;
toBytes(point: WeierstrassPoint<Fp>): Uint8Array;
toHex(point: WeierstrassPoint<Fp>): string;
};
export type BlsLongSignatureCoder<Fp> = {
fromBytes(bytes: Uint8Array): WeierstrassPoint<Fp>;
fromHex(hex: string): WeierstrassPoint<Fp>;
toBytes(point: WeierstrassPoint<Fp>): Uint8Array;
toHex(point: WeierstrassPoint<Fp>): string;
};
export type BlsFields = {
Fp: IField<Fp>;
Fr: IField<bigint>;
Fp2: Fp2Bls;
Fp6: Fp6Bls;
Fp12: Fp12Bls;
};
export type BlsPostPrecomputePointAddFn = (
Rx: Fp2,
Ry: Fp2,
Rz: Fp2,
Qx: Fp2,
Qy: Fp2
) => { Rx: Fp2; Ry: Fp2; Rz: Fp2 };
export type BlsPostPrecomputeFn = (
Rx: Fp2,
Ry: Fp2,
Rz: Fp2,
Qx: Fp2,
Qy: Fp2,
pointAdd: BlsPostPrecomputePointAddFn
) => void;
export type BlsPairing = {
lengths: CurveLengths;
Fr: IField<bigint>;
Fp12: Fp12Bls;
calcPairingPrecomputes: (p: WeierstrassPoint<Fp2>) => Precompute;
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
pairing: (P: WeierstrassPoint<Fp>, Q: WeierstrassPoint<Fp2>, withFinalExponent?: boolean) => Fp12;
pairingBatch: (
pairs: { g1: WeierstrassPoint<Fp>; g2: WeierstrassPoint<Fp2> }[],
withFinalExponent?: boolean
) => Fp12;
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
};
export type BlsPairingParams = {
// 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;
twistType: BlsTwistType; // BLS12-381: Multiplicative, BN254: Divisive
randomBytes?: (len?: number) => Uint8Array;
postPrecompute?: BlsPostPrecomputeFn; // Ugly hack to untwist point in BN254 after miller loop
};
export type BlsHasherParams = {
mapToG1?: MapToCurve<Fp>;
mapToG2?: MapToCurve<Fp2>;
hasherOpts: H2COpts;
hasherOptsG1: H2COpts;
hasherOptsG2: H2COpts;
};
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
type Precompute = PrecomputeSingle[];
/**
* BLS consists of two curves: G1 and G2:
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
*/
export interface BlsCurvePair {
lengths: CurveLengths;
millerLoopBatch: BlsPairing['millerLoopBatch'];
pairing: BlsPairing['pairing'];
pairingBatch: BlsPairing['pairingBatch'];
G1: { Point: WeierstrassPointCons<Fp> };
G2: { Point: WeierstrassPointCons<Fp2> };
fields: {
Fp: IField<Fp>;
Fp2: Fp2Bls;
Fp6: Fp6Bls;
Fp12: Fp12Bls;
Fr: IField<bigint>;
};
utils: {
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
calcPairingPrecomputes: BlsPairing['calcPairingPrecomputes'];
};
params: {
ateLoopSize: bigint;
twistType: BlsTwistType;
};
}
export interface BlsCurvePairWithHashers extends BlsCurvePair {
G1: H2CHasher<WeierstrassPointCons<Fp>>;
G2: H2CHasher<WeierstrassPointCons<Fp2>>;
}
export interface BlsCurvePairWithSignatures extends BlsCurvePairWithHashers {
longSignatures: BlsSigs<bigint, Fp2>;
shortSignatures: BlsSigs<Fp2, bigint>;
}
type BLSInput = Uint8Array;
export interface BlsSigs<P, S> {
lengths: CurveLengths;
keygen(seed?: Uint8Array): {
secretKey: Uint8Array;
publicKey: WeierstrassPoint<P>;
};
getPublicKey(secretKey: Uint8Array): WeierstrassPoint<P>;
sign(hashedMessage: WeierstrassPoint<S>, secretKey: Uint8Array): WeierstrassPoint<S>;
verify(
signature: WeierstrassPoint<S> | BLSInput,
message: WeierstrassPoint<S>,
publicKey: WeierstrassPoint<P> | BLSInput
): boolean;
verifyBatch: (
signature: WeierstrassPoint<S> | BLSInput,
items: { message: WeierstrassPoint<S>; publicKey: WeierstrassPoint<P> | BLSInput }[]
) => boolean;
aggregatePublicKeys(publicKeys: (WeierstrassPoint<P> | BLSInput)[]): WeierstrassPoint<P>;
aggregateSignatures(signatures: (WeierstrassPoint<S> | BLSInput)[]): WeierstrassPoint<S>;
hash(message: Uint8Array, DST?: string | Uint8Array, hashOpts?: H2CHashOpts): WeierstrassPoint<S>;
Signature: BlsLongSignatureCoder<S>;
}
// 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;
}
function aNonEmpty(arr: any[]) {
if (!Array.isArray(arr) || arr.length === 0) throw new Error('expected non-empty array');
}
// This should be enough for bn254, no need to export full stuff?
function createBlsPairing(
fields: BlsFields,
G1: WeierstrassPointCons<Fp>,
G2: WeierstrassPointCons<Fp2>,
params: BlsPairingParams
): BlsPairing {
const { Fr, Fp2, Fp12 } = fields;
const { twistType, ateLoopSize, xNegative, postPrecompute } = params;
type G1 = typeof G1.BASE;
type G2 = typeof G2.BASE;
// Applies sparse multiplication as line function
let lineFunction: (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) => Fp12;
if (twistType === '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 (twistType === '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(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 (postPrecompute) {
const last = ell[ell.length - 1];
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 (xNegative) 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 = [];
// Cache precomputed toAffine for all points
normalizeZ(
G1,
pairs.map(({ g1 }) => g1)
);
normalizeZ(
G2,
pairs.map(({ g2 }) => g2)
);
for (const { g1, g2 } of pairs) {
if (g1.is0() || g2.is0()) 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 {
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
}
const lengths = {
seed: getMinHashLength(Fr.ORDER),
};
const rand = params.randomBytes || randomBytes;
const randomSecretKey = (seed = rand(lengths.seed)): Uint8Array => {
abytes(seed, lengths.seed, 'seed');
return mapHashToField(seed, Fr.ORDER);
};
return {
lengths,
Fr,
Fp12, // NOTE: we re-export Fp12 here because pairing results are Fp12!
millerLoopBatch,
pairing,
pairingBatch,
calcPairingPrecomputes,
randomSecretKey,
};
}
function createBlsSig<P, S>(
blsPairing: BlsPairing,
PubPoint: WeierstrassPointCons<P>,
SigPoint: WeierstrassPointCons<S>,
isSigG1: boolean,
hashToSigCurve: (msg: Uint8Array, options?: H2CDSTOpts) => WeierstrassPoint<S>,
SignatureCoder?: BlsLongSignatureCoder<S>
): BlsSigs<P, S> {
const { Fr, Fp12, pairingBatch, randomSecretKey, lengths } = blsPairing;
if (!SignatureCoder) {
SignatureCoder = {
fromBytes: notImplemented,
fromHex: notImplemented,
toBytes: notImplemented,
toHex: notImplemented,
};
}
type PubPoint = WeierstrassPoint<P>;
type SigPoint = WeierstrassPoint<S>;
function normPub(point: PubPoint | BLSInput): PubPoint {
return point instanceof PubPoint ? (point as PubPoint) : PubPoint.fromBytes(point);
}
function normSig(point: SigPoint | BLSInput): SigPoint {
return point instanceof SigPoint ? (point as SigPoint) : SigPoint.fromBytes(point);
}
function amsg(m: unknown): SigPoint {
if (!(m instanceof SigPoint))
throw new Error(`expected valid message hashed to ${!isSigG1 ? 'G2' : 'G1'} curve`);
return m as SigPoint;
}
type G1 = WeierstrassPoint<Fp>;
type G2 = WeierstrassPoint<Fp2>;
type PairingInput = { g1: G1; g2: G2 };
// What matters here is what point pairing API accepts as G1 or G2, not actual size or names
const pair: (a: PubPoint, b: SigPoint) => PairingInput = !isSigG1
? (a: PubPoint, b: SigPoint) => ({ g1: a, g2: b }) as PairingInput
: (a: PubPoint, b: SigPoint) => ({ g1: b, g2: a }) as PairingInput;
return Object.freeze({
lengths: { ...lengths, secretKey: Fr.BYTES },
keygen(seed?: Uint8Array) {
const secretKey = randomSecretKey(seed);
const publicKey = this.getPublicKey(secretKey);
return { secretKey, publicKey };
},
// P = pk x G
getPublicKey(secretKey: Uint8Array): PubPoint {
let sec;
try {
sec = PubPoint.Fn.fromBytes(secretKey);
} catch (error) {
// @ts-ignore
throw new Error('invalid private key: ' + typeof secretKey, { cause: error });
}
return PubPoint.BASE.multiply(sec);
},
// S = pk x H(m)
sign(message: SigPoint, secretKey: Uint8Array, unusedArg?: any): SigPoint {
if (unusedArg != null) throw new Error('sign() expects 2 arguments');
const sec = PubPoint.Fn.fromBytes(secretKey);
amsg(message).assertValidity();
return message.multiply(sec);
},
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
// e(P, H(m)) == e(G, S)
// e(S, G) == e(H(m), P)
verify(
signature: SigPoint | BLSInput,
message: SigPoint,
publicKey: PubPoint | BLSInput,
unusedArg?: any
): boolean {
if (unusedArg != null) throw new Error('verify() expects 3 arguments');
signature = normSig(signature);
publicKey = normPub(publicKey);
const P = publicKey.negate();
const G = PubPoint.BASE;
const Hm = amsg(message);
const S = signature;
// This code was changed in 1.9.x:
// Before it was G.negate() in G2, now it's always pubKey.negate
// e(P, -Q)===e(-P, Q)==e(P, Q)^-1. Negate can be done anywhere (as long it is done once per pair).
// We just moving sign, but since pairing is multiplicative, we doing X * X^-1 = 1
const exp = pairingBatch([pair(P, Hm), pair(G, S)]);
return Fp12.eql(exp, Fp12.ONE);
},
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
verifyBatch(
signature: SigPoint | BLSInput,
items: { message: SigPoint; publicKey: PubPoint | BLSInput }[]
): boolean {
aNonEmpty(items);
const sig = normSig(signature);
const nMessages = items.map((i) => i.message);
const nPublicKeys = items.map((i) => normPub(i.publicKey));
// NOTE: this works only for exact same object
const messagePubKeyMap = new Map<SigPoint, PubPoint[]>();
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 = [];
const G = PubPoint.BASE;
try {
for (const [msg, keys] of messagePubKeyMap) {
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
paired.push(pair(groupPublicKey, msg));
}
paired.push(pair(G.negate(), sig));
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
} catch {
return false;
}
},
// Adds a bunch of public key points together.
// pk1 + pk2 + pk3 = pkA
aggregatePublicKeys(publicKeys: (PubPoint | BLSInput)[]): PubPoint {
aNonEmpty(publicKeys);
publicKeys = publicKeys.map((pub) => normPub(pub));
const agg = (publicKeys as PubPoint[]).reduce((sum, p) => sum.add(p), PubPoint.ZERO);
agg.assertValidity();
return agg;
},
// Adds a bunch of signature points together.
// pk1 + pk2 + pk3 = pkA
aggregateSignatures(signatures: (SigPoint | BLSInput)[]): SigPoint {
aNonEmpty(signatures);
signatures = signatures.map((sig) => normSig(sig));
const agg = (signatures as SigPoint[]).reduce((sum, s) => sum.add(s), SigPoint.ZERO);
agg.assertValidity();
return agg;
},
hash(messageBytes: Uint8Array, DST?: string | Uint8Array): SigPoint {
abytes(messageBytes);
const opts = DST ? { DST } : undefined;
return hashToSigCurve(messageBytes, opts);
},
Signature: SignatureCoder,
}) /*satisfies Signer */;
}
type BlsSignatureCoders = Partial<{
LongSignature: BlsLongSignatureCoder<Fp2>;
ShortSignature: BlsShortSignatureCoder<Fp>;
}>;
// NOTE: separate function instead of function override, so we don't depend on hasher in bn254.
export function blsBasic(
fields: BlsFields,
G1_Point: WeierstrassPointCons<Fp>,
G2_Point: WeierstrassPointCons<Fp2>,
params: BlsPairingParams
): BlsCurvePair {
// Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = fields;
// Point on G1 curve: (x, y)
// const G1_Point = weierstrass(CURVE.G1, { Fn: Fr });
const G1 = { Point: G1_Point };
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2 = { Point: G2_Point };
const pairingRes = createBlsPairing(fields, G1_Point, G2_Point, params);
const {
millerLoopBatch,
pairing,
pairingBatch,
calcPairingPrecomputes,
randomSecretKey,
lengths,
} = pairingRes;
G1.Point.BASE.precompute(4);
return Object.freeze({
lengths,
millerLoopBatch,
pairing,
pairingBatch,
G1,
G2,
fields: { Fr, Fp, Fp2, Fp6, Fp12 },
params: {
ateLoopSize: params.ateLoopSize,
twistType: params.twistType,
},
utils: {
randomSecretKey,
calcPairingPrecomputes,
},
});
}
// We can export this too, but seems there is not much reasons for now? If user wants hasher, they can just create hasher.
function blsHashers(
fields: BlsFields,
G1_Point: WeierstrassPointCons<Fp>,
G2_Point: WeierstrassPointCons<Fp2>,
params: BlsPairingParams,
hasherParams: BlsHasherParams
): BlsCurvePairWithHashers {
const base = blsBasic(fields, G1_Point, G2_Point, params);
const G1Hasher = createHasher(G1_Point, hasherParams.mapToG1 || notImplemented, {
...hasherParams.hasherOpts,
...hasherParams.hasherOptsG1,
});
const G2Hasher = createHasher(G2_Point, hasherParams.mapToG2 || notImplemented, {
...hasherParams.hasherOpts,
...hasherParams.hasherOptsG2,
});
return Object.freeze({ ...base, G1: G1Hasher, G2: G2Hasher });
}
// G1_Point: ProjConstructor<bigint>, G2_Point: ProjConstructor<Fp2>,
// Rename to blsSignatures?
export function bls(
fields: BlsFields,
G1_Point: WeierstrassPointCons<Fp>,
G2_Point: WeierstrassPointCons<Fp2>,
params: BlsPairingParams,
hasherParams: BlsHasherParams,
signatureCoders: BlsSignatureCoders
): BlsCurvePairWithSignatures {
const base = blsHashers(fields, G1_Point, G2_Point, params, hasherParams);
const pairingRes: BlsPairing = {
...base,
Fr: base.fields.Fr,
Fp12: base.fields.Fp12,
calcPairingPrecomputes: base.utils.calcPairingPrecomputes,
randomSecretKey: base.utils.randomSecretKey,
};
const longSignatures = createBlsSig(
pairingRes,
G1_Point,
G2_Point,
false,
base.G2.hashToCurve,
signatureCoders?.LongSignature
);
const shortSignatures = createBlsSig(
pairingRes,
G2_Point,
G1_Point,
true,
base.G1.hashToCurve,
signatureCoders?.ShortSignature
);
return Object.freeze({ ...base, longSignatures, shortSignatures });
}