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,233 +62,235 @@ 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, () => {
fc.assert( should('equality', () => {
fc.property(FC_BIGINT, (num) => {
const a = create(num);
const b = create(num);
deepStrictEqual(Fp.equals(a, b), true);
deepStrictEqual(Fp.equals(b, a), true);
})
);
});
should(`${name} non-equality`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.equals(a, b), num1 === num2);
deepStrictEqual(Fp.equals(b, a), num1 === num2);
})
);
});
should(`${name} add/subtract/commutativity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
})
);
});
should(`${name} add/subtract/associativity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
})
);
});
should(`${name} add/subtract/x+0=x`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
})
);
});
should(`${name} add/subtract/x-0=x`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
})
);
});
should(`${name} add/subtract/negate equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num1) => {
const a = create(num1);
const b = create(num1);
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.negate(a));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
})
);
});
should(`${name} add/subtract/negate`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
})
);
});
should(`${name} negate(0)`, () => {
deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO);
});
should(`${name} multiply/commutativity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
})
);
});
should(`${name} multiply/associativity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
})
);
});
should(`${name} multiply/distributivity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
})
);
});
should(`${name} multiply/add equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.mul(a, 0n), Fp.ZERO);
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(a, 1n), a);
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
deepStrictEqual(Fp.mul(a, 2n), Fp.add(a, a));
deepStrictEqual(Fp.mul(a, 3n), Fp.add(Fp.add(a, a), a));
deepStrictEqual(Fp.mul(a, 4n), Fp.add(Fp.add(Fp.add(a, a), a), a));
})
);
});
should(`${name} multiply/square equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
})
);
});
should(`${name} multiply/pow equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.pow(a, 0n), Fp.ONE);
deepStrictEqual(Fp.pow(a, 1n), a);
deepStrictEqual(Fp.pow(a, 2n), Fp.mul(a, a));
deepStrictEqual(Fp.pow(a, 3n), Fp.mul(Fp.mul(a, a), a));
})
);
});
should(`${name} square(0)`, () => {
deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
});
should(`${name} square(1)`, () => {
deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE);
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
});
should(`${name} square(-1)`, () => {
const minus1 = Fp.negate(Fp.ONE);
deepStrictEqual(Fp.square(minus1), Fp.ONE);
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
});
const isSquare = mod.FpIsSquare(Fp);
// Not implemented
if (Fp !== bls12_381.CURVE.Fp12) {
should(`${name} multiply/sqrt`, () => {
fc.assert( fc.assert(
fc.property(FC_BIGINT, (num) => { fc.property(FC_BIGINT, (num) => {
const a = create(num); const a = create(num);
let root; const b = create(num);
try { deepStrictEqual(Fp.equals(a, b), true);
root = Fp.sqrt(a); deepStrictEqual(Fp.equals(b, a), true);
} catch (e) { })
deepStrictEqual(isSquare(a), false); );
return; });
} should('non-equality', () => {
deepStrictEqual(isSquare(a), true); fc.assert(
deepStrictEqual(Fp.equals(Fp.square(root), a), true, 'sqrt(a)^2 == a'); fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
deepStrictEqual(Fp.equals(Fp.square(Fp.negate(root)), a), true, '(-sqrt(a))^2 == a'); const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.equals(a, b), num1 === num2);
deepStrictEqual(Fp.equals(b, a), num1 === num2);
})
);
});
should('add/subtract/commutativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
})
);
});
should('add/subtract/associativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
})
);
});
should('add/subtract/x+0=x', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
})
);
});
should('add/subtract/x-0=x', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
})
);
});
should('add/subtract/negate equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num1) => {
const a = create(num1);
const b = create(num1);
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.negate(a));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
})
);
});
should('add/subtract/negate', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
})
);
});
should('negate(0)', () => {
deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO);
});
should('multiply/commutativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
})
);
});
should('multiply/associativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
})
);
});
should('multiply/distributivity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
})
);
});
should('multiply/add equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.mul(a, 0n), Fp.ZERO);
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(a, 1n), a);
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
deepStrictEqual(Fp.mul(a, 2n), Fp.add(a, a));
deepStrictEqual(Fp.mul(a, 3n), Fp.add(Fp.add(a, a), a));
deepStrictEqual(Fp.mul(a, 4n), Fp.add(Fp.add(Fp.add(a, a), a), a));
})
);
});
should('multiply/square equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
})
);
});
should('multiply/pow equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.pow(a, 0n), Fp.ONE);
deepStrictEqual(Fp.pow(a, 1n), a);
deepStrictEqual(Fp.pow(a, 2n), Fp.mul(a, a));
deepStrictEqual(Fp.pow(a, 3n), Fp.mul(Fp.mul(a, a), a));
}) })
); );
}); });
should(`${name} sqrt(0)`, () => { should('square(0)', () => {
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO);
const sqrt1 = Fp.sqrt(Fp.ONE); deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
deepStrictEqual( });
Fp.equals(sqrt1, Fp.ONE) || Fp.equals(sqrt1, Fp.negate(Fp.ONE)),
true, should('square(1)', () => {
'sqrt(1) = 1 or -1' deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE);
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
});
should('square(-1)', () => {
const minus1 = Fp.negate(Fp.ONE);
deepStrictEqual(Fp.square(minus1), Fp.ONE);
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
});
const isSquare = mod.FpIsSquare(Fp);
// Not implemented
if (Fp !== bls12_381.CURVE.Fp12) {
should('multiply/sqrt', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
let root;
try {
root = Fp.sqrt(a);
} catch (e) {
deepStrictEqual(isSquare(a), false);
return;
}
deepStrictEqual(isSquare(a), true);
deepStrictEqual(Fp.equals(Fp.square(root), a), true, 'sqrt(a)^2 == a');
deepStrictEqual(Fp.equals(Fp.square(Fp.negate(root)), a), true, '(-sqrt(a))^2 == a');
})
);
});
should('sqrt(0)', () => {
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
const sqrt1 = Fp.sqrt(Fp.ONE);
deepStrictEqual(
Fp.equals(sqrt1, Fp.ONE) || Fp.equals(sqrt1, Fp.negate(Fp.ONE)),
true,
'sqrt(1) = 1 or -1'
);
});
}
should('div/division by one equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
if (Fp.equals(a, Fp.ZERO)) return; // No division by zero
deepStrictEqual(Fp.div(a, Fp.ONE), a);
deepStrictEqual(Fp.div(a, a), Fp.ONE);
})
);
});
should('zero division equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
})
);
});
should('div/division distributivity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
})
);
});
should('div/division and multiplication equality', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.invert(b)));
})
); );
}); });
}
should(`${name} div/division by one equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
if (Fp.equals(a, Fp.ZERO)) return; // No division by zero
deepStrictEqual(Fp.div(a, Fp.ONE), a);
deepStrictEqual(Fp.div(a, a), Fp.ONE);
})
);
});
should(`${name} zero division equality`, () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
})
);
});
should(`${name} div/division distributivity`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
})
);
});
should(`${name} div/division and multiplication equality`, () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.invert(b)));
})
);
}); });
} }
} }
@ -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,256 +343,270 @@ 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));
// Here we check basic group laws, to verify that points works as group const title = `${name}/${pointName}`;
should(`${name}/${pointName}/Basic group laws (zero)`, () => { describe(title, () => {
equal(G[0].double(), G[0], '(0*G).double() = 0'); describe('basic group laws', () => {
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0'); // Here we check basic group laws, to verify that points works as group
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0'); should('(zero)', () => {
equal(G[0].negate(), G[0], '-0 = 0'); equal(G[0].double(), G[0], '(0*G).double() = 0');
for (let i = 0; i < G.length; i++) { equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
const p = G[i]; equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`); equal(G[0].negate(), G[0], '-0 = 0');
equal(G[0].multiply(i + 1), G[0], `${i + 1}*0 = 0`); for (let i = 0; i < G.length; i++) {
const p = G[i];
equal(p, p.add(G[0]), '${i}*G + 0 = ${i}*G');
equal(G[0].multiply(i + 1), G[0], '${i + 1}*0 = 0');
}
});
should('(one)', () => {
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].add(G[1]), G[2], '1*G + 1*G = 2*G');
});
should('(sanity tests)', () => {
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[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
});
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[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
});
should('(double)', () => {
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
});
should('(multiply)', () => {
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
});
should('(same point addition)', () => {
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
});
should('(same point (negative) addition)', () => {
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');
});
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[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');
const half = CURVE_ORDER / 2n;
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');
});
should('(inversion)', () => {
const a = 1234n;
const b = 5678n;
const c = a * b;
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
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');
});
should('(multiply, rand)', () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a + b, CURVE_ORDER);
if (c === CURVE_ORDER || c < 1n) return;
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
const pC = G[1].multiply(c);
equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
equal(pA.add(pB), pC, 'pA + pB = pC');
}),
{ numRuns: NUM_RUNS }
)
);
should('(multiply2, rand)', () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a * b, CURVE_ORDER);
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
}),
{ numRuns: NUM_RUNS }
)
);
});
for (const op of ['add', 'subtract']) {
describe(op, () => {
should('type check', () => {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
G[1][op](G[2]);
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
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](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([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}');
throws(() => G[1][op](o.BASE), '${op}/other curve point');
});
});
}
should('equals type check', () => {
throws(() => G[1].equals(0), '0');
throws(() => G[1].equals(0n), '0n');
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1].equals(123.456), '123.456');
throws(() => G[1].equals(true), 'true');
throws(() => G[1].equals('1'), "'1'");
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([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})');
throws(() => G[1].equals(o.BASE), 'other curve point');
});
for (const op of ['multiply', 'multiplyUnsafe']) {
if (!p.BASE[op]) continue;
describe(op, () => {
should('type check', () => {
if (op !== 'multiplyUnsafe') {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
}
G[1][op](1n);
G[1][op](CURVE_ORDER - 1n);
throws(() => G[1][op](G[2]), 'G[2]');
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), '1');
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
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](o.BASE), 'other curve point');
});
});
}
// Complex point (Extended/Jacobian/Projective?)
if (p.BASE.toAffine) {
should('toAffine()', () => {
equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
});
}
if (p.fromAffine) {
should('fromAffine()', () => {
equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
});
}
// toHex/fromHex (if available)
if (p.fromHex && p.BASE.toHex) {
should('fromHex(toHex()) roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const hex = p.BASE.multiply(x).toHex();
deepStrictEqual(p.fromHex(hex).toHex(), hex);
})
);
});
} }
}); });
should(`${name}/${pointName}/Basic group laws (one)`, () => { }
equal(G[1].double(), G[2], '(1*G).double() = 2*G'); describe(name, () => {
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0'); // Generic complex things (getPublicKey/sign/verify/getSharedSecret)
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G'); should('getPublicKey type check', () => {
throws(() => C.getPublicKey(0), '0');
throws(() => C.getPublicKey(0n), '0n');
throws(() => C.getPublicKey(false), 'false');
throws(() => C.getPublicKey(123.456), '123.456');
throws(() => C.getPublicKey(true), 'true');
throws(() => C.getPublicKey(''), "''");
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
//throws(() => C.getPublicKey('1'), "'1'");
throws(() => C.getPublicKey('key'), "'key'");
throws(() => C.getPublicKey(new Uint8Array([])));
throws(() => C.getPublicKey(new Uint8Array([0])));
throws(() => C.getPublicKey(new Uint8Array([1])));
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
}); });
should(`${name}/${pointName}/Basic group laws (sanity tests)`, () => { should('.verify() should verify random signatures', () =>
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[7].add(G[3].negate()), G[4], `7*G - 3*G = 4*G`);
});
should(`${name}/${pointName}/Basic group laws (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[2]).add(G[2]), `4*G + 3*G = 3*G + 2*G + 2*G`);
});
should(`${name}/${pointName}/Basic group laws (double)`, () => {
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
});
should(`${name}/${pointName}/Basic group laws (multiply)`, () => {
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
});
should(`${name}/${pointName}/Basic group laws (same point addition)`, () => {
equal(G[3].add(G[3]), G[6], `3*G + 3*G = 6*G`);
});
should(`${name}/${pointName}/Basic group laws (same point (negative) addition)`, () => {
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');
});
should(`${name}/${pointName}/Basic group laws (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[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');
const half = CURVE_ORDER / 2n;
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');
});
should(`${name}/${pointName}/Basic group laws (inversion)`, () => {
const a = 1234n;
const b = 5678n;
const c = a * b;
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
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');
});
should(`${name}/${pointName}/Basic group laws (multiply, rand)`, () =>
fc.assert( fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const c = mod.mod(a + b, CURVE_ORDER); const priv = C.utils.randomPrivateKey();
if (c === CURVE_ORDER || c < 1n) return; const pub = C.getPublicKey(priv);
const pA = G[1].multiply(a); const sig = C.sign(msg, priv);
const pB = G[1].multiply(b); deepStrictEqual(
const pC = G[1].multiply(c); C.verify(sig, msg, pub),
equal(pA.add(pB), pB.add(pA), `pA + pB = pB + pA`); true,
equal(pA.add(pB), pC, `pA + pB = pC`); 'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
);
}), }),
{ numRuns: NUM_RUNS } { numRuns: NUM_RUNS }
) )
); );
should(`${name}/${pointName}/Basic group laws (multiply2, rand)`, () => should('.sign() edge cases', () => {
fc.assert( throws(() => C.sign());
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { throws(() => C.sign(''));
const c = mod.mod(a * b, CURVE_ORDER);
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
equal(pA.multiply(b), pB.multiply(a), `b*pA = a*pB`);
equal(pA.multiply(b), G[1].multiply(c), `b*pA = c*G`);
}),
{ numRuns: NUM_RUNS }
)
);
for (const op of ['add', 'subtract']) {
should(`${name}/${pointName}/${op} type check`, () => {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
G[1][op](G[2]);
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
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](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([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}`);
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
});
}
should(`${name}/${pointName}/equals type check`, () => {
throws(() => G[1].equals(0), '0');
throws(() => G[1].equals(0n), '0n');
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1].equals(123.456), '123.456');
throws(() => G[1].equals(true), 'true');
throws(() => G[1].equals('1'), "'1'");
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([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})`);
throws(() => G[1].equals(o.BASE), 'other curve point');
}); });
for (const op of ['multiply', 'multiplyUnsafe']) { should('.verify() should not verify signature with wrong hash', () => {
if (!p.BASE[op]) continue; const MSG = '01'.repeat(32);
should(`${name}/${pointName}/${op} type check`, () => { const PRIV_KEY = 0x2n;
if (op !== 'multiplyUnsafe') { const WRONG_MSG = '11'.repeat(32);
throws(() => G[1][op](0), '0'); const signature = C.sign(MSG, PRIV_KEY);
throws(() => G[1][op](0n), '0n'); const publicKey = C.getPublicKey(PRIV_KEY);
} deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
G[1][op](1n);
G[1][op](CURVE_ORDER - 1n);
throws(() => G[1][op](G[2]), 'G[2]');
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), '1');
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
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](o.BASE), 'other curve point');
});
}
// Complex point (Extended/Jacobian/Projective?)
if (p.BASE.toAffine) {
should(`${name}/${pointName}/toAffine()`, () => {
equal(p.ZERO.toAffine(), C.Point.ZERO, `0 = 0`);
equal(p.BASE.toAffine(), C.Point.BASE, `1 = 1`);
});
}
if (p.fromAffine) {
should(`${name}/${pointName}/fromAffine()`, () => {
equal(p.ZERO, p.fromAffine(C.Point.ZERO), `0 = 0`);
equal(p.BASE, p.fromAffine(C.Point.BASE), `1 = 1`);
});
}
// toHex/fromHex (if available)
if (p.fromHex && p.BASE.toHex) {
should(`${name}/${pointName}/fromHex(toHex()) roundtrip`, () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const hex = p.BASE.multiply(x).toHex();
deepStrictEqual(p.fromHex(hex).toHex(), hex);
})
);
});
}
}
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should(`${name}/getPublicKey type check`, () => {
throws(() => C.getPublicKey(0), '0');
throws(() => C.getPublicKey(0n), '0n');
throws(() => C.getPublicKey(false), 'false');
throws(() => C.getPublicKey(123.456), '123.456');
throws(() => C.getPublicKey(true), 'true');
throws(() => C.getPublicKey(''), "''");
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
//throws(() => C.getPublicKey('1'), "'1'");
throws(() => C.getPublicKey('key'), "'key'");
throws(() => C.getPublicKey(new Uint8Array([])));
throws(() => C.getPublicKey(new Uint8Array([0])));
throws(() => C.getPublicKey(new Uint8Array([1])));
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
});
should(`${name}.verify()/should verify random signatures`, () =>
fc.assert(
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const priv = C.utils.randomPrivateKey();
const pub = C.getPublicKey(priv);
const sig = C.sign(msg, priv);
deepStrictEqual(
C.verify(sig, msg, pub),
true,
`priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}`
);
}),
{ numRuns: NUM_RUNS }
)
);
should(`${name}.sign()/edge cases`, () => {
throws(() => C.sign());
throws(() => C.sign(''));
});
should(`${name}.verify()/should not verify signature with wrong hash`, () => {
const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n;
const WRONG_MSG = '11'.repeat(32);
const signature = C.sign(MSG, PRIV_KEY);
const publicKey = C.getPublicKey(PRIV_KEY);
deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
});
// 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?
// should(`${name}/should not verify signature with wrong message`, () => {
// fc.assert(
// fc.property(
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// (bytes, wrongBytes) => {
// const privKey = C.utils.randomPrivateKey();
// const message = new Uint8Array(bytes);
// const wrongMessage = new Uint8Array(wrongBytes);
// const publicKey = C.getPublicKey(privKey);
// const signature = C.sign(message, privKey);
// deepStrictEqual(
// C.verify(signature, wrongMessage, publicKey),
// bytes.toString() === wrongBytes.toString()
// );
// }
// ),
// { numRuns: NUM_RUNS }
// );
// });
if (C.getSharedSecret) {
should(`${name}/getSharedSecret() should be commutative`, () => {
for (let i = 0; i < NUM_RUNS; i++) {
const asec = C.utils.randomPrivateKey();
const apub = C.getPublicKey(asec);
const bsec = C.utils.randomPrivateKey();
const bpub = C.getPublicKey(bsec);
try {
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
} catch (error) {
console.error('not commutative', { asec, apub, bsec, bpub });
throw error;
}
}
}); });
} // 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?
// should('should not verify signature with wrong message', () => {
// fc.assert(
// fc.property(
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// (bytes, wrongBytes) => {
// const privKey = C.utils.randomPrivateKey();
// const message = new Uint8Array(bytes);
// const wrongMessage = new Uint8Array(wrongBytes);
// const publicKey = C.getPublicKey(privKey);
// const signature = C.sign(message, privKey);
// deepStrictEqual(
// C.verify(signature, wrongMessage, publicKey),
// bytes.toString() === wrongBytes.toString()
// );
// }
// ),
// { numRuns: NUM_RUNS }
// );
// });
if (C.getSharedSecret) {
should('getSharedSecret() should be commutative', () => {
for (let i = 0; i < NUM_RUNS; i++) {
const asec = C.utils.randomPrivateKey();
const apub = C.getPublicKey(asec);
const bsec = C.utils.randomPrivateKey();
const bpub = C.getPublicKey(bsec);
try {
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
} catch (error) {
console.error('not commutative', { asec, apub, bsec, bpub });
throw error;
}
}
});
}
});
} }
should('secp224k1 sqrt bug', () => { should('secp224k1 sqrt bug', () => {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -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,43 +51,51 @@ 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) {
for (let i = 0; i < vectors.tests.length; i++) { describe(`${vectors.hash}/${vectors.DST.length}`, () => {
const t = vectors.tests[i]; for (let i = 0; i < vectors.tests.length; i++) {
should(`expand_message_xmd/${vectors.hash}/${vectors.DST.length}/${i}`, () => { const t = vectors.tests[i];
const p = expand_message_xmd( should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
stringToBytes(t.msg), const p = expand_message_xmd(
stringToBytes(vectors.DST), stringToBytes(t.msg),
t.len_in_bytes, stringToBytes(vectors.DST),
hash t.len_in_bytes,
); hash
deepStrictEqual(bytesToHex(p), t.uniform_bytes); );
}); deepStrictEqual(bytesToHex(p), t.uniform_bytes);
} });
}
});
} }
testExpandXMD(sha256, xmd_sha256_38); describe('expand_message_xmd', () => {
testExpandXMD(sha256, xmd_sha256_256); testExpandXMD(sha256, xmd_sha256_38);
testExpandXMD(sha512, xmd_sha512_38); testExpandXMD(sha256, xmd_sha256_256);
testExpandXMD(sha512, xmd_sha512_38);
});
function testExpandXOF(hash, vectors) { function testExpandXOF(hash, vectors) {
for (let i = 0; i < vectors.tests.length; i++) { describe(`${vectors.hash}/${vectors.DST.length}`, () => {
const t = vectors.tests[i]; for (let i = 0; i < vectors.tests.length; i++) {
should(`expand_message_xof/${vectors.hash}/${vectors.DST.length}/${i}`, () => { const t = vectors.tests[i];
const p = expand_message_xof( should(`${i}`, () => {
stringToBytes(t.msg), const p = expand_message_xof(
stringToBytes(vectors.DST), stringToBytes(t.msg),
+t.len_in_bytes, stringToBytes(vectors.DST),
vectors.k, +t.len_in_bytes,
hash vectors.k,
); hash
deepStrictEqual(bytesToHex(p), t.uniform_bytes); );
}); deepStrictEqual(bytesToHex(p), t.uniform_bytes);
} });
}
});
} }
testExpandXOF(shake128, xof_shake128_36); describe('expand_message_xof', () => {
testExpandXOF(shake128, xof_shake128_256); testExpandXOF(shake128, xof_shake128_36);
testExpandXOF(shake256, xof_shake256_36); testExpandXOF(shake128, xof_shake128_256);
testExpandXOF(shake256, xof_shake256_36);
});
function stringToFp(s) { function stringToFp(s) {
// bls-G2 support // bls-G2 support
@ -99,26 +107,30 @@ function stringToFp(s) {
} }
function testCurve(curve, ro, nu) { function testCurve(curve, ro, nu) {
for (let i = 0; i < ro.vectors.length; i++) { describe(`${ro.curve}/${ro.ciphersuite}`, () => {
const t = ro.vectors[i]; for (let i = 0; i < ro.vectors.length; i++) {
should(`${ro.curve}/${ro.ciphersuite}(${i})`, () => { const t = ro.vectors[i];
const p = curve.Point.hashToCurve(stringToBytes(t.msg), { should(`(${i})`, () => {
DST: ro.dst, const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
DST: ro.dst,
});
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
}); });
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px'); }
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(`(${i})`, () => {
should(`${nu.curve}/${nu.ciphersuite}(${i})`, () => { const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), { DST: nu.dst,
DST: nu.dst, });
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
}); });
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px'); }
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,53 +18,55 @@ const G_PROOF = new jubjub.ExtendedPoint(
const getXY = (p) => ({ x: p.x, y: p.y }); const getXY = (p) => ({ x: p.x, y: p.y });
should('toHex/fromHex', () => { describe('jubjub', () => {
// More than field should('toHex/fromHex', () => {
throws(() => // More than field
jubjub.Point.fromHex( throws(() =>
jubjub.Point.fromHex(
new Uint8Array([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
])
)
);
// Multiplicative generator (sqrt == null), not on curve.
throws(() =>
jubjub.Point.fromHex(
new Uint8Array([
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
)
);
const tmp = jubjub.Point.fromHex(
new Uint8Array([ new Uint8Array([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
])
)
);
// Multiplicative generator (sqrt == null), not on curve.
throws(() =>
jubjub.Point.fromHex(
new Uint8Array([
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
]) ])
) );
); deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
const tmp = jubjub.Point.fromHex( deepStrictEqual(tmp.y, 0n);
new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
);
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
deepStrictEqual(tmp.y, 0n);
const S = G_SPEND.toAffine().toRawBytes(); const S = G_SPEND.toAffine().toRawBytes();
const S2 = G_SPEND.double().toAffine().toRawBytes(); const S2 = G_SPEND.double().toAffine().toRawBytes();
const P = G_PROOF.toAffine().toRawBytes(); const P = G_PROOF.toAffine().toRawBytes();
const P2 = G_PROOF.double().toAffine().toRawBytes(); const P2 = G_PROOF.double().toAffine().toRawBytes();
const S_exp = jubjub.Point.fromHex(S); const S_exp = jubjub.Point.fromHex(S);
const S2_exp = jubjub.Point.fromHex(S2); const S2_exp = jubjub.Point.fromHex(S2);
const P_exp = jubjub.Point.fromHex(P); const P_exp = jubjub.Point.fromHex(P);
const P2_exp = jubjub.Point.fromHex(P2); const P2_exp = jubjub.Point.fromHex(P2);
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp)); deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp)); deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp)); deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp)); deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
}); });
should('Find generators', () => { should('Find generators', () => {
const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95])); const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95]));
const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95])); const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95]));
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.

@ -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,19 +344,21 @@ 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];
for (const hName in hashes) { describe('Wycheproof/WYCHEPROOF_ECDSA', () => {
const { hash, tests } = hashes[hName]; for (const hName in hashes) {
const CURVE = curve.create(hash); const { hash, tests } = hashes[hName];
should(`Wycheproof/WYCHEPROOF_ECDSA ${name}/${hName}`, () => { const CURVE = curve.create(hash);
for (let i = 0; i < tests.length; i++) { should(`${name}/${hName}`, () => {
const groups = tests[i].testGroups; for (let i = 0; i < tests.length; i++) {
for (let j = 0; j < groups.length; j++) { const groups = tests[i].testGroups;
const group = groups[j]; for (let j = 0; j < groups.length; j++) {
runWycheproof(name, CURVE, group, `${i}/${j}`); const group = groups[j];
runWycheproof(name, CURVE, group, `${i}/${j}`);
}
} }
} });
}); }
} });
} }
const hexToBigint = (hex) => BigInt(`0x${hex}`); const hexToBigint = (hex) => BigInt(`0x${hex}`);

File diff suppressed because it is too large Load Diff