Refactor BLS, change API

This commit is contained in:
Paul Miller 2023-04-02 14:38:03 +00:00
parent 37ebe6c40f
commit c15c964f77
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
7 changed files with 255 additions and 153 deletions

@ -214,6 +214,7 @@ There are following zero-dependency algorithms:
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve) - [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve) - [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve) - [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
- [abstract/bls: BLS curves](#abstractbls-bls-curves)
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points) - [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash) - [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
- [abstract/modular: Modular arithmetics utilities](#abstractmodular-modular-arithmetics-utilities) - [abstract/modular: Modular arithmetics utilities](#abstractmodular-modular-arithmetics-utilities)
@ -491,6 +492,74 @@ Proper Elliptic Curve Points are not implemented yet.
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`. You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
### abstract/bls: BLS curves
The module abstracts BLS (Barreto-Lynn-Scott) primitives.
Right now we only implement BLS12-381, but in theory defining BLS12-377, 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`
Full types:
```ts
getPublicKey: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
};
verify: (
signature: Hex | ProjPointType<Fp2>,
message: Hex | ProjPointType<Fp2>,
publicKey: Hex | ProjPointType<Fp>
) => boolean;
verifyBatch: (
signature: Hex | ProjPointType<Fp2>,
messages: (Hex | ProjPointType<Fp2>)[],
publicKeys: (Hex | ProjPointType<Fp>)[]
) => boolean;
aggregatePublicKeys: {
(publicKeys: Hex[]): Uint8Array;
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
};
aggregateSignatures: {
(signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>;
params: {
x: bigint;
r: bigint;
G1b: bigint;
G2b: Fp2;
};
fields: {
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
Fr: IField<bigint>;
};
utils: {
randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
};
```
### abstract/hash-to-curve: Hashing strings to curve points ### abstract/hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16). The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16).
@ -589,11 +658,6 @@ type PoseidonOpts = {
const instance = poseidon(opts: PoseidonOpts); const instance = poseidon(opts: PoseidonOpts);
``` ```
### abstract/bls
The module abstracts BLS (Barreto-Lynn-Scott) primitives. In theory you should be able to write BLS12-377, BLS24,
and others with it.
### abstract/modular: Modular arithmetics utilities ### abstract/modular: Modular arithmetics utilities
```ts ```ts

@ -24,13 +24,16 @@ import {
type Fp = bigint; // Can be different field? type Fp = bigint; // Can be different field?
// prettier-ignore
const _2n = BigInt(2), _3n = BigInt(3);
export type SignatureCoder<Fp2> = { export type SignatureCoder<Fp2> = {
decode(hex: Hex): ProjPointType<Fp2>; fromHex(hex: Hex): ProjPointType<Fp2>;
encode(point: ProjPointType<Fp2>): Uint8Array; toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
}; };
export type CurveType<Fp, Fp2, Fp6, Fp12> = { export type CurveType<Fp, Fp2, Fp6, Fp12> = {
r: bigint;
G1: Omit<CurvePointsType<Fp>, 'n'> & { G1: Omit<CurvePointsType<Fp>, 'n'> & {
mapToCurve: htf.MapToCurve<Fp>; mapToCurve: htf.MapToCurve<Fp>;
htfDefaults: htf.Opts; htfDefaults: htf.Opts;
@ -40,20 +43,25 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
mapToCurve: htf.MapToCurve<Fp2>; mapToCurve: htf.MapToCurve<Fp2>;
htfDefaults: htf.Opts; htfDefaults: htf.Opts;
}; };
x: bigint; fields: {
Fp: IField<Fp>; Fp: IField<Fp>;
Fr: IField<bigint>; Fr: IField<bigint>;
Fp2: IField<Fp2> & { Fp2: IField<Fp2> & {
reim: (num: Fp2) => { re: bigint; im: bigint }; reim: (num: Fp2) => { re: bigint; im: bigint };
multiplyByB: (num: Fp2) => Fp2; multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2; frobeniusMap(num: Fp2, power: number): Fp2;
};
Fp6: IField<Fp6>;
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;
};
}; };
Fp6: IField<Fp6>; params: {
Fp12: IField<Fp12> & { x: bigint;
frobeniusMap(num: Fp12, power: number): Fp12; r: bigint;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
}; };
htfDefaults: htf.Opts; htfDefaults: htf.Opts;
hash: CHash; // Because we need outputLen for DRBG hash: CHash; // Because we need outputLen for DRBG
@ -61,18 +69,6 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
}; };
export type CurveFn<Fp, Fp2, Fp6, Fp12> = { export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
Fr: IField<bigint>;
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>;
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
getPublicKey: (privateKey: PrivKey) => Uint8Array; getPublicKey: (privateKey: PrivKey) => Uint8Array;
sign: { sign: {
(message: Hex, privateKey: PrivKey): Uint8Array; (message: Hex, privateKey: PrivKey): Uint8Array;
@ -83,6 +79,11 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
message: Hex | ProjPointType<Fp2>, message: Hex | ProjPointType<Fp2>,
publicKey: Hex | ProjPointType<Fp> publicKey: Hex | ProjPointType<Fp>
) => boolean; ) => boolean;
verifyBatch: (
signature: Hex | ProjPointType<Fp2>,
messages: (Hex | ProjPointType<Fp2>)[],
publicKeys: (Hex | ProjPointType<Fp>)[]
) => boolean;
aggregatePublicKeys: { aggregatePublicKeys: {
(publicKeys: Hex[]): Uint8Array; (publicKeys: Hex[]): Uint8Array;
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>; (publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
@ -91,22 +92,36 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
(signatures: Hex[]): Uint8Array; (signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>; (signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
}; };
verifyBatch: ( millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
signature: Hex | ProjPointType<Fp2>, pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
messages: (Hex | ProjPointType<Fp2>)[], G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
publicKeys: (Hex | ProjPointType<Fp>)[] G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
) => boolean; Signature: SignatureCoder<Fp2>;
params: {
x: bigint;
r: bigint;
G1b: bigint;
G2b: Fp2;
};
fields: {
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
Fr: IField<bigint>;
};
utils: { utils: {
randomPrivateKey: () => Uint8Array; randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
}; };
}; };
export function bls<Fp2, Fp6, Fp12>( export function bls<Fp2, Fp6, Fp12>(
CURVE: CurveType<Fp, Fp2, Fp6, Fp12> CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
): CurveFn<Fp, Fp2, Fp6, Fp12> { ): CurveFn<Fp, Fp2, Fp6, Fp12> {
// Fields looks pretty specific for curve, so for now we need to pass them with opts // Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE; const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
const BLS_X_LEN = bitLen(CURVE.x); const BLS_X_LEN = bitLen(CURVE.params.x);
const groupLen = 32; // TODO: calculate; hardcoded for now const groupLen = 32; // TODO: calculate; hardcoded for now
// Pre-compute coefficients for sparse multiplication // Pre-compute coefficients for sparse multiplication
@ -122,18 +137,18 @@ export function bls<Fp2, Fp6, Fp12>(
// Double // Double
let t0 = Fp2.sqr(Ry); // Ry² let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz² let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, 3n); // 3 * T2 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 let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([ ell_coeff.push([
Fp2.sub(t2, t0), // T2 - T0 Fp2.sub(t2, t0), // T2 - T0
Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx² Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
Fp2.neg(t4), // -T4 Fp2.neg(t4), // -T4
]); ]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2 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² 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 Rz = Fp2.mul(t0, t4); // T0 * T4
if (bitGet(CURVE.x, i)) { if (bitGet(CURVE.params.x, i)) {
// Addition // Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
@ -145,7 +160,7 @@ export function bls<Fp2, Fp6, Fp12>(
let t2 = Fp2.sqr(t1); // T1² let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1 let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx 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 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 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 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 Rz = Fp2.mul(Rz, t3); // Rz * T3
@ -155,7 +170,7 @@ export function bls<Fp2, Fp6, Fp12>(
} }
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 { function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
const { x } = CURVE; const { x } = CURVE.params;
const Px = g1[0]; const Px = g1[0];
const Py = g1[1]; const Py = g1[1];
let f12 = Fp12.ONE; let f12 = Fp12.ONE;
@ -174,8 +189,9 @@ export function bls<Fp2, Fp6, Fp12>(
const utils = { const utils = {
randomPrivateKey: (): Uint8Array => { randomPrivateKey: (): Uint8Array => {
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r)); return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.params.r));
}, },
calcPairingPrecomputes,
}; };
// Point on G1 curve: (x, y) // Point on G1 curve: (x, y)
@ -236,7 +252,7 @@ export function bls<Fp2, Fp6, Fp12>(
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point); return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
} }
function normP2(point: G2Hex): G2 { function normP2(point: G2Hex): G2 {
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point); return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
} }
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 { function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
return point instanceof G2.ProjectivePoint return point instanceof G2.ProjectivePoint
@ -259,7 +275,7 @@ export function bls<Fp2, Fp6, Fp12>(
msgPoint.assertValidity(); msgPoint.assertValidity();
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey)); const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
if (message instanceof G2.ProjectivePoint) return sigPoint; if (message instanceof G2.ProjectivePoint) return sigPoint;
return Signature.encode(sigPoint); return Signature.toRawBytes(sigPoint);
} }
// Checks if pairing of public key & hash is equal to pairing of generator & signature. // Checks if pairing of public key & hash is equal to pairing of generator & signature.
@ -309,7 +325,7 @@ export function bls<Fp2, Fp6, Fp12>(
aggAffine.assertValidity(); aggAffine.assertValidity();
return aggAffine; return aggAffine;
} }
return Signature.encode(aggAffine); return Signature.toRawBytes(aggAffine);
} }
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407 // https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
@ -353,24 +369,30 @@ export function bls<Fp2, Fp6, Fp12>(
G1.ProjectivePoint.BASE._setWindowSize(4); G1.ProjectivePoint.BASE._setWindowSize(4);
return { return {
CURVE,
Fr,
Fp,
Fp2,
Fp6,
Fp12,
G1,
G2,
Signature,
millerLoop,
calcPairingPrecomputes,
pairing,
getPublicKey, getPublicKey,
sign, sign,
verify, verify,
verifyBatch,
aggregatePublicKeys, aggregatePublicKeys,
aggregateSignatures, aggregateSignatures,
verifyBatch, millerLoop,
pairing,
G1,
G2,
Signature,
fields: {
Fr,
Fp,
Fp2,
Fp6,
Fp12,
},
params: {
x: CURVE.params.x,
r: CURVE.params.r,
G1b: CURVE.G1.b,
G2b: CURVE.G2.b,
},
utils, utils,
}; };
} }

@ -123,12 +123,12 @@ export function utf8ToBytes(str: string): Uint8Array {
// Amount of bits inside bigint (Same as n.toString(2).length) // Amount of bits inside bigint (Same as n.toString(2).length)
export function bitLen(n: bigint) { export function bitLen(n: bigint) {
let len; let len;
for (len = 0; n > 0n; n >>= _1n, len += 1); for (len = 0; n > _0n; n >>= _1n, len += 1);
return len; return len;
} }
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays) // Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
// Same as !!+Array.from(n.toString(2)).reverse()[pos] // Same as !!+Array.from(n.toString(2)).reverse()[pos]
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & 1n; export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & _1n;
// Sets single bit at position // Sets single bit at position
export const bitSet = (n: bigint, pos: number, value: boolean) => export const bitSet = (n: bigint, pos: number, value: boolean) =>
n | ((value ? _1n : _0n) << BigInt(pos)); n | ((value ? _1n : _0n) << BigInt(pos));

@ -59,6 +59,7 @@ import {
bitGet, bitGet,
Hex, Hex,
bitMask, bitMask,
bytesToHex,
} from './abstract/utils.js'; } from './abstract/utils.js';
// Types // Types
import { import {
@ -72,8 +73,8 @@ import { isogenyMap } from './abstract/hash-to-curve.js';
// Be friendly to bad ECMAScript parsers by not using bigint literals // Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore // prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4); const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
const _8n = BigInt(8), // prettier-ignore
_16n = BigInt(16); const _8n = BigInt(8), _16n = BigInt(16);
// CURVE FIELDS // CURVE FIELDS
// Finite field over p. // Finite field over p.
@ -950,9 +951,9 @@ const isogenyMapG1 = isogenyMap(
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i // SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
const G2_SWU = mapToCurveSimpleSWU(Fp2, { const G2_SWU = mapToCurveSimpleSWU(Fp2, {
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(240n) }), // A' = 240 * I A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
B: Fp2.create({ c0: Fp.create(1012n), c1: Fp.create(1012n) }), // B' = 1012 * (1 + I) B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
Z: Fp2.create({ c0: Fp.create(-2n), c1: Fp.create(-1n) }), // Z: -(2 + I) Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
}); });
// Optimized SWU Map - Fp to G1 // Optimized SWU Map - Fp to G1
const G1_SWU = mapToCurveSimpleSWU(Fp, { const G1_SWU = mapToCurveSimpleSWU(Fp, {
@ -966,7 +967,7 @@ const G1_SWU = mapToCurveSimpleSWU(Fp, {
'0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0' '0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0'
) )
), ),
Z: Fp.create(11n), Z: Fp.create(BigInt(11)),
}); });
// Endomorphisms (for fast cofactor clearing) // Endomorphisms (for fast cofactor clearing)
@ -1042,7 +1043,23 @@ const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
// Compressed point of infinity // Compressed point of infinity
const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(_0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
// NOTE: by some reasons it was missed in bls12-381, looks like bug
point.assertValidity();
const len = Fp.BYTES;
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
const { x, y } = point.toAffine();
const { re: x0, im: x1 } = Fp2.reim(x);
const { re: y0, im: y1 } = Fp2.reim(y);
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
const z2 = x0;
return concatB(numberToBytesBE(z1, len), numberToBytesBE(z2, len));
}
// To verify curve parameters, see pairing-friendly-curves spec: // To verify curve parameters, see pairing-friendly-curves spec:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09 // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09
@ -1056,13 +1073,13 @@ const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS
// Here goes constants && point encoding format // Here goes constants && point encoding format
export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// Fields // Fields
Fr, fields: {
Fp, Fp,
Fp2, Fp2,
Fp6, Fp6,
Fp12, Fp12,
// order; z⁴ z² + 1 Fr,
r: Fr.ORDER, // Same as N in other curves },
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where // G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3 // characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
G1: { G1: {
@ -1095,8 +1112,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz); const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
// todo: unroll // todo: unroll
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(bls12_381.CURVE.x); // [u2]P const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
return u2P.equals(phi); return u2P.equals(phi);
// https://eprint.iacr.org/2019/814.pdf // https://eprint.iacr.org/2019/814.pdf
@ -1115,21 +1132,23 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// https://eprint.iacr.org/2019/403 // https://eprint.iacr.org/2019/403
clearCofactor: (c, point) => { clearCofactor: (c, point) => {
// return this.multiplyUnsafe(CURVE.h); // return this.multiplyUnsafe(CURVE.h);
return point.multiplyUnsafe(bls12_381.CURVE.x).add(point); // x*P + P return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
}, },
mapToCurve: (scalars: bigint[]) => { mapToCurve: (scalars: bigint[]) => {
const { x, y } = G1_SWU(Fp.create(scalars[0])); const { x, y } = G1_SWU(Fp.create(scalars[0]));
return isogenyMapG1(x, y); return isogenyMapG1(x, y);
}, },
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => { fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
bytes = bytes.slice();
if (bytes.length === 48) { if (bytes.length === 48) {
// TODO: Fp.bytes
const P = Fp.ORDER; const P = Fp.ORDER;
const compressedValue = bytesToNumberBE(bytes); const compressedValue = bytesToNumberBE(bytes);
const bflag = bitGet(compressedValue, I_BIT_POS); const bflag = bitGet(compressedValue, I_BIT_POS);
// Zero // Zero
if (bflag === _1n) return { x: _0n, y: _0n }; if (bflag === _1n) return { x: _0n, y: _0n };
const x = Fp.create(compressedValue & Fp.MASK); const x = Fp.create(compressedValue & Fp.MASK);
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.CURVE.G1.b)); // y² = x³ + b const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
let y = Fp.sqrt(right); let y = Fp.sqrt(right);
if (!y) throw new Error('Invalid compressed G1 point'); if (!y) throw new Error('Invalid compressed G1 point');
const aflag = bitGet(compressedValue, C_BIT_POS); const aflag = bitGet(compressedValue, C_BIT_POS);
@ -1138,8 +1157,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
} else if (bytes.length === 96) { } else if (bytes.length === 96) {
// Check if the infinity flag is set // Check if the infinity flag is set
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine(); if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES)); const x = bytesToNumberBE(bytes.subarray(0, Fp.BYTES));
const y = bytesToNumberBE(bytes.slice(Fp.BYTES)); const y = bytesToNumberBE(bytes.subarray(Fp.BYTES));
return { x: Fp.create(x), y: Fp.create(y) }; return { x: Fp.create(x), y: Fp.create(y) };
} else { } else {
throw new Error('Invalid point G1, expected 48/96 bytes'); throw new Error('Invalid point G1, expected 48/96 bytes');
@ -1212,7 +1231,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// It returns false for shitty points. // It returns false for shitty points.
// https://eprint.iacr.org/2021/1130.pdf // https://eprint.iacr.org/2021/1130.pdf
isTorsionFree: (c, P): boolean => { isTorsionFree: (c, P): boolean => {
return P.multiplyUnsafe(bls12_381.CURVE.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P) return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
// Older version: https://eprint.iacr.org/2019/814.pdf // Older version: https://eprint.iacr.org/2019/814.pdf
// Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
// return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero(); // return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
@ -1222,7 +1241,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// https://eprint.iacr.org/2017/419.pdf // https://eprint.iacr.org/2017/419.pdf
// prettier-ignore // prettier-ignore
clearCofactor: (c, P) => { clearCofactor: (c, P) => {
const { x } = bls12_381.CURVE; const x = bls12_381.params.x;
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
let t2 = G2psi(c, P); // Ψ(P) let t2 = G2psi(c, P); // Ψ(P)
let t3 = P.double(); // 2P let t3 = P.double(); // 2P
@ -1236,6 +1255,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P) return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
}, },
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => { fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
bytes = bytes.slice();
const m_byte = bytes[0] & 0xe0; const m_byte = bytes[0] & 0xe0;
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) { if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
throw new Error('Invalid encoding flag: ' + m_byte); throw new Error('Invalid encoding flag: ' + m_byte);
@ -1246,7 +1266,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
const L = Fp.BYTES; const L = Fp.BYTES;
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to)); const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
if (bytes.length === 96 && bitC) { if (bytes.length === 96 && bitC) {
const { b } = bls12_381.CURVE.G2; const b = bls12_381.params.G2b;
const P = Fp.ORDER; const P = Fp.ORDER;
bytes[0] = bytes[0] & 0x1f; // clear flags bytes[0] = bytes[0] & 0x1f; // clear flags
@ -1280,31 +1300,31 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
} }
}, },
toBytes: (c, point, isCompressed) => { toBytes: (c, point, isCompressed) => {
const { BYTES: len, ORDER: P } = Fp;
const isZero = point.equals(c.ZERO); const isZero = point.equals(c.ZERO);
const { x, y } = point.toAffine(); const { x, y } = point.toAffine();
if (isCompressed) { if (isCompressed) {
const P = Fp.ORDER; if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P); const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
// set compressed & sign bits (looks like different offsets than for G1/Fp?) // set compressed & sign bits (looks like different offsets than for G1/Fp?)
let x_1 = bitSet(x.c1, C_BIT_POS, flag); let x_1 = bitSet(x.c1, C_BIT_POS, flag);
x_1 = bitSet(x_1, S_BIT_POS, true); x_1 = bitSet(x_1, S_BIT_POS, true);
return concatB(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES)); return concatB(numberToBytesBE(x_1, len), numberToBytesBE(x.c0, len));
} else { } else {
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6; if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
const { re: x0, im: x1 } = Fp2.reim(x); const { re: x0, im: x1 } = Fp2.reim(x);
const { re: y0, im: y1 } = Fp2.reim(y); const { re: y0, im: y1 } = Fp2.reim(y);
return concatB( return concatB(
numberToBytesBE(x1, Fp.BYTES), numberToBytesBE(x1, len),
numberToBytesBE(x0, Fp.BYTES), numberToBytesBE(x0, len),
numberToBytesBE(y1, Fp.BYTES), numberToBytesBE(y1, len),
numberToBytesBE(y0, Fp.BYTES) numberToBytesBE(y0, len)
); );
} }
}, },
Signature: { Signature: {
// TODO: Optimize, it's very slow because of sqrt. // TODO: Optimize, it's very slow because of sqrt.
decode(hex: Hex): ProjPointType<Fp2> { fromHex(hex: Hex): ProjPointType<Fp2> {
hex = ensureBytes('signatureHex', hex); hex = ensureBytes('signatureHex', hex);
const P = Fp.ORDER; const P = Fp.ORDER;
const half = hex.length / 2; const half = hex.length / 2;
@ -1319,7 +1339,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
const x1 = Fp.create(z1 & Fp.MASK); const x1 = Fp.create(z1 & Fp.MASK);
const x2 = Fp.create(z2); const x2 = Fp.create(z2);
const x = Fp2.create({ c0: x2, c1: x1 }); const x = Fp2.create({ c0: x2, c1: x1 });
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.CURVE.G2.b); // y² = x³ + 4 const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
// The slow part // The slow part
let y = Fp2.sqrt(y2); let y = Fp2.sqrt(y2);
if (!y) throw new Error('Failed to find a square root'); if (!y) throw new Error('Failed to find a square root');
@ -1335,24 +1355,18 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
point.assertValidity(); point.assertValidity();
return point; return point;
}, },
encode(point: ProjPointType<Fp2>) { toRawBytes(point: ProjPointType<Fp2>) {
// NOTE: by some reasons it was missed in bls12-381, looks like bug return signatureG2ToRawBytes(point);
point.assertValidity(); },
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO)) toHex(point: ProjPointType<Fp2>) {
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES)); return bytesToHex(signatureG2ToRawBytes(point));
const a = point.toAffine();
const { re: x0, im: x1 } = Fp2.reim(a.x);
const { re: y0, im: y1 } = Fp2.reim(a.y);
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
const z2 = x0;
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
}, },
}, },
}, },
// The BLS parameter x for BLS12-381 params: {
x: BLS_X, x: BLS_X, // The BLS parameter x for BLS12-381
r: Fr.ORDER, // order; z⁴ z² + 1; CURVE.n from other curves
},
htfDefaults, htfDefaults,
hash: sha256, hash: sha256,
randomBytes, randomBytes,

@ -30,19 +30,19 @@ const FIELDS = {
pallas: { Fp: [pallas.CURVE.Fp] }, pallas: { Fp: [pallas.CURVE.Fp] },
vesta: { Fp: [vesta.CURVE.Fp] }, vesta: { Fp: [vesta.CURVE.Fp] },
bls12: { bls12: {
Fp: [bls12_381.CURVE.Fp], Fp: [bls12_381.fields.Fp],
Fp2: [ Fp2: [
bls12_381.CURVE.Fp2, bls12_381.fields.Fp2,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), { fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
minLength: 2, minLength: 2,
maxLength: 2, maxLength: 2,
}), }),
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]), (Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
], ],
// Fp6: [bls12_381.CURVE.Fp6], // Fp6: [bls12_381.fields.Fp6],
Fp12: [ Fp12: [
bls12_381.CURVE.Fp12, bls12_381.fields.Fp12,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), { fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
minLength: 12, minLength: 12,
maxLength: 12, maxLength: 12,
}), }),
@ -221,7 +221,7 @@ for (const c in FIELDS) {
const isSquare = mod.FpIsSquare(Fp); const isSquare = mod.FpIsSquare(Fp);
// Not implemented // Not implemented
if (Fp !== bls12_381.CURVE.Fp12) { if (Fp !== bls12_381.fields.Fp12) {
should('multiply/sqrt', () => { should('multiply/sqrt', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {

@ -24,11 +24,10 @@ const SCALAR_VECTORS = readFileSync('./test/bls12-381/bls12-381-scalar-test-vect
const NUM_RUNS = Number(process.env.RUNS_COUNT || 10); // reduce to 1 to shorten test time const NUM_RUNS = Number(process.env.RUNS_COUNT || 10); // reduce to 1 to shorten test time
fc.configureGlobal({ numRuns: NUM_RUNS }); fc.configureGlobal({ numRuns: NUM_RUNS });
const { Fp2 } = bls;
const G1Point = bls.G1.ProjectivePoint; const G1Point = bls.G1.ProjectivePoint;
const G2Point = bls.G2.ProjectivePoint; const G2Point = bls.G2.ProjectivePoint;
const G1Aff = (x, y) => G1Point.fromAffine({ x, y }); const G1Aff = (x, y) => G1Point.fromAffine({ x, y });
const CURVE_ORDER = bls.CURVE.r; const CURVE_ORDER = bls.params.r;
const FC_MSG = fc.hexaString({ minLength: 64, maxLength: 64 }); const FC_MSG = fc.hexaString({ minLength: 64, maxLength: 64 });
const FC_MSG_5 = fc.array(FC_MSG, { minLength: 5, maxLength: 5 }); const FC_MSG_5 = fc.array(FC_MSG, { minLength: 5, maxLength: 5 });
@ -42,10 +41,10 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
function equal(a, b, comment) { function equal(a, b, comment) {
deepStrictEqual(a.equals(b), true, `eq(${comment})`); deepStrictEqual(a.equals(b), true, `eq(${comment})`);
} }
const { Fp, Fp2 } = bls.fields;
// Fp // Fp
describe('bls12-381 Fp', () => { describe('bls12-381 Fp', () => {
const Fp = bls.Fp;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
should('multiply/sqrt', () => { should('multiply/sqrt', () => {
@ -60,8 +59,7 @@ describe('bls12-381 Fp', () => {
// Fp2 // Fp2
describe('bls12-381 Fp2', () => { describe('bls12-381 Fp2', () => {
const Fp = bls.Fp; const { Fp, Fp2 } = bls.fields;
const Fp2 = bls.Fp2;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 }); const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 });
@ -149,7 +147,7 @@ describe('bls12-381 Fp2', () => {
// Point // Point
describe('bls12-381 Point', () => { describe('bls12-381 Point', () => {
const Fp = bls.Fp; const { Fp } = bls.fields;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
const PointG1 = G1Point; const PointG1 = G1Point;
const PointG2 = G2Point; const PointG2 = G2Point;
@ -557,9 +555,12 @@ describe('bls12-381 Point', () => {
]; ];
// Use wNAF allow scalars higher than CURVE.r // Use wNAF allow scalars higher than CURVE.r
const w = wNAF(G2Point, 1); const w = wNAF(G2Point, 1);
const hEff = BigInt(
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
);
for (let p of points) { for (let p of points) {
const ours = p.clearCofactor(); const ours = p.clearCofactor();
const shouldBe = w.unsafeLadder(p, bls.CURVE.G2.hEff); const shouldBe = w.unsafeLadder(p, hEff);
deepStrictEqual(ours.equals(shouldBe), true, 'clearLast'); deepStrictEqual(ours.equals(shouldBe), true, 'clearLast');
} }
}); });
@ -577,12 +578,12 @@ describe('bls12-381/basic', () => {
deepStrictEqual(g1.x, G1Point.ZERO.x); deepStrictEqual(g1.x, G1Point.ZERO.x);
deepStrictEqual(g1.y, G1Point.ZERO.y); deepStrictEqual(g1.y, G1Point.ZERO.y);
// Test Non-Zero // Test Non-Zero
const x = bls.Fp.create( const x = Fp.create(
BigInt( BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb' '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
) )
); );
const y = bls.Fp.create( const y = Fp.create(
BigInt( BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1' '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
) )
@ -603,12 +604,12 @@ describe('bls12-381/basic', () => {
deepStrictEqual(g1.x, G1Point.ZERO.x); deepStrictEqual(g1.x, G1Point.ZERO.x);
deepStrictEqual(g1.y, G1Point.ZERO.y); deepStrictEqual(g1.y, G1Point.ZERO.y);
// Test Non-Zero // Test Non-Zero
const x = bls.Fp.create( const x = Fp.create(
BigInt( BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb' '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
) )
); );
const y = bls.Fp.create( const y = Fp.create(
BigInt( BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1' '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
) )
@ -689,12 +690,12 @@ describe('bls12-381/basic', () => {
// Test Zero // Test Zero
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40); deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero // Test Non-Zero
const x = bls.Fp.create( const x = Fp.create(
BigInt( BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb' '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
) )
); );
const y = bls.Fp.create( const y = Fp.create(
BigInt( BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1' '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
) )
@ -710,12 +711,12 @@ describe('bls12-381/basic', () => {
// Test Zero // Test Zero
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40); deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero // Test Non-Zero
const x = bls.Fp.create( const x = Fp.create(
BigInt( BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb' '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
) )
); );
const y = bls.Fp.create( const y = Fp.create(
BigInt( BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1' '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
) )
@ -801,32 +802,32 @@ describe('bls12-381/basic', () => {
throws(() => G2Point.fromPrivateKey(0n)); throws(() => G2Point.fromPrivateKey(0n));
}); });
const VALID_G1 = new G1Point( const VALID_G1 = new G1Point(
bls.Fp.create( Fp.create(
3609742242174788176010452839163620388872641749536604986743596621604118973777515189035770461528205168143692110933639n 3609742242174788176010452839163620388872641749536604986743596621604118973777515189035770461528205168143692110933639n
), ),
bls.Fp.create( Fp.create(
1619277690257184054444116778047375363103842303863153349133480657158810226683757397206929105479676799650932070320089n 1619277690257184054444116778047375363103842303863153349133480657158810226683757397206929105479676799650932070320089n
), ),
bls.Fp.create(1n) Fp.create(1n)
); );
const VALID_G1_2 = new G1Point( const VALID_G1_2 = new G1Point(
bls.Fp.create( Fp.create(
1206972466279728255044019580914616126536509750250979180256809997983196363639429409634110400978470384566664128085207n 1206972466279728255044019580914616126536509750250979180256809997983196363639429409634110400978470384566664128085207n
), ),
bls.Fp.create( Fp.create(
2991142246317096160788653339959532007292638191110818490939476869616372888657136539642598243964263069435065725313423n 2991142246317096160788653339959532007292638191110818490939476869616372888657136539642598243964263069435065725313423n
), ),
bls.Fp.create(1n) Fp.create(1n)
); );
const INVALID_G1 = new G1Point( const INVALID_G1 = new G1Point(
bls.Fp.create( Fp.create(
499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n 499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n
), ),
bls.Fp.create( Fp.create(
3934582309586258715640230772291917282844636728991757779640464479794033391537662970190753981664259511166946374555673n 3934582309586258715640230772291917282844636728991757779640464479794033391537662970190753981664259511166946374555673n
), ),
bls.Fp.create(1n) Fp.create(1n)
); );
should('aggregate pubkeys', () => { should('aggregate pubkeys', () => {
@ -855,7 +856,7 @@ describe('bls12-381/basic', () => {
}); });
should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, () => { should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, () => {
const options = { const options = {
p: bls.CURVE.r, p: bls.params.r,
m: 1, m: 1,
expand: undefined, expand: undefined,
}; };
@ -863,7 +864,7 @@ describe('bls12-381/basic', () => {
const [okmAscii, expectedHex] = vector; const [okmAscii, expectedHex] = vector;
const expected = BigInt('0x' + expectedHex); const expected = BigInt('0x' + expectedHex);
const okm = utf8ToBytes(okmAscii); const okm = utf8ToBytes(okmAscii);
const scalars = hash_to_field(okm, 1, Object.assign({}, bls.CURVE.htfDefaults, options)); const scalars = hash_to_field(okm, 1, Object.assign({}, bls.G2.CURVE.htfDefaults, options));
deepStrictEqual(scalars[0][0], expected); deepStrictEqual(scalars[0][0], expected);
} }
}); });
@ -871,7 +872,8 @@ describe('bls12-381/basic', () => {
// Pairing // Pairing
describe('pairing', () => { describe('pairing', () => {
const { pairing, Fp12 } = bls; const { pairing } = bls;
const { Fp12 } = bls.fields;
const G1 = G1Point.BASE; const G1 = G1Point.BASE;
const G2 = G2Point.BASE; const G2 = G2Point.BASE;
@ -1000,7 +1002,7 @@ describe('hash-to-curve', () => {
]; ];
for (let i = 0; i < VECTORS_G1.length; i++) { for (let i = 0; i < VECTORS_G1.length; i++) {
const t = VECTORS_G1[i]; const t = VECTORS_G1[i];
should(`hashToCurve/G1 Killic (${i})`, () => { should(`G1 Killic (${i})`, () => {
const p = bls.G1.hashToCurve(t.msg, { const p = bls.G1.hashToCurve(t.msg, {
DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN', DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN',
}); });
@ -1259,7 +1261,7 @@ describe('bls12-381 deterministic', () => {
.reverse() .reverse()
.reduce((acc, i) => acc + i); .reduce((acc, i) => acc + i);
const Fp12 = bls.Fp12; const { Fp12 } = bls.fields;
should('Killic based/Pairing', () => { should('Killic based/Pairing', () => {
const t = bls.pairing(G1Point.BASE, G2Point.BASE); const t = bls.pairing(G1Point.BASE, G2Point.BASE);