tests: Use describe()

This commit is contained in:
Paul Miller 2023-01-13 15:00:13 +00:00
parent 5312d92b2c
commit f4cf21b9c8
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
9 changed files with 2995 additions and 2928 deletions

@ -31,7 +31,7 @@
"@types/node": "18.11.3", "@types/node": "18.11.3",
"fast-check": "3.0.0", "fast-check": "3.0.0",
"micro-bmark": "0.2.0", "micro-bmark": "0.2.0",
"micro-should": "0.2.0", "micro-should": "0.3.0",
"prettier": "2.6.2", "prettier": "2.6.2",
"rollup": "2.75.5", "rollup": "2.75.5",
"typescript": "4.7.3" "typescript": "4.7.3"

@ -1,5 +1,5 @@
import { deepStrictEqual, throws } from 'assert'; import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should'; import { should, describe } from 'micro-should';
import * as fc from 'fast-check'; import * as fc from 'fast-check';
import * as mod from '../lib/esm/abstract/modular.js'; import * as mod from '../lib/esm/abstract/modular.js';
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js'; import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
@ -62,7 +62,8 @@ for (const c in FIELDS) {
const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n);
const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num); const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num);
should(`${name} equality`, () => { describe(name, () => {
should('equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -72,7 +73,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} non-equality`, () => { should('non-equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => { fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1); const a = create(num1);
@ -82,7 +83,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/commutativity`, () => { should('add/subtract/commutativity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => { fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1); const a = create(num1);
@ -91,7 +92,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/associativity`, () => { should('add/subtract/associativity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => { fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1); const a = create(num1);
@ -101,7 +102,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/x+0=x`, () => { should('add/subtract/x+0=x', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -109,7 +110,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/x-0=x`, () => { should('add/subtract/x-0=x', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -118,7 +119,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/negate equality`, () => { should('add/subtract/negate equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num1) => { fc.property(FC_BIGINT, (num1) => {
const a = create(num1); const a = create(num1);
@ -129,7 +130,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} add/subtract/negate`, () => { should('add/subtract/negate', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -138,11 +139,11 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} negate(0)`, () => { should('negate(0)', () => {
deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO);
}); });
should(`${name} multiply/commutativity`, () => { should('multiply/commutativity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => { fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1); const a = create(num1);
@ -151,7 +152,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} multiply/associativity`, () => { should('multiply/associativity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => { fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1); const a = create(num1);
@ -161,7 +162,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} multiply/distributivity`, () => { should('multiply/distributivity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => { fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1); const a = create(num1);
@ -171,7 +172,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} multiply/add equality`, () => { should('multiply/add equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -185,7 +186,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} multiply/square equality`, () => { should('multiply/square equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -193,7 +194,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} multiply/pow equality`, () => { should('multiply/pow equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -205,17 +206,17 @@ for (const c in FIELDS) {
); );
}); });
should(`${name} square(0)`, () => { should('square(0)', () => {
deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
}); });
should(`${name} square(1)`, () => { should('square(1)', () => {
deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE); deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE);
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE); deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
}); });
should(`${name} square(-1)`, () => { should('square(-1)', () => {
const minus1 = Fp.negate(Fp.ONE); const minus1 = Fp.negate(Fp.ONE);
deepStrictEqual(Fp.square(minus1), Fp.ONE); deepStrictEqual(Fp.square(minus1), Fp.ONE);
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE); deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
@ -224,7 +225,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.CURVE.Fp12) {
should(`${name} multiply/sqrt`, () => { should('multiply/sqrt', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -242,7 +243,7 @@ for (const c in FIELDS) {
); );
}); });
should(`${name} sqrt(0)`, () => { should('sqrt(0)', () => {
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
const sqrt1 = Fp.sqrt(Fp.ONE); const sqrt1 = Fp.sqrt(Fp.ONE);
deepStrictEqual( deepStrictEqual(
@ -253,7 +254,7 @@ for (const c in FIELDS) {
}); });
} }
should(`${name} div/division by one equality`, () => { should('div/division by one equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -263,7 +264,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} zero division equality`, () => { should('zero division equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
@ -271,7 +272,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} div/division distributivity`, () => { should('div/division distributivity', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => { fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1); const a = create(num1);
@ -281,7 +282,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
should(`${name} div/division and multiplication equality`, () => { should('div/division and multiplication equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => { fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1); const a = create(num1);
@ -290,6 +291,7 @@ for (const c in FIELDS) {
}) })
); );
}); });
});
} }
} }
@ -311,12 +313,12 @@ const NUM_RUNS = 5;
const getXY = (p) => ({ x: p.x, y: p.y }); const getXY = (p) => ({ x: p.x, y: p.y });
function equal(a, b, comment) { function equal(a, b, comment) {
deepStrictEqual(a.equals(b), true, `eq(${comment})`); deepStrictEqual(a.equals(b), true, 'eq(${comment})');
if (a.toAffine && b.toAffine) { if (a.toAffine && b.toAffine) {
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), `eqToAffine(${comment})`); deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), 'eqToAffine(${comment})');
} else if (!a.toAffine && !b.toAffine) { } else if (!a.toAffine && !b.toAffine) {
// Already affine // Already affine
deepStrictEqual(getXY(a), getXY(b), `eqAffine(${comment})`); deepStrictEqual(getXY(a), getXY(b), 'eqAffine(${comment})');
} else throw new Error('Different point types'); } else throw new Error('Different point types');
} }
@ -341,46 +343,49 @@ for (const name in CURVES) {
const G = [p.ZERO, p.BASE]; const G = [p.ZERO, p.BASE];
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i)); for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
const title = `${name}/${pointName}`;
describe(title, () => {
describe('basic group laws', () => {
// Here we check basic group laws, to verify that points works as group // Here we check basic group laws, to verify that points works as group
should(`${name}/${pointName}/Basic group laws (zero)`, () => { should('(zero)', () => {
equal(G[0].double(), G[0], '(0*G).double() = 0'); equal(G[0].double(), G[0], '(0*G).double() = 0');
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0'); equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0'); equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
equal(G[0].negate(), G[0], '-0 = 0'); equal(G[0].negate(), G[0], '-0 = 0');
for (let i = 0; i < G.length; i++) { for (let i = 0; i < G.length; i++) {
const p = G[i]; const p = G[i];
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`); equal(p, p.add(G[0]), '${i}*G + 0 = ${i}*G');
equal(G[0].multiply(i + 1), G[0], `${i + 1}*0 = 0`); equal(G[0].multiply(i + 1), G[0], '${i + 1}*0 = 0');
} }
}); });
should(`${name}/${pointName}/Basic group laws (one)`, () => { should('(one)', () => {
equal(G[1].double(), G[2], '(1*G).double() = 2*G'); equal(G[1].double(), G[2], '(1*G).double() = 2*G');
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0'); equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G'); equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
}); });
should(`${name}/${pointName}/Basic group laws (sanity tests)`, () => { should('(sanity tests)', () => {
equal(G[2].double(), G[4], `(2*G).double() = 4*G`); equal(G[2].double(), G[4], '(2*G).double() = 4*G');
equal(G[2].add(G[2]), G[4], `2*G + 2*G = 4*G`); equal(G[2].add(G[2]), G[4], '2*G + 2*G = 4*G');
equal(G[7].add(G[3].negate()), G[4], `7*G - 3*G = 4*G`); equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
}); });
should(`${name}/${pointName}/Basic group laws (addition commutativity)`, () => { should('(addition commutativity)', () => {
equal(G[4].add(G[3]), G[3].add(G[4]), `4*G + 3*G = 3*G + 4*G`); equal(G[4].add(G[3]), G[3].add(G[4]), '4*G + 3*G = 3*G + 4*G');
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), `4*G + 3*G = 3*G + 2*G + 2*G`); equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
}); });
should(`${name}/${pointName}/Basic group laws (double)`, () => { should('(double)', () => {
equal(G[3].double(), G[6], '(3*G).double() = 6*G'); equal(G[3].double(), G[6], '(3*G).double() = 6*G');
}); });
should(`${name}/${pointName}/Basic group laws (multiply)`, () => { should('(multiply)', () => {
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G'); equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
}); });
should(`${name}/${pointName}/Basic group laws (same point addition)`, () => { should('(same point addition)', () => {
equal(G[3].add(G[3]), G[6], `3*G + 3*G = 6*G`); equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
}); });
should(`${name}/${pointName}/Basic group laws (same point (negative) addition)`, () => { should('(same point (negative) addition)', () => {
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G'); equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G'); equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
}); });
should(`${name}/${pointName}/Basic group laws (curve order)`, () => { should('(curve order)', () => {
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0'); equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G'); equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0'); equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
@ -388,7 +393,7 @@ for (const name in CURVES) {
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0]; const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0'); equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
}); });
should(`${name}/${pointName}/Basic group laws (inversion)`, () => { should('(inversion)', () => {
const a = 1234n; const a = 1234n;
const b = 5678n; const b = 5678n;
const c = a * b; const c = a * b;
@ -396,7 +401,7 @@ for (const name in CURVES) {
const inv = mod.invert(b, CURVE_ORDER); const inv = mod.invert(b, CURVE_ORDER);
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G'); equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
}); });
should(`${name}/${pointName}/Basic group laws (multiply, rand)`, () => should('(multiply, rand)', () =>
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a + b, CURVE_ORDER); const c = mod.mod(a + b, CURVE_ORDER);
@ -404,27 +409,29 @@ for (const name in CURVES) {
const pA = G[1].multiply(a); const pA = G[1].multiply(a);
const pB = G[1].multiply(b); const pB = G[1].multiply(b);
const pC = G[1].multiply(c); const pC = G[1].multiply(c);
equal(pA.add(pB), pB.add(pA), `pA + pB = pB + pA`); equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
equal(pA.add(pB), pC, `pA + pB = pC`); equal(pA.add(pB), pC, 'pA + pB = pC');
}), }),
{ numRuns: NUM_RUNS } { numRuns: NUM_RUNS }
) )
); );
should(`${name}/${pointName}/Basic group laws (multiply2, rand)`, () => should('(multiply2, rand)', () =>
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a * b, CURVE_ORDER); const c = mod.mod(a * b, CURVE_ORDER);
const pA = G[1].multiply(a); const pA = G[1].multiply(a);
const pB = G[1].multiply(b); const pB = G[1].multiply(b);
equal(pA.multiply(b), pB.multiply(a), `b*pA = a*pB`); equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
equal(pA.multiply(b), G[1].multiply(c), `b*pA = c*G`); equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
}), }),
{ numRuns: NUM_RUNS } { numRuns: NUM_RUNS }
) )
); );
});
for (const op of ['add', 'subtract']) { for (const op of ['add', 'subtract']) {
should(`${name}/${pointName}/${op} type check`, () => { describe(op, () => {
should('type check', () => {
throws(() => G[1][op](0), '0'); throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n'); throws(() => G[1][op](0n), '0n');
G[1][op](G[2]); G[1][op](G[2]);
@ -432,17 +439,21 @@ for (const name in CURVES) {
throws(() => G[1][op](123.456), '123.456'); throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true'); throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), "'1'"); throws(() => G[1][op]('1'), "'1'");
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }'); throws(
() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }),
'{ x: 1n, y: 1n, z: 1n, t: 1n }'
);
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])'); throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])'); throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])'); throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])'); throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`); if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), 'Point ${op} ${pointName}');
throws(() => G[1][op](o.BASE), `${op}/other curve point`); throws(() => G[1][op](o.BASE), '${op}/other curve point');
});
}); });
} }
should(`${name}/${pointName}/equals type check`, () => { should('equals type check', () => {
throws(() => G[1].equals(0), '0'); throws(() => G[1].equals(0), '0');
throws(() => G[1].equals(0n), '0n'); throws(() => G[1].equals(0n), '0n');
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G'); deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
@ -457,13 +468,14 @@ for (const name in CURVES) {
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])'); throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])'); throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])'); throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), `Point.equals(${pointName})`); if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
throws(() => G[1].equals(o.BASE), 'other curve point'); throws(() => G[1].equals(o.BASE), 'other curve point');
}); });
for (const op of ['multiply', 'multiplyUnsafe']) { for (const op of ['multiply', 'multiplyUnsafe']) {
if (!p.BASE[op]) continue; if (!p.BASE[op]) continue;
should(`${name}/${pointName}/${op} type check`, () => { describe(op, () => {
should('type check', () => {
if (op !== 'multiplyUnsafe') { if (op !== 'multiplyUnsafe') {
throws(() => G[1][op](0), '0'); throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n'); throws(() => G[1][op](0n), '0n');
@ -482,23 +494,24 @@ for (const name in CURVES) {
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])'); throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
throws(() => G[1][op](o.BASE), 'other curve point'); throws(() => G[1][op](o.BASE), 'other curve point');
}); });
});
} }
// Complex point (Extended/Jacobian/Projective?) // Complex point (Extended/Jacobian/Projective?)
if (p.BASE.toAffine) { if (p.BASE.toAffine) {
should(`${name}/${pointName}/toAffine()`, () => { should('toAffine()', () => {
equal(p.ZERO.toAffine(), C.Point.ZERO, `0 = 0`); equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
equal(p.BASE.toAffine(), C.Point.BASE, `1 = 1`); equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
}); });
} }
if (p.fromAffine) { if (p.fromAffine) {
should(`${name}/${pointName}/fromAffine()`, () => { should('fromAffine()', () => {
equal(p.ZERO, p.fromAffine(C.Point.ZERO), `0 = 0`); equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
equal(p.BASE, p.fromAffine(C.Point.BASE), `1 = 1`); equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
}); });
} }
// toHex/fromHex (if available) // toHex/fromHex (if available)
if (p.fromHex && p.BASE.toHex) { if (p.fromHex && p.BASE.toHex) {
should(`${name}/${pointName}/fromHex(toHex()) roundtrip`, () => { should('fromHex(toHex()) roundtrip', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (x) => { fc.property(FC_BIGINT, (x) => {
const hex = p.BASE.multiply(x).toHex(); const hex = p.BASE.multiply(x).toHex();
@ -507,9 +520,11 @@ for (const name in CURVES) {
); );
}); });
} }
});
} }
describe(name, () => {
// Generic complex things (getPublicKey/sign/verify/getSharedSecret) // Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should(`${name}/getPublicKey type check`, () => { should('getPublicKey type check', () => {
throws(() => C.getPublicKey(0), '0'); throws(() => C.getPublicKey(0), '0');
throws(() => C.getPublicKey(0n), '0n'); throws(() => C.getPublicKey(0n), '0n');
throws(() => C.getPublicKey(false), 'false'); throws(() => C.getPublicKey(false), 'false');
@ -524,7 +539,7 @@ for (const name in CURVES) {
throws(() => C.getPublicKey(new Uint8Array([1]))); throws(() => C.getPublicKey(new Uint8Array([1])));
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1))); throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
}); });
should(`${name}.verify()/should verify random signatures`, () => should('.verify() should verify random signatures', () =>
fc.assert( fc.assert(
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => { fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const priv = C.utils.randomPrivateKey(); const priv = C.utils.randomPrivateKey();
@ -533,18 +548,18 @@ for (const name in CURVES) {
deepStrictEqual( deepStrictEqual(
C.verify(sig, msg, pub), C.verify(sig, msg, pub),
true, true,
`priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}` 'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
); );
}), }),
{ numRuns: NUM_RUNS } { numRuns: NUM_RUNS }
) )
); );
should(`${name}.sign()/edge cases`, () => { should('.sign() edge cases', () => {
throws(() => C.sign()); throws(() => C.sign());
throws(() => C.sign('')); throws(() => C.sign(''));
}); });
should(`${name}.verify()/should not verify signature with wrong hash`, () => { should('.verify() should not verify signature with wrong hash', () => {
const MSG = '01'.repeat(32); const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n; const PRIV_KEY = 0x2n;
const WRONG_MSG = '11'.repeat(32); const WRONG_MSG = '11'.repeat(32);
@ -554,7 +569,7 @@ for (const name in CURVES) {
}); });
// NOTE: fails for ed, because of empty message. Since we convert it to scalar, // NOTE: fails for ed, because of empty message. Since we convert it to scalar,
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case? // need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
// should(`${name}/should not verify signature with wrong message`, () => { // should('should not verify signature with wrong message', () => {
// fc.assert( // fc.assert(
// fc.property( // fc.property(
// fc.array(fc.integer({ min: 0x00, max: 0xff })), // fc.array(fc.integer({ min: 0x00, max: 0xff })),
@ -576,7 +591,7 @@ for (const name in CURVES) {
// }); // });
if (C.getSharedSecret) { if (C.getSharedSecret) {
should(`${name}/getSharedSecret() should be commutative`, () => { should('getSharedSecret() should be commutative', () => {
for (let i = 0; i < NUM_RUNS; i++) { for (let i = 0; i < NUM_RUNS; i++) {
const asec = C.utils.randomPrivateKey(); const asec = C.utils.randomPrivateKey();
const apub = C.getPublicKey(asec); const apub = C.getPublicKey(asec);
@ -591,6 +606,7 @@ for (const name in CURVES) {
} }
}); });
} }
});
} }
should('secp224k1 sqrt bug', () => { should('secp224k1 sqrt bug', () => {

@ -1,5 +1,5 @@
import { bls12_381 } from '../lib/esm/bls12-381.js'; import { bls12_381 } from '../lib/esm/bls12-381.js';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import { deepStrictEqual, notDeepStrictEqual, throws } from 'assert'; import { deepStrictEqual, notDeepStrictEqual, throws } from 'assert';
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import * as fc from 'fast-check'; import * as fc from 'fast-check';
@ -38,11 +38,11 @@ const B_384_40 = '40'.padEnd(384, '0'); // [0x40, 0, 0...]
const getPubKey = (priv) => bls.getPublicKey(priv); const getPubKey = (priv) => bls.getPublicKey(priv);
// Fp // Fp
{ describe('bls12-381 Fp', () => {
const Fp = bls.Fp; const Fp = bls.Fp;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
should('bls12-381/Fp/multiply/sqrt', () => { should('multiply/sqrt', () => {
let sqr1 = Fp.sqrt(Fp.create(300855555557n)); let sqr1 = Fp.sqrt(Fp.create(300855555557n));
deepStrictEqual( deepStrictEqual(
sqr1 && sqr1.toString(), sqr1 && sqr1.toString(),
@ -50,16 +50,16 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
throws(() => Fp.sqrt(Fp.create(72057594037927816n))); throws(() => Fp.sqrt(Fp.create(72057594037927816n)));
}); });
} });
// Fp2 // Fp2
{ describe('bls12-381 Fp2', () => {
const Fp = bls.Fp; const Fp = bls.Fp;
const Fp2 = bls.Fp2; 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 });
should('bls12-381 Fp2/non-equality', () => { should('non-equality', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => { fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => {
const a = Fp2.fromBigTuple([num1[0], num1[1]]); const a = Fp2.fromBigTuple([num1[0], num1[1]]);
@ -70,7 +70,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
}); });
should('bls12-381 Fp2/div/x/1=x', () => { should('div/x/1=x', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT_2, (num) => { fc.property(FC_BIGINT_2, (num) => {
const a = Fp2.fromBigTuple([num[0], num[1]]); const a = Fp2.fromBigTuple([num[0], num[1]]);
@ -81,7 +81,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
}); });
should('bls12-381 Fp2/frobenius', () => { should('frobenius', () => {
// expect(Fp2.FROBENIUS_COEFFICIENTS[0].equals(Fp.ONE)).toBe(true); // expect(Fp2.FROBENIUS_COEFFICIENTS[0].equals(Fp.ONE)).toBe(true);
// expect( // expect(
// Fp2.FROBENIUS_COEFFICIENTS[1].equals( // Fp2.FROBENIUS_COEFFICIENTS[1].equals(
@ -139,16 +139,17 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
true true
); );
}); });
} });
// Point // Point
{ describe('bls12-381 Point', () => {
const Fp = bls.Fp; const Fp = bls.Fp;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n); const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
const PointG1 = bls.G1.Point; const PointG1 = bls.G1.Point;
const PointG2 = bls.G2.Point; const PointG2 = bls.G2.Point;
should('bls12-381 Point/Point with Fp coordinates/Point equality', () => { describe('with Fp coordinates', () => {
should('Point equality', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.array(FC_BIGINT, { minLength: 3, maxLength: 3 }), fc.array(FC_BIGINT, { minLength: 3, maxLength: 3 }),
@ -164,16 +165,16 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
) )
); );
}); });
should('bls12-381 Point/Point with Fp coordinates/should be placed on curve vector 1', () => { should('be placed on curve vector 1', () => {
const a = new PointG1(Fp.create(0n), Fp.create(0n)); const a = new PointG1(Fp.create(0n), Fp.create(0n));
a.assertValidity(); a.assertValidity();
}); });
should('bls12-381 Point/Point with Fp coordinates/should not be placed on curve vector 1', () => { should('not be placed on curve vector 1', () => {
const a = new PointG1(Fp.create(0n), Fp.create(1n)); const a = new PointG1(Fp.create(0n), Fp.create(1n));
throws(() => a.assertValidity()); throws(() => a.assertValidity());
}); });
should('bls12-381 Point/Point with Fp coordinates/should be placed on curve vector 2', () => { should('be placed on curve vector 2', () => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn
@ -184,7 +185,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
a.assertValidity(); a.assertValidity();
}); });
should('bls12-381 Point/Point with Fp coordinates/should be placed on curve vector 3', () => { should('be placed on curve vector 3', () => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
3971675556538908004130084773503021351583407620890695272226385332452194486153316625183061567093226342405194446632851n 3971675556538908004130084773503021351583407620890695272226385332452194486153316625183061567093226342405194446632851n
@ -196,7 +197,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
a.assertValidity(); a.assertValidity();
}); });
should('bls12-381 Point/Point with Fp coordinates/should not be placed on curve vector 3', () => { should('not be placed on curve vector 3', () => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
622186380008502900120948444810967255157373993223369845903602988014033704418470621816206856882891545628885272576827n 622186380008502900120948444810967255157373993223369845903602988014033704418470621816206856882891545628885272576827n
@ -207,7 +208,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
throws(() => a.assertValidity()); throws(() => a.assertValidity());
}); });
should('bls12-381 Point/Point with Fp coordinates/should not be placed on curve vector 2', () => { should('not be placed on curve vector 2', () => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6ban 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6ban
@ -219,9 +220,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
throws(() => a.assertValidity()); throws(() => a.assertValidity());
}); });
should( should('be doubled and placed on curve vector 1', () => {
'bls12-381 Point/Point with Fp coordinates/should be doubled and placed on curve vector 1',
() => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn
@ -245,11 +244,8 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
deepStrictEqual(double, a.multiply(2n)); deepStrictEqual(double, a.multiply(2n));
deepStrictEqual(double, a.add(a)); deepStrictEqual(double, a.add(a));
} });
); should('be pdoubled and placed on curve vector 2', () => {
should(
'bls12-381 Point/Point with Fp coordinates/should be pdoubled and placed on curve vector 2',
() => {
const a = new PointG1( const a = new PointG1(
Fp.create( Fp.create(
3971675556538908004130084773503021351583407620890695272226385332452194486153316625183061567093226342405194446632851n 3971675556538908004130084773503021351583407620890695272226385332452194486153316625183061567093226342405194446632851n
@ -273,9 +269,8 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
deepStrictEqual(double, a.multiply(2n)); deepStrictEqual(double, a.multiply(2n));
deepStrictEqual(double, a.add(a)); deepStrictEqual(double, a.add(a));
} });
); should('not validate incorrect point', () => {
should('bls12-381 Point/Point with Fp coordinates/should not validate incorrect point', () => {
const x = const x =
499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n; 499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n;
const y = const y =
@ -284,8 +279,10 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
const p = new PointG1(Fp.create(x), Fp.create(y)); const p = new PointG1(Fp.create(x), Fp.create(y));
throws(() => p.assertValidity()); throws(() => p.assertValidity());
}); });
});
should('bls12-381 Point/Point with Fp2 coordinates/Point equality', () => { describe('with Fp2 coordinates', () => {
should('Point equality', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.array(fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 }), { fc.array(fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 }), {
@ -297,8 +294,16 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
maxLength: 3, maxLength: 3,
}), }),
([x1, y1, z1], [x2, y2, z2]) => { ([x1, y1, z1], [x2, y2, z2]) => {
const p1 = new PointG2(Fp2.fromBigTuple(x1), Fp2.fromBigTuple(y1), Fp2.fromBigTuple(z1)); const p1 = new PointG2(
const p2 = new PointG2(Fp2.fromBigTuple(x2), Fp2.fromBigTuple(y2), Fp2.fromBigTuple(z2)); Fp2.fromBigTuple(x1),
Fp2.fromBigTuple(y1),
Fp2.fromBigTuple(z1)
);
const p2 = new PointG2(
Fp2.fromBigTuple(x2),
Fp2.fromBigTuple(y2),
Fp2.fromBigTuple(z2)
);
deepStrictEqual(p1.equals(p1), true); deepStrictEqual(p1.equals(p1), true);
deepStrictEqual(p2.equals(p2), true); deepStrictEqual(p2.equals(p2), true);
deepStrictEqual(p1.equals(p2), false); deepStrictEqual(p1.equals(p2), false);
@ -307,11 +312,11 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
) )
); );
}); });
// should('bls12-381 Point/Point with Fp2 coordinates/should be placed on curve vector 1', () => { // should('be placed on curve vector 1', () => {
// const a = new PointG2(Fp2.fromBigTuple([0n, 0n]), Fp2.fromBigTuple([0n, 0n])); // const a = new PointG2(Fp2.fromBigTuple([0n, 0n]), Fp2.fromBigTuple([0n, 0n]));
// a.assertValidity(); // a.assertValidity();
// }); // });
should('bls12-381 Point/Point with Fp2 coordinates/should be placed on curve vector 2', () => { should('be placed on curve vector 2', () => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n, 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
@ -325,7 +330,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
a.assertValidity(); a.assertValidity();
}); });
should('bls12-381 Point/Point with Fp2 coordinates/should be placed on curve vector 3', () => { should('be placed on curve vector 3', () => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
233289878585407360737561818812172281900488265436962145913969074168503452745466655442125797664134009339799716079103n, 233289878585407360737561818812172281900488265436962145913969074168503452745466655442125797664134009339799716079103n,
@ -338,20 +343,15 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
); );
a.assertValidity(); a.assertValidity();
}); });
should( should('not be placed on curve vector 1', () => {
'bls12-381 Point/Point with Fp2 coordinates/should not be placed on curve vector 1',
() => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([0n, 0n]), Fp2.fromBigTuple([0n, 0n]),
Fp2.fromBigTuple([1n, 0n]), Fp2.fromBigTuple([1n, 0n]),
Fp2.fromBigTuple([1n, 0n]) Fp2.fromBigTuple([1n, 0n])
); );
throws(() => a.assertValidity()); throws(() => a.assertValidity());
} });
); should('not be placed on curve vector 2', () => {
should(
'bls12-381 Point/Point with Fp2 coordinates/should not be placed on curve vector 2',
() => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4410b647ae3d1770bac0326a805bbefd48056c8c121bdb8n, 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4410b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
@ -364,11 +364,8 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
Fp2.fromBigTuple([1n, 0n]) Fp2.fromBigTuple([1n, 0n])
); );
throws(() => a.assertValidity()); throws(() => a.assertValidity());
} });
); should('not be placed on curve vector 3', () => {
should(
'bls12-381 Point/Point with Fp2 coordinates/should not be placed on curve vector 3',
() => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
0x877d52dd65245f8908a03288adcd396f489ef87ae23fe110c5aa48bc208fbd1a0ed403df5b1ac137922b915f1f38ec37n, 0x877d52dd65245f8908a03288adcd396f489ef87ae23fe110c5aa48bc208fbd1a0ed403df5b1ac137922b915f1f38ec37n,
@ -384,10 +381,10 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
]) ])
); );
throws(() => a.assertValidity()); throws(() => a.assertValidity());
} });
); });
should('bls12-381 Point/should be doubled and placed on curve vector 1', () => { should('be doubled and placed on curve vector 1', () => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n, 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
@ -417,7 +414,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
deepStrictEqual(double, a.multiply(2n)); deepStrictEqual(double, a.multiply(2n));
deepStrictEqual(double, a.add(a)); deepStrictEqual(double, a.add(a));
}); });
should('bls12-381 Point/should be doubled and placed on curve vector 2', () => { should('be doubled and placed on curve vector 2', () => {
const a = new PointG2( const a = new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
233289878585407360737561818812172281900488265436962145913969074168503452745466655442125797664134009339799716079103n, 233289878585407360737561818812172281900488265436962145913969074168503452745466655442125797664134009339799716079103n,
@ -455,49 +452,51 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
0x53eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n, 0x53eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n,
0x63eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000n, 0x63eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000n,
]; ];
should('bls12-381 Point/wNAF multiplication same as unsafe (G1, W=1)', () => { describe('wNAF multiplication same as unsafe', () => {
should('(G1, W=1)', () => {
let G = PointG1.BASE.negate().negate(); // create new point let G = PointG1.BASE.negate().negate(); // create new point
G._setWindowSize(1); G._setWindowSize(1);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/wNAF multiplication same as unsafe (G1, W=4)', () => { should('(G1, W=4)', () => {
let G = PointG1.BASE.negate().negate(); let G = PointG1.BASE.negate().negate();
G._setWindowSize(4); G._setWindowSize(4);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/wNAF multiplication same as unsafe (G1, W=5)', () => { should('(G1, W=5)', () => {
let G = PointG1.BASE.negate().negate(); let G = PointG1.BASE.negate().negate();
G._setWindowSize(5); G._setWindowSize(5);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/wNAF multiplication same as unsafe (G2, W=1)', () => { should('(G2, W=1)', () => {
let G = PointG2.BASE.negate().negate(); let G = PointG2.BASE.negate().negate();
G._setWindowSize(1); G._setWindowSize(1);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/wNAF multiplication same as unsafe (G2, W=4)', () => { should('(G2, W=4)', () => {
let G = PointG2.BASE.negate().negate(); let G = PointG2.BASE.negate().negate();
G._setWindowSize(4); G._setWindowSize(4);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/wNAF multiplication same as unsafe (G2, W=5)', () => { should('(G2, W=5)', () => {
let G = PointG2.BASE.negate().negate(); let G = PointG2.BASE.negate().negate();
G._setWindowSize(5); G._setWindowSize(5);
for (let k of wNAF_VECTORS) { for (let k of wNAF_VECTORS) {
deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true); deepStrictEqual(G.multiply(k).equals(G.multiplyUnsafe(k)), true);
} }
}); });
should('bls12-381 Point/PSI cofactor cleaning same as multiplication', () => { });
should('PSI cofactor cleaning same as multiplication', () => {
const points = [ const points = [
new PointG2( new PointG2(
Fp2.fromBigTuple([ Fp2.fromBigTuple([
@ -558,14 +557,15 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
deepStrictEqual(ours.equals(shouldBe), true, 'clearLast'); deepStrictEqual(ours.equals(shouldBe), true, 'clearLast');
} }
}); });
} });
// index.ts // index.ts
// bls.PointG1.BASE.clearMultiplyPrecomputes(); // bls.PointG1.BASE.clearMultiplyPrecomputes();
// bls.PointG1.BASE.calcMultiplyPrecomputes(4); // bls.PointG1.BASE.calcMultiplyPrecomputes(4);
should('bls12-381/basic/should construct point G1 from its uncompressed form (Raw Bytes)', () => { describe('bls12-381/basic', () => {
should('construct point G1 from its uncompressed form (Raw Bytes)', () => {
// Test Zero // Test Zero
const g1 = bls.G1.Point.fromHex(B_192_40); const g1 = bls.G1.Point.fromHex(B_192_40);
deepStrictEqual(g1.x, bls.G1.Point.ZERO.x); deepStrictEqual(g1.x, bls.G1.Point.ZERO.x);
@ -590,7 +590,7 @@ should('bls12-381/basic/should construct point G1 from its uncompressed form (Ra
deepStrictEqual(g1_.y, y); deepStrictEqual(g1_.y, y);
}); });
should('bls12-381/basic/should construct point G1 from its uncompressed form (Hex)', () => { should('construct point G1 from its uncompressed form (Hex)', () => {
// Test Zero // Test Zero
const g1 = bls.G1.Point.fromHex(B_192_40); const g1 = bls.G1.Point.fromHex(B_192_40);
@ -616,7 +616,7 @@ should('bls12-381/basic/should construct point G1 from its uncompressed form (He
deepStrictEqual(g1_.y, y); deepStrictEqual(g1_.y, y);
}); });
should('bls12-381/basic/should construct point G2 from its uncompressed form (Raw Bytes)', () => { should('construct point G2 from its uncompressed form (Raw Bytes)', () => {
// Test Zero // Test Zero
const g2 = bls.G2.Point.fromHex(B_384_40); const g2 = bls.G2.Point.fromHex(B_384_40);
deepStrictEqual(g2.x, bls.G2.Point.ZERO.x, 'zero(x)'); deepStrictEqual(g2.x, bls.G2.Point.ZERO.x, 'zero(x)');
@ -647,7 +647,7 @@ should('bls12-381/basic/should construct point G2 from its uncompressed form (Ra
deepStrictEqual(g2_.y, y); deepStrictEqual(g2_.y, y);
}); });
should('bls12-381/basic/should construct point G2 from its uncompressed form (Hex)', () => { should('construct point G2 from its uncompressed form (Hex)', () => {
// Test Zero // Test Zero
const g2 = bls.G2.Point.fromHex(B_384_40); const g2 = bls.G2.Point.fromHex(B_384_40);
@ -679,7 +679,7 @@ should('bls12-381/basic/should construct point G2 from its uncompressed form (He
deepStrictEqual(g2_.y, y); deepStrictEqual(g2_.y, y);
}); });
should('bls12-381/basic/should get uncompressed form of point G1 (Raw Bytes)', () => { should('get uncompressed form of point G1 (Raw Bytes)', () => {
// Test Zero // Test Zero
deepStrictEqual(bls.G1.Point.ZERO.toHex(false), B_192_40); deepStrictEqual(bls.G1.Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero // Test Non-Zero
@ -700,7 +700,7 @@ should('bls12-381/basic/should get uncompressed form of point G1 (Raw Bytes)', (
); );
}); });
should('bls12-381/basic/should get uncompressed form of point G1 (Hex)', () => { should('get uncompressed form of point G1 (Hex)', () => {
// Test Zero // Test Zero
deepStrictEqual(bls.G1.Point.ZERO.toHex(false), B_192_40); deepStrictEqual(bls.G1.Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero // Test Non-Zero
@ -721,7 +721,7 @@ should('bls12-381/basic/should get uncompressed form of point G1 (Hex)', () => {
); );
}); });
should('bls12-381/basic/should get uncompressed form of point G2 (Raw Bytes)', () => { should('get uncompressed form of point G2 (Raw Bytes)', () => {
// Test Zero // Test Zero
deepStrictEqual(bls.G2.Point.ZERO.toHex(false), B_384_40); deepStrictEqual(bls.G2.Point.ZERO.toHex(false), B_384_40);
// Test Non-Zero // Test Non-Zero
@ -748,7 +748,7 @@ should('bls12-381/basic/should get uncompressed form of point G2 (Raw Bytes)', (
); );
}); });
should('bls12-381/basic/should get uncompressed form of point G2 (Hex)', () => { should('get uncompressed form of point G2 (Hex)', () => {
// Test Zero // Test Zero
deepStrictEqual(bls.G2.Point.ZERO.toHex(false), B_384_40); deepStrictEqual(bls.G2.Point.ZERO.toHex(false), B_384_40);
@ -776,22 +776,22 @@ should('bls12-381/basic/should get uncompressed form of point G2 (Hex)', () => {
); );
}); });
should('bls12-381/basic/should compress and decompress G1 points', async () => { should('compress and decompress G1 points', async () => {
const priv = bls.G1.Point.fromPrivateKey(42n); const priv = bls.G1.Point.fromPrivateKey(42n);
const publicKey = priv.toHex(true); const publicKey = priv.toHex(true);
const decomp = bls.G1.Point.fromHex(publicKey); const decomp = bls.G1.Point.fromHex(publicKey);
deepStrictEqual(publicKey, decomp.toHex(true)); deepStrictEqual(publicKey, decomp.toHex(true));
}); });
should('bls12-381/basic/should not compress and decompress zero G1 point', () => { should('not compress and decompress zero G1 point', () => {
throws(() => bls.G1.Point.fromPrivateKey(0n)); throws(() => bls.G1.Point.fromPrivateKey(0n));
}); });
should('bls12-381/basic/should compress and decompress G2 points', () => { should('compress and decompress G2 points', () => {
const priv = bls.G2.Point.fromPrivateKey(42n); const priv = bls.G2.Point.fromPrivateKey(42n);
const publicKey = priv.toHex(true); const publicKey = priv.toHex(true);
const decomp = bls.G2.Point.fromHex(publicKey); const decomp = bls.G2.Point.fromHex(publicKey);
deepStrictEqual(publicKey, decomp.toHex(true)); deepStrictEqual(publicKey, decomp.toHex(true));
}); });
should('bls12-381/basic/should not compress and decompress zero G2 point', () => { should('not compress and decompress zero G2 point', () => {
throws(() => bls.G2.Point.fromPrivateKey(0n)); throws(() => bls.G2.Point.fromPrivateKey(0n));
}); });
const VALID_G1 = new bls.G1.Point( const VALID_G1 = new bls.G1.Point(
@ -820,7 +820,7 @@ const INVALID_G1 = new bls.G1.Point(
) )
); );
should('bls12-381/basic/should aggregate pubkeys', () => { should('aggregate pubkeys', () => {
const agg = bls.aggregatePublicKeys([VALID_G1, VALID_G1_2]); const agg = bls.aggregatePublicKeys([VALID_G1, VALID_G1_2]);
deepStrictEqual( deepStrictEqual(
agg.x, agg.x,
@ -832,19 +832,19 @@ should('bls12-381/basic/should aggregate pubkeys', () => {
); );
}); });
should('bls12-381/basic/should not aggregate invalid pubkeys', () => { should('not aggregate invalid pubkeys', () => {
throws(() => bls.aggregatePublicKeys([VALID_G1, INVALID_G1])); throws(() => bls.aggregatePublicKeys([VALID_G1, INVALID_G1]));
}); });
// should aggregate signatures // should aggregate signatures
should(`should produce correct signatures (${G2_VECTORS.length} vectors)`, async () => { should(`produce correct signatures (${G2_VECTORS.length} vectors)`, async () => {
for (let vector of G2_VECTORS) { for (let vector of G2_VECTORS) {
const [priv, msg, expected] = vector; const [priv, msg, expected] = vector;
const sig = await bls.sign(msg, priv); const sig = await bls.sign(msg, priv);
deepStrictEqual(bls.utils.bytesToHex(sig), expected); deepStrictEqual(bls.utils.bytesToHex(sig), expected);
} }
}); });
should(`should produce correct scalars (${SCALAR_VECTORS.length} vectors)`, async () => { should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, async () => {
const options = { const options = {
p: bls.CURVE.r, p: bls.CURVE.r,
m: 1, m: 1,
@ -858,7 +858,7 @@ should(`should produce correct scalars (${SCALAR_VECTORS.length} vectors)`, asyn
deepStrictEqual(scalars[0][0], expected); deepStrictEqual(scalars[0][0], expected);
} }
}); });
should('bls12-381/basic/should verify signed message', async () => { should('verify signed message', async () => {
for (let i = 0; i < NUM_RUNS; i++) { for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G2_VECTORS[i]; const [priv, msg] = G2_VECTORS[i];
const sig = await bls.sign(msg, priv); const sig = await bls.sign(msg, priv);
@ -867,7 +867,7 @@ should('bls12-381/basic/should verify signed message', async () => {
deepStrictEqual(res, true); deepStrictEqual(res, true);
} }
}); });
should('bls12-381/basic/should not verify signature with wrong message', async () => { should('not verify signature with wrong message', async () => {
for (let i = 0; i < NUM_RUNS; i++) { for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G2_VECTORS[i]; const [priv, msg] = G2_VECTORS[i];
const invMsg = G2_VECTORS[i + 1][1]; const invMsg = G2_VECTORS[i + 1][1];
@ -877,7 +877,7 @@ should('bls12-381/basic/should not verify signature with wrong message', async (
deepStrictEqual(res, false); deepStrictEqual(res, false);
} }
}); });
should('bls12-381/basic/should not verify signature with wrong key', async () => { should('not verify signature with wrong key', async () => {
for (let i = 0; i < NUM_RUNS; i++) { for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G2_VECTORS[i]; const [priv, msg] = G2_VECTORS[i];
const sig = await bls.sign(msg, priv); const sig = await bls.sign(msg, priv);
@ -887,7 +887,7 @@ should('bls12-381/basic/should not verify signature with wrong key', async () =>
deepStrictEqual(res, false); deepStrictEqual(res, false);
} }
}); });
should('bls12-381/basic/should verify multi-signature', async () => { should('verify multi-signature', async () => {
await fc.assert( await fc.assert(
fc.asyncProperty(FC_MSG_5, FC_BIGINT_5, async (messages, privateKeys) => { fc.asyncProperty(FC_MSG_5, FC_BIGINT_5, async (messages, privateKeys) => {
privateKeys = privateKeys.slice(0, messages.length); privateKeys = privateKeys.slice(0, messages.length);
@ -901,7 +901,7 @@ should('bls12-381/basic/should verify multi-signature', async () => {
}) })
); );
}); });
should('bls12-381/basic/should batch verify multi-signatures', async () => { should('batch verify multi-signatures', async () => {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
FC_MSG_5, FC_MSG_5,
@ -926,7 +926,7 @@ should('bls12-381/basic/should batch verify multi-signatures', async () => {
) )
); );
}); });
should('bls12-381/basic/should not verify multi-signature with wrong public keys', async () => { should('not verify multi-signature with wrong public keys', async () => {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
FC_MSG_5, FC_MSG_5,
@ -951,7 +951,7 @@ should('bls12-381/basic/should not verify multi-signature with wrong public keys
) )
); );
}); });
should('bls12-381/basic/should verify multi-signature as simple signature', async () => { should('verify multi-signature as simple signature', async () => {
await fc.assert( await fc.assert(
fc.asyncProperty(FC_MSG, FC_BIGINT_5, async (message, privateKeys) => { fc.asyncProperty(FC_MSG, FC_BIGINT_5, async (message, privateKeys) => {
const publicKey = await Promise.all(privateKeys.map(getPubKey)); const publicKey = await Promise.all(privateKeys.map(getPubKey));
@ -964,7 +964,7 @@ should('bls12-381/basic/should verify multi-signature as simple signature', asyn
}) })
); );
}); });
should('bls12-381/basic/should not verify wrong multi-signature as simple signature', async () => { should('not verify wrong multi-signature as simple signature', async () => {
await fc.assert( await fc.assert(
fc.asyncProperty(FC_MSG, FC_MSG, FC_BIGINT_5, async (message, wrongMessage, privateKeys) => { fc.asyncProperty(FC_MSG, FC_MSG, FC_BIGINT_5, async (message, wrongMessage, privateKeys) => {
const publicKey = await Promise.all(privateKeys.map(getPubKey)); const publicKey = await Promise.all(privateKeys.map(getPubKey));
@ -980,34 +980,35 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
}) })
); );
}); });
});
// Pairing // Pairing
{ describe('pairing', () => {
const { pairing, Fp12 } = bls; const { pairing, Fp12 } = bls;
const G1 = bls.G1.Point.BASE; const G1 = bls.G1.Point.BASE;
const G2 = bls.G2.Point.BASE; const G2 = bls.G2.Point.BASE;
should('pairing/creates negative G1 pairing', () => { should('creates negative G1 pairing', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
const p2 = pairing(G1.negate(), G2); const p2 = pairing(G1.negate(), G2);
deepStrictEqual(Fp12.mul(p1, p2), Fp12.ONE); deepStrictEqual(Fp12.mul(p1, p2), Fp12.ONE);
}); });
should('pairing/creates negative G2 pairing', () => { should('creates negative G2 pairing', () => {
const p2 = pairing(G1.negate(), G2); const p2 = pairing(G1.negate(), G2);
const p3 = pairing(G1, G2.negate()); const p3 = pairing(G1, G2.negate());
deepStrictEqual(p2, p3); deepStrictEqual(p2, p3);
}); });
should('pairing/creates proper pairing output order', () => { should('creates proper pairing output order', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
const p2 = Fp12.pow(p1, CURVE_ORDER); const p2 = Fp12.pow(p1, CURVE_ORDER);
deepStrictEqual(p2, Fp12.ONE); deepStrictEqual(p2, Fp12.ONE);
}); });
should('pairing/G1 billinearity', () => { should('G1 billinearity', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
const p2 = pairing(G1.multiply(2n), G2); const p2 = pairing(G1.multiply(2n), G2);
deepStrictEqual(Fp12.mul(p1, p1), p2); deepStrictEqual(Fp12.mul(p1, p1), p2);
}); });
should('pairing/should not degenerate', () => { should('should not degenerate', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
const p2 = pairing(G1.multiply(2n), G2); const p2 = pairing(G1.multiply(2n), G2);
const p3 = pairing(G1, G2.negate()); const p3 = pairing(G1, G2.negate());
@ -1015,17 +1016,17 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
notDeepStrictEqual(p1, p3); notDeepStrictEqual(p1, p3);
notDeepStrictEqual(p2, p3); notDeepStrictEqual(p2, p3);
}); });
should('pairing/G2 billinearity', () => { should('G2 billinearity', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
const p2 = pairing(G1, G2.multiply(2n)); const p2 = pairing(G1, G2.multiply(2n));
deepStrictEqual(Fp12.mul(p1, p1), p2); deepStrictEqual(Fp12.mul(p1, p1), p2);
}); });
should('pairing/proper pairing composite check', () => { should('proper pairing composite check', () => {
const p1 = pairing(G1.multiply(37n), G2.multiply(27n)); const p1 = pairing(G1.multiply(37n), G2.multiply(27n));
const p2 = pairing(G1.multiply(999n), G2); const p2 = pairing(G1.multiply(999n), G2);
deepStrictEqual(p1, p2); deepStrictEqual(p1, p2);
}); });
should('pairing/vectors from https://github.com/zkcrypto/pairing', () => { should('vectors from https://github.com/zkcrypto/pairing', () => {
const p1 = pairing(G1, G2); const p1 = pairing(G1, G2);
deepStrictEqual( deepStrictEqual(
p1, p1,
@ -1045,7 +1046,7 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
]) ])
); );
}); });
should('pairing/finalExponentiate is correct', () => { should('finalExponentiate is correct', () => {
const p1 = Fp12.fromBigTwelve([ const p1 = Fp12.fromBigTwelve([
690392658038414015999440694435086329841032295415825549843130960252222448232974816207293269712691075396080336239827n, 690392658038414015999440694435086329841032295415825549843130960252222448232974816207293269712691075396080336239827n,
1673244384695948045466836192250093912021245353707563547917201356526057153141766171738038843400145227470982267854187n, 1673244384695948045466836192250093912021245353707563547917201356526057153141766171738038843400145227470982267854187n,
@ -1078,9 +1079,9 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
]) ])
); );
}); });
} });
// hashToCurve // hashToCurve
{ describe('hash-to-curve', () => {
const DST = 'QUUX-V01-CS02-with-expander-SHA256-128'; const DST = 'QUUX-V01-CS02-with-expander-SHA256-128';
const VECTORS = [ const VECTORS = [
{ {
@ -1824,9 +1825,9 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
deepStrictEqual(p.toHex(), t.expected); deepStrictEqual(p.toHex(), t.expected);
}); });
} }
} });
// Deterministic // Deterministic
{ describe('bls12-381 deterministic', () => {
// NOTE: Killic returns all items in reversed order, which looks strange: // NOTE: Killic returns all items in reversed order, which looks strange:
// instead of `Fp2(${this.c0} + ${this.c1}×i)`; it returns `Fp2(${this.c0}×i + ${this.c1})`; // instead of `Fp2(${this.c0} + ${this.c1}×i)`; it returns `Fp2(${this.c0}×i + ${this.c1})`;
const killicHex = (lst) => const killicHex = (lst) =>
@ -1934,7 +1935,7 @@ should('bls12-381/basic/should not verify wrong multi-signature as simple signat
} }
} }
}); });
} });
// ESM is broken. // ESM is broken.
import url from 'url'; import url from 'url';

@ -1,7 +1,14 @@
import { deepStrictEqual, throws } from 'assert'; import { deepEqual, deepStrictEqual, strictEqual, throws } from 'assert';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import * as fc from 'fast-check'; import * as fc from 'fast-check';
import { ed25519, ed25519ctx, ed25519ph, x25519, RistrettoPoint } from '../lib/esm/ed25519.js'; import {
ed25519,
ed25519ctx,
ed25519ph,
x25519,
RistrettoPoint,
ED25519_TORSION_SUBGROUP,
} from '../lib/esm/ed25519.js';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' }; import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils'; import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
@ -10,6 +17,7 @@ import { sha512 } from '@noble/hashes/sha512';
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' }; import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' }; import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
describe('ed25519', () => {
const ed = ed25519; const ed = ed25519;
const hex = bytesToHex; const hex = bytesToHex;
@ -27,12 +35,12 @@ function utf8ToBytes(str) {
ed.utils.precompute(8); ed.utils.precompute(8);
should('ed25519/should not accept >32byte private keys', () => { should('not accept >32byte private keys', () => {
const invalidPriv = const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n; 100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv)); throws(() => ed.getPublicKey(invalidPriv));
}); });
should('ed25519/should verify recent signature', () => { should('verify recent signature', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.hexaString({ minLength: 2, maxLength: 32 }), fc.hexaString({ minLength: 2, maxLength: 32 }),
@ -48,7 +56,7 @@ should('ed25519/should verify recent signature', () => {
{ numRuns: 5 } { numRuns: 5 }
); );
}); });
should('ed25519/should not verify signature with wrong message', () => { should('not verify signature with wrong message', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })), fc.array(fc.integer({ min: 0x00, max: 0xff })),
@ -72,38 +80,40 @@ should('ed25519/should not verify signature with wrong message', () => {
const privKey = to32Bytes('a665a45920422f9d417e4867ef'); const privKey = to32Bytes('a665a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a'); const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c'); const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
should('ed25519/basic methods/should sign and verify', () => { describe('basic methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('ed25519/basic methods/should not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
should('ed25519/basic methods/should not verify signature with wrong hash', () => { should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
}); });
});
should('ed25519/sync methods/should sign and verify', () => { describe('sync methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('ed25519/sync methods/should not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
should('ed25519/sync methods/should not verify signature with wrong hash', () => { should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
}); });
});
// https://xmr.llcoins.net/addresstests.html // https://xmr.llcoins.net/addresstests.html
should( should(
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 1', 'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 1',
@ -651,6 +661,18 @@ should('X25519 base point', () => {
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu); deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
}); });
should('isTorsionFree()', () => {
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
const dirty = orig.add(ed.Point.fromHex(hex));
const cleared = dirty.clearCofactor();
strictEqual(orig.isTorsionFree(), true, `orig must be torsionFree: ${hex}`);
strictEqual(dirty.isTorsionFree(), false, `dirty must not be torsionFree: ${hex}`);
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
}
});
});
// ESM is broken. // ESM is broken.
import url from 'url'; import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) { if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {

@ -1,5 +1,5 @@
import { deepStrictEqual, throws } from 'assert'; import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import * as fc from 'fast-check'; import * as fc from 'fast-check';
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js'; import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils'; import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
@ -7,6 +7,7 @@ import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' }; import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' }; import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
describe('ed448', () => {
const ed = ed448; const ed = ed448;
const hex = bytesToHex; const hex = bytesToHex;
ed.utils.precompute(4); ed.utils.precompute(4);
@ -323,7 +324,7 @@ for (let i = 0; i < VECTORS_RFC8032.length; i++) {
}); });
} }
should('ed448/should not accept >57byte private keys', async () => { should('not accept >57byte private keys', async () => {
const invalidPriv = const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n; 100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv)); throws(() => ed.getPublicKey(invalidPriv));
@ -334,7 +335,7 @@ function to57Bytes(numOrStr) {
return hexToBytes(hex.padStart(114, '0')); return hexToBytes(hex.padStart(114, '0'));
} }
should('ed448/should verify recent signature', () => { should('verify recent signature', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.hexaString({ minLength: 2, maxLength: 57 }), fc.hexaString({ minLength: 2, maxLength: 57 }),
@ -350,7 +351,7 @@ should('ed448/should verify recent signature', () => {
{ numRuns: 5 } { numRuns: 5 }
); );
}); });
should('ed448/should not verify signature with wrong message', () => { should('not verify signature with wrong message', () => {
fc.assert( fc.assert(
fc.property( fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })), fc.array(fc.integer({ min: 0x00, max: 0xff })),
@ -374,39 +375,43 @@ should('ed448/should not verify signature with wrong message', () => {
const privKey = to57Bytes('a665a45920422f9d417e4867ef'); const privKey = to57Bytes('a665a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a'); const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c'); const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
should('ed25519/basic methods/should sign and verify', () => { describe('basic methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('ed25519/basic methods/should not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
should('ed25519/basic methods/should not verify signature with wrong hash', () => { should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
}); });
});
should('ed25519/sync methods/should sign and verify', () => { describe('sync methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('ed25519/sync methods/should not verify signature with wrong public key', async () => { should('not verify signature with wrong public key', async () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
should('ed25519/sync methods/should not verify signature with wrong hash', async () => { should('not verify signature with wrong hash', async () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
}); });
});
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => { should('BASE_POINT.multiply() throws in Point#multiply on TEST 5', () => {
for (const num of [0n, 0, -1n, -1, 1.1]) { for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => ed.Point.BASE.multiply(num)); throws(() => ed.Point.BASE.multiply(num));
} }
@ -657,6 +662,7 @@ should('X448 base point', () => {
// const u = Fp.create(y * y * invX); // const u = Fp.create(y * y * invX);
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu); deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
}); });
});
// ESM is broken. // ESM is broken.
import url from 'url'; import url from 'url';

@ -1,5 +1,5 @@
import { deepStrictEqual } from 'assert'; import { deepStrictEqual } from 'assert';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import { bytesToHex } from '@noble/hashes/utils'; import { bytesToHex } from '@noble/hashes/utils';
// Generic tests for all curves in package // Generic tests for all curves in package
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
@ -51,9 +51,10 @@ import { default as ed448_ro } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' }; import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
function testExpandXMD(hash, vectors) { function testExpandXMD(hash, vectors) {
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
for (let i = 0; i < vectors.tests.length; i++) { for (let i = 0; i < vectors.tests.length; i++) {
const t = vectors.tests[i]; const t = vectors.tests[i];
should(`expand_message_xmd/${vectors.hash}/${vectors.DST.length}/${i}`, () => { should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
const p = expand_message_xmd( const p = expand_message_xmd(
stringToBytes(t.msg), stringToBytes(t.msg),
stringToBytes(vectors.DST), stringToBytes(vectors.DST),
@ -63,16 +64,20 @@ function testExpandXMD(hash, vectors) {
deepStrictEqual(bytesToHex(p), t.uniform_bytes); deepStrictEqual(bytesToHex(p), t.uniform_bytes);
}); });
} }
});
} }
describe('expand_message_xmd', () => {
testExpandXMD(sha256, xmd_sha256_38); testExpandXMD(sha256, xmd_sha256_38);
testExpandXMD(sha256, xmd_sha256_256); testExpandXMD(sha256, xmd_sha256_256);
testExpandXMD(sha512, xmd_sha512_38); testExpandXMD(sha512, xmd_sha512_38);
});
function testExpandXOF(hash, vectors) { function testExpandXOF(hash, vectors) {
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
for (let i = 0; i < vectors.tests.length; i++) { for (let i = 0; i < vectors.tests.length; i++) {
const t = vectors.tests[i]; const t = vectors.tests[i];
should(`expand_message_xof/${vectors.hash}/${vectors.DST.length}/${i}`, () => { should(`${i}`, () => {
const p = expand_message_xof( const p = expand_message_xof(
stringToBytes(t.msg), stringToBytes(t.msg),
stringToBytes(vectors.DST), stringToBytes(vectors.DST),
@ -83,11 +88,14 @@ function testExpandXOF(hash, vectors) {
deepStrictEqual(bytesToHex(p), t.uniform_bytes); deepStrictEqual(bytesToHex(p), t.uniform_bytes);
}); });
} }
});
} }
describe('expand_message_xof', () => {
testExpandXOF(shake128, xof_shake128_36); testExpandXOF(shake128, xof_shake128_36);
testExpandXOF(shake128, xof_shake128_256); testExpandXOF(shake128, xof_shake128_256);
testExpandXOF(shake256, xof_shake256_36); testExpandXOF(shake256, xof_shake256_36);
});
function stringToFp(s) { function stringToFp(s) {
// bls-G2 support // bls-G2 support
@ -99,9 +107,10 @@ function stringToFp(s) {
} }
function testCurve(curve, ro, nu) { function testCurve(curve, ro, nu) {
describe(`${ro.curve}/${ro.ciphersuite}`, () => {
for (let i = 0; i < ro.vectors.length; i++) { for (let i = 0; i < ro.vectors.length; i++) {
const t = ro.vectors[i]; const t = ro.vectors[i];
should(`${ro.curve}/${ro.ciphersuite}(${i})`, () => { should(`(${i})`, () => {
const p = curve.Point.hashToCurve(stringToBytes(t.msg), { const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
DST: ro.dst, DST: ro.dst,
}); });
@ -109,9 +118,11 @@ function testCurve(curve, ro, nu) {
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py'); deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
}); });
} }
});
describe(`${nu.curve}/${nu.ciphersuite}`, () => {
for (let i = 0; i < nu.vectors.length; i++) { for (let i = 0; i < nu.vectors.length; i++) {
const t = nu.vectors[i]; const t = nu.vectors[i];
should(`${nu.curve}/${nu.ciphersuite}(${i})`, () => { should(`(${i})`, () => {
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), { const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
DST: nu.dst, DST: nu.dst,
}); });
@ -119,6 +130,7 @@ function testCurve(curve, ro, nu) {
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py'); deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
}); });
} }
});
} }
testCurve(secp256r1, p256_ro, p256_nu); testCurve(secp256r1, p256_ro, p256_nu);

@ -1,5 +1,5 @@
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js'; import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import { deepStrictEqual, throws } from 'assert'; import { deepStrictEqual, throws } from 'assert';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils'; import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
@ -18,6 +18,7 @@ const G_PROOF = new jubjub.ExtendedPoint(
const getXY = (p) => ({ x: p.x, y: p.y }); const getXY = (p) => ({ x: p.x, y: p.y });
describe('jubjub', () => {
should('toHex/fromHex', () => { should('toHex/fromHex', () => {
// More than field // More than field
throws(() => throws(() =>
@ -66,6 +67,7 @@ should('Find generators', () => {
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine())); deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine())); deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
}); });
});
// ESM is broken. // ESM is broken.
import url from 'url'; import url from 'url';

@ -1,5 +1,5 @@
import { deepStrictEqual, throws } from 'assert'; import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should'; import { describe, should } from 'micro-should';
import { secp192r1, P192 } from '../lib/esm/p192.js'; import { secp192r1, P192 } from '../lib/esm/p192.js';
import { secp224r1, P224 } from '../lib/esm/p224.js'; import { secp224r1, P224 } from '../lib/esm/p224.js';
import { secp256r1, P256 } from '../lib/esm/p256.js'; import { secp256r1, P256 } from '../lib/esm/p256.js';
@ -344,10 +344,11 @@ function runWycheproof(name, CURVE, group, index) {
for (const name in WYCHEPROOF_ECDSA) { for (const name in WYCHEPROOF_ECDSA) {
const { curve, hashes } = WYCHEPROOF_ECDSA[name]; const { curve, hashes } = WYCHEPROOF_ECDSA[name];
describe('Wycheproof/WYCHEPROOF_ECDSA', () => {
for (const hName in hashes) { for (const hName in hashes) {
const { hash, tests } = hashes[hName]; const { hash, tests } = hashes[hName];
const CURVE = curve.create(hash); const CURVE = curve.create(hash);
should(`Wycheproof/WYCHEPROOF_ECDSA ${name}/${hName}`, () => { should(`${name}/${hName}`, () => {
for (let i = 0; i < tests.length; i++) { for (let i = 0; i < tests.length; i++) {
const groups = tests[i].testGroups; const groups = tests[i].testGroups;
for (let j = 0; j < groups.length; j++) { for (let j = 0; j < groups.length; j++) {
@ -357,6 +358,7 @@ for (const name in WYCHEPROOF_ECDSA) {
} }
}); });
} }
});
} }
const hexToBigint = (hex) => BigInt(`0x${hex}`); const hexToBigint = (hex) => BigInt(`0x${hex}`);

@ -7,7 +7,7 @@ import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
import { default as privates } from './vectors/privates.json' assert { type: 'json' }; import { default as privates } from './vectors/privates.json' assert { type: 'json' };
import { default as points } from './vectors/points.json' assert { type: 'json' }; import { default as points } from './vectors/points.json' assert { type: 'json' };
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' }; import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
import { should } from 'micro-should'; import { should, describe } from 'micro-should';
import { deepStrictEqual, throws } from 'assert'; import { deepStrictEqual, throws } from 'assert';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils'; import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
@ -30,7 +30,8 @@ function hexToNumber(hex) {
return BigInt(`0x${hex}`); return BigInt(`0x${hex}`);
} }
should('secp256k1.getPublicKey()', () => { describe('secp256k1', () => {
should('getPublicKey()', () => {
const data = privatesTxt const data = privatesTxt
.split('\n') .split('\n')
.filter((line) => line) .filter((line) => line)
@ -49,12 +50,12 @@ should('secp256k1.getPublicKey()', () => {
deepStrictEqual(toBEHex(point3.y), y); deepStrictEqual(toBEHex(point3.y), y);
} }
}); });
should('secp256k1.getPublicKey() rejects invalid keys', () => { should('getPublicKey() rejects invalid keys', () => {
for (const item of INVALID_ITEMS) { for (const item of INVALID_ITEMS) {
throws(() => secp.getPublicKey(item)); throws(() => secp.getPublicKey(item));
} }
}); });
should('secp256k1.precompute', () => { should('precompute', () => {
secp.utils.precompute(4); secp.utils.precompute(4);
const data = privatesTxt const data = privatesTxt
.split('\n') .split('\n')
@ -75,7 +76,7 @@ should('secp256k1.precompute', () => {
} }
}); });
should('secp256k1.Point.isValidPoint()', () => { should('Point.isValidPoint()', () => {
for (const vector of points.valid.isPoint) { for (const vector of points.valid.isPoint) {
const { P, expected } = vector; const { P, expected } = vector;
if (expected) { if (expected) {
@ -86,7 +87,7 @@ should('secp256k1.Point.isValidPoint()', () => {
} }
}); });
should('secp256k1.Point.fromPrivateKey()', () => { should('Point.fromPrivateKey()', () => {
for (const vector of points.valid.pointFromScalar) { for (const vector of points.valid.pointFromScalar) {
const { d, expected } = vector; const { d, expected } = vector;
let p = secp.Point.fromPrivateKey(d); let p = secp.Point.fromPrivateKey(d);
@ -94,7 +95,7 @@ should('secp256k1.Point.fromPrivateKey()', () => {
} }
}); });
should('secp256k1.Point#toHex(compressed)', () => { should('Point#toHex(compressed)', () => {
for (const vector of points.valid.pointCompress) { for (const vector of points.valid.pointCompress) {
const { P, compress, expected } = vector; const { P, compress, expected } = vector;
let p = secp.Point.fromHex(P); let p = secp.Point.fromHex(P);
@ -102,7 +103,7 @@ should('secp256k1.Point#toHex(compressed)', () => {
} }
}); });
should('secp256k1.Point#toHex() roundtrip (failed case)', () => { should('Point#toHex() roundtrip (failed case)', () => {
const point1 = const point1 =
secp.Point.fromPrivateKey( secp.Point.fromPrivateKey(
88572218780422190464634044548753414301110513745532121983949500266768436236425n 88572218780422190464634044548753414301110513745532121983949500266768436236425n
@ -111,7 +112,7 @@ should('secp256k1.Point#toHex() roundtrip (failed case)', () => {
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex); // deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
}); });
should('secp256k1.Point#toHex() roundtrip', () => { should('Point#toHex() roundtrip', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (x) => { fc.property(FC_BIGINT, (x) => {
const point1 = secp.Point.fromPrivateKey(x); const point1 = secp.Point.fromPrivateKey(x);
@ -121,7 +122,7 @@ should('secp256k1.Point#toHex() roundtrip', () => {
); );
}); });
should('secp256k1.Point#add(other)', () => { should('Point#add(other)', () => {
for (const vector of points.valid.pointAdd) { for (const vector of points.valid.pointAdd) {
const { P, Q, expected } = vector; const { P, Q, expected } = vector;
let p = secp.Point.fromHex(P); let p = secp.Point.fromHex(P);
@ -136,7 +137,7 @@ should('secp256k1.Point#add(other)', () => {
} }
}); });
should('secp256k1.Point#multiply(privateKey)', () => { should('Point#multiply(privateKey)', () => {
for (const vector of points.valid.pointMultiply) { for (const vector of points.valid.pointMultiply) {
const { P, d, expected } = vector; const { P, d, expected } = vector;
const p = secp.Point.fromHex(P); const p = secp.Point.fromHex(P);
@ -175,7 +176,7 @@ should('secp256k1.Point#multiply(privateKey)', () => {
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0]) // console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
// }); // });
should('secp256k1.Signature.fromCompactHex() roundtrip', () => { should('Signature.fromCompactHex() roundtrip', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => { fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new secp.Signature(r, s); const sig = new secp.Signature(r, s);
@ -184,7 +185,7 @@ should('secp256k1.Signature.fromCompactHex() roundtrip', () => {
); );
}); });
should('secp256k1.Signature.fromDERHex() roundtrip', () => { should('Signature.fromDERHex() roundtrip', () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => { fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new secp.Signature(r, s); const sig = new secp.Signature(r, s);
@ -193,7 +194,7 @@ should('secp256k1.Signature.fromDERHex() roundtrip', () => {
); );
}); });
should('secp256k1.sign()/should create deterministic signatures with RFC 6979', () => { should('sign()/should create deterministic signatures with RFC 6979', () => {
for (const vector of ecdsa.valid) { for (const vector of ecdsa.valid) {
let usig = secp.sign(vector.m, vector.d); let usig = secp.sign(vector.m, vector.d);
let sig = usig.toCompactHex(); let sig = usig.toCompactHex();
@ -203,18 +204,21 @@ should('secp256k1.sign()/should create deterministic signatures with RFC 6979',
} }
}); });
should('secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979', () => { should(
'secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979',
() => {
for (const vector of ecdsa.invalid.sign) { for (const vector of ecdsa.invalid.sign) {
throws(() => secp.sign(vector.m, vector.d)); throws(() => secp.sign(vector.m, vector.d));
} }
}); }
);
should('secp256k1.sign()/edge cases', () => { should('sign()/edge cases', () => {
throws(() => secp.sign()); throws(() => secp.sign());
throws(() => secp.sign('')); throws(() => secp.sign(''));
}); });
should('secp256k1.sign()/should create correct DER encoding against libsecp256k1', () => { should('sign()/should create correct DER encoding against libsecp256k1', () => {
const CASES = [ const CASES = [
[ [
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b', 'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
@ -237,7 +241,7 @@ should('secp256k1.sign()/should create correct DER encoding against libsecp256k1
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp); deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
} }
}); });
should('secp256k1.sign()/sign ecdsa extraData', () => { should('sign()/sign ecdsa extraData', () => {
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000'; const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001'; const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33'; const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
@ -258,7 +262,7 @@ should('secp256k1.sign()/sign ecdsa extraData', () => {
} }
}); });
should('secp256k1.verify()/should verify signature', () => { should('verify()/should verify signature', () => {
const MSG = '01'.repeat(32); const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n; const PRIV_KEY = 0x2n;
const signature = secp.sign(MSG, PRIV_KEY); const signature = secp.sign(MSG, PRIV_KEY);
@ -266,7 +270,7 @@ should('secp256k1.verify()/should verify signature', () => {
deepStrictEqual(publicKey.length, 65); deepStrictEqual(publicKey.length, 65);
deepStrictEqual(secp.verify(signature, MSG, publicKey), true); deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
}); });
should('secp256k1.verify()/should not verify signature with wrong public key', () => { should('verify()/should not verify signature with wrong public key', () => {
const MSG = '01'.repeat(32); const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n; const PRIV_KEY = 0x2n;
const WRONG_PRIV_KEY = 0x22n; const WRONG_PRIV_KEY = 0x22n;
@ -275,7 +279,7 @@ should('secp256k1.verify()/should not verify signature with wrong public key', (
deepStrictEqual(publicKey.length, 130); deepStrictEqual(publicKey.length, 130);
deepStrictEqual(secp.verify(signature, MSG, publicKey), false); deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
}); });
should('secp256k1.verify()/should not verify signature with wrong hash', () => { should('verify()/should not verify signature with wrong hash', () => {
const MSG = '01'.repeat(32); const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n; const PRIV_KEY = 0x2n;
const WRONG_MSG = '11'.repeat(32); const WRONG_MSG = '11'.repeat(32);
@ -284,7 +288,7 @@ should('secp256k1.verify()/should not verify signature with wrong hash', () => {
deepStrictEqual(publicKey.length, 65); deepStrictEqual(publicKey.length, 65);
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false); deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
}); });
should('secp256k1.verify()/should verify random signatures', () => should('verify()/should verify random signatures', () =>
fc.assert( fc.assert(
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => { fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
const pub = secp.getPublicKey(privKey); const pub = secp.getPublicKey(privKey);
@ -293,10 +297,11 @@ should('secp256k1.verify()/should verify random signatures', () =>
}) })
) )
); );
should('secp256k1.verify()/should not verify signature with invalid r/s', () => { should('verify()/should not verify signature with invalid r/s', () => {
const msg = new Uint8Array([ const msg = new Uint8Array([
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1, 0xe3, 0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1,
0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60, 0x50, 0x23, 0xe3, 0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60,
0x50, 0x23,
]); ]);
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n; const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n; const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
@ -312,7 +317,7 @@ should('secp256k1.verify()/should not verify signature with invalid r/s', () =>
// Verifies, but it shouldn't, because signature S > curve order // Verifies, but it shouldn't, because signature S > curve order
deepStrictEqual(verified, false); deepStrictEqual(verified, false);
}); });
should('secp256k1.verify()/should not verify msg = curve order', () => { should('verify()/should not verify msg = curve order', () => {
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'; const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n; const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n; const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
@ -322,7 +327,7 @@ should('secp256k1.verify()/should not verify msg = curve order', () => {
const sig = new secp.Signature(r, s); const sig = new secp.Signature(r, s);
deepStrictEqual(secp.verify(sig, msg, pub), false); deepStrictEqual(secp.verify(sig, msg, pub), false);
}); });
should('secp256k1.verify()/should verify non-strict msg bb5a...', () => { should('verify()/should verify non-strict msg bb5a...', () => {
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023'; const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n; const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n; const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
@ -362,7 +367,7 @@ for (let vec of vectors) {
}); });
} }
should('secp256k1.recoverPublicKey()/should recover public key from recovery bit', () => { should('recoverPublicKey()/should recover public key from recovery bit', () => {
const message = '00000000000000000000000000000000000000000000000000000000deadbeef'; const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
const privateKey = 123456789n; const privateKey = 123456789n;
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false); const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
@ -373,14 +378,14 @@ should('secp256k1.recoverPublicKey()/should recover public key from recovery bit
deepStrictEqual(recoveredPubkey.toHex(false), publicKey); deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
deepStrictEqual(secp.verify(sig, message, publicKey), true); deepStrictEqual(secp.verify(sig, message, publicKey), true);
}); });
should('secp256k1.recoverPublicKey()/should not recover zero points', () => { should('recoverPublicKey()/should not recover zero points', () => {
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9'; const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
const sig = const sig =
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9'; '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
const recovery = 0; const recovery = 0;
throws(() => secp.recoverPublicKey(msgHash, sig, recovery)); throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
}); });
should('secp256k1.recoverPublicKey()/should handle all-zeros msghash', () => { should('recoverPublicKey()/should handle all-zeros msghash', () => {
const privKey = secp.utils.randomPrivateKey(); const privKey = secp.utils.randomPrivateKey();
const pub = secp.getPublicKey(privKey); const pub = secp.getPublicKey(privKey);
const zeros = '0000000000000000000000000000000000000000000000000000000000000000'; const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
@ -388,7 +393,7 @@ should('secp256k1.recoverPublicKey()/should handle all-zeros msghash', () => {
const recoveredKey = sig.recoverPublicKey(zeros); const recoveredKey = sig.recoverPublicKey(zeros);
deepStrictEqual(recoveredKey.toRawBytes(), pub); deepStrictEqual(recoveredKey.toRawBytes(), pub);
}); });
should('secp256k1.recoverPublicKey()/should handle RFC 6979 vectors', () => { should('recoverPublicKey()/should handle RFC 6979 vectors', () => {
for (const vector of ecdsa.valid) { for (const vector of ecdsa.valid) {
let usig = secp.sign(vector.m, vector.d); let usig = secp.sign(vector.m, vector.d);
let sig = usig.toDERHex(); let sig = usig.toDERHex();
@ -402,7 +407,7 @@ should('secp256k1.recoverPublicKey()/should handle RFC 6979 vectors', () => {
function derToPub(der) { function derToPub(der) {
return der.slice(46); return der.slice(46);
} }
should('secp256k1.getSharedSecret()/should produce correct results', () => { should('getSharedSecret()/should produce correct results', () => {
// TODO: Once der is there, run all tests. // TODO: Once der is there, run all tests.
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) { for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
if (vector.result === 'invalid' || vector.private.length !== 64) { if (vector.result === 'invalid' || vector.private.length !== 64) {
@ -415,7 +420,7 @@ should('secp256k1.getSharedSecret()/should produce correct results', () => {
} }
} }
}); });
should('secp256k1.getSharedSecret()/priv/pub order matters', () => { should('getSharedSecret()/priv/pub order matters', () => {
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) { for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
if (vector.result === 'valid') { if (vector.result === 'valid') {
let priv = vector.private; let priv = vector.private;
@ -424,11 +429,11 @@ should('secp256k1.getSharedSecret()/priv/pub order matters', () => {
} }
} }
}); });
should('secp256k1.getSharedSecret()/rejects invalid keys', () => { should('getSharedSecret()/rejects invalid keys', () => {
throws(() => secp.getSharedSecret('01', '02')); throws(() => secp.getSharedSecret('01', '02'));
}); });
should('secp256k1.utils.isValidPrivateKey()', () => { should('utils.isValidPrivateKey()', () => {
for (const vector of privates.valid.isPrivate) { for (const vector of privates.valid.isPrivate) {
const { d, expected } = vector; const { d, expected } = vector;
deepStrictEqual(secp.utils.isValidPrivateKey(d), expected); deepStrictEqual(secp.utils.isValidPrivateKey(d), expected);
@ -472,45 +477,45 @@ const tweakUtils = {
}, },
}; };
should('secp256k1.privateAdd()', () => { should('privateAdd()', () => {
for (const vector of privates.valid.add) { for (const vector of privates.valid.add) {
const { a, b, expected } = vector; const { a, b, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected); deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
} }
}); });
should('secp256k1.privateNegate()', () => { should('privateNegate()', () => {
for (const vector of privates.valid.negate) { for (const vector of privates.valid.negate) {
const { a, expected } = vector; const { a, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected); deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
} }
}); });
should('secp256k1.pointAddScalar()', () => { should('pointAddScalar()', () => {
for (const vector of points.valid.pointAddScalar) { for (const vector of points.valid.pointAddScalar) {
const { description, P, d, expected } = vector; const { description, P, d, expected } = vector;
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected); deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
} }
}); });
should('secp256k1.pointAddScalar() invalid', () => { should('pointAddScalar() invalid', () => {
for (const vector of points.invalid.pointAddScalar) { for (const vector of points.invalid.pointAddScalar) {
const { P, d, exception } = vector; const { P, d, exception } = vector;
throws(() => tweakUtils.pointAddScalar(P, d)); throws(() => tweakUtils.pointAddScalar(P, d));
} }
}); });
should('secp256k1.pointMultiply()', () => { should('pointMultiply()', () => {
for (const vector of points.valid.pointMultiply) { for (const vector of points.valid.pointMultiply) {
const { P, d, expected } = vector; const { P, d, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected); deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
} }
}); });
should('secp256k1.pointMultiply() invalid', () => { should('pointMultiply() invalid', () => {
for (const vector of points.invalid.pointMultiply) { for (const vector of points.invalid.pointMultiply) {
const { P, d, exception } = vector; const { P, d, exception } = vector;
throws(() => tweakUtils.pointMultiply(P, d)); throws(() => tweakUtils.pointMultiply(P, d));
} }
}); });
should('secp256k1.wychenproof vectors', () => { should('wychenproof vectors', () => {
for (let group of wp.testGroups) { for (let group of wp.testGroups) {
const pubKey = secp.Point.fromHex(group.key.uncompressed); const pubKey = secp.Point.fromHex(group.key.uncompressed);
for (let test of group.tests) { for (let test of group.tests) {
@ -537,6 +542,7 @@ should('secp256k1.wychenproof vectors', () => {
} }
} }
}); });
});
// ESM is broken. // ESM is broken.
import url from 'url'; import url from 'url';