diff --git a/test/basic.test.js b/test/basic.test.js index b908719..25c5d63 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -68,8 +68,8 @@ for (const c in FIELDS) { 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); + deepStrictEqual(Fp.eql(a, b), true); + deepStrictEqual(Fp.eql(b, a), true); }) ); }); @@ -78,8 +78,8 @@ for (const c in FIELDS) { 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); + deepStrictEqual(Fp.eql(a, b), num1 === num2); + deepStrictEqual(Fp.eql(b, a), num1 === num2); }) ); }); @@ -124,8 +124,8 @@ for (const c in FIELDS) { 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(Fp.ZERO, a), Fp.neg(a)); + deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.neg(b))); deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n)))); }) ); @@ -134,13 +134,13 @@ for (const c in FIELDS) { 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))); + deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a)); + deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n))); }) ); }); should('negate(0)', () => { - deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO); + deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO); }); should('multiply/commutativity', () => { @@ -190,7 +190,7 @@ for (const c in FIELDS) { fc.assert( fc.property(FC_BIGINT, (num) => { const a = create(num); - deepStrictEqual(Fp.square(a), Fp.mul(a, a)); + deepStrictEqual(Fp.sqr(a), Fp.mul(a, a)); }) ); }); @@ -207,18 +207,18 @@ for (const c in FIELDS) { }); should('square(0)', () => { - deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO); + deepStrictEqual(Fp.sqr(Fp.ZERO), Fp.ZERO); deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO); }); should('square(1)', () => { - deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE); + deepStrictEqual(Fp.sqr(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); + const minus1 = Fp.neg(Fp.ONE); + deepStrictEqual(Fp.sqr(minus1), Fp.ONE); deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE); }); @@ -237,8 +237,8 @@ for (const c in FIELDS) { 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'); + deepStrictEqual(Fp.eql(Fp.sqr(root), a), true, 'sqrt(a)^2 == a'); + deepStrictEqual(Fp.eql(Fp.sqr(Fp.neg(root)), a), true, '(-sqrt(a))^2 == a'); }) ); }); @@ -247,7 +247,7 @@ for (const c in FIELDS) { 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)), + Fp.eql(sqrt1, Fp.ONE) || Fp.eql(sqrt1, Fp.neg(Fp.ONE)), true, 'sqrt(1) = 1 or -1' ); @@ -258,7 +258,7 @@ for (const c in FIELDS) { fc.assert( fc.property(FC_BIGINT, (num) => { const a = create(num); - if (Fp.equals(a, Fp.ZERO)) return; // No division by zero + if (Fp.eql(a, Fp.ZERO)) return; // No division by zero deepStrictEqual(Fp.div(a, Fp.ONE), a); deepStrictEqual(Fp.div(a, a), Fp.ONE); }) @@ -287,7 +287,7 @@ for (const c in FIELDS) { 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))); + deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b))); }) ); }); @@ -347,7 +347,7 @@ for (const name in CURVES) { describe(title, () => { describe('basic group laws', () => { // Here we check basic group laws, to verify that points works as group - should('(zero)', () => { + should('zero', () => { 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].subtract(G[0]), G[0], '0*G - 0*G = 0'); @@ -358,34 +358,34 @@ for (const name in CURVES) { equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`); } }); - should('(one)', () => { + 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)', () => { + 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)', () => { + should('add 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)', () => { + should('double', () => { equal(G[3].double(), G[6], '(3*G).double() = 6*G'); }); - should('(multiply)', () => { + should('multiply', () => { equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G'); }); - should('(same point addition)', () => { + should('add same-point', () => { equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G'); }); - should('(same point (negative) addition)', () => { + should('add same-point negative', () => { 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)', () => { + should('mul by 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'); @@ -393,7 +393,7 @@ for (const name in CURVES) { 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)', () => { + should('inversion', () => { const a = 1234n; const b = 5678n; const c = a * b; @@ -401,7 +401,7 @@ for (const name in CURVES) { 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)', () => + should('multiply, rand', () => fc.assert( fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { const c = mod.mod(a + b, CURVE_ORDER); @@ -415,7 +415,7 @@ for (const name in CURVES) { { numRuns: NUM_RUNS } ) ); - should('(multiply2, rand)', () => + should('multiply2, rand', () => fc.assert( fc.property(FC_BIGINT, FC_BIGINT, (a, b) => { const c = mod.mod(a * b, CURVE_ORDER); @@ -524,20 +524,23 @@ for (const name in CURVES) { } describe(name, () => { // Generic complex things (getPublicKey/sign/verify/getSharedSecret) - should('getPublicKey type check', () => { + should('.getPublicKey() type check', () => { throws(() => C.getPublicKey(0), '0'); throws(() => C.getPublicKey(0n), '0n'); throws(() => C.getPublicKey(false), 'false'); + throws(() => C.getPublicKey(123), '123'); 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('1'), "'1'"); throws(() => C.getPublicKey('key'), "'key'"); + throws(() => C.getPublicKey({})); 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))); + throws(() => C.getPublicKey(Array(32).fill(1))); }); should('.verify() should verify random signatures', () => fc.assert( @@ -559,13 +562,27 @@ for (const name in CURVES) { throws(() => C.sign('')); }); - should('.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); + describe('verify()', () => { + should('true for proper signatures', () => { + const msg = '01'.repeat(32); + const priv = C.utils.randomPrivateKey(); + const sig = C.sign(msg, priv); + const pub = C.getPublicKey(priv); + deepStrictEqual(C.verify(sig, msg, pub), true); + }); + should('false for wrong messages', () => { + const msg = '01'.repeat(32); + const priv = C.utils.randomPrivateKey(); + const sig = C.sign(msg, priv); + const pub = C.getPublicKey(priv); + deepStrictEqual(C.verify(sig, '11'.repeat(32), pub), false); + }); + should('false for wrong keys', () => { + const msg = '01'.repeat(32); + const priv = C.utils.randomPrivateKey(); + const sig = C.sign(msg, priv); + deepStrictEqual(C.verify(sig, msg, C.getPublicKey(C.utils.randomPrivateKey())), 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? @@ -618,10 +635,10 @@ should('secp224k1 sqrt bug', () => { 23621584063597419797792593680131996961517196803742576047493035507225n ); deepStrictEqual( - Fp.negate(sqrtMinus1), + Fp.neg(sqrtMinus1), 3338362603553219996874421406887633712040719456283732096017030791656n ); - deepStrictEqual(Fp.square(sqrtMinus1), Fp.create(-1n)); + deepStrictEqual(Fp.sqr(sqrtMinus1), Fp.create(-1n)); }); // ESM is broken. diff --git a/test/bls12-381.test.js b/test/bls12-381.test.js index 42a53b5..86f220c 100644 --- a/test/bls12-381.test.js +++ b/test/bls12-381.test.js @@ -71,8 +71,8 @@ describe('bls12-381 Fp2', () => { fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => { const a = Fp2.fromBigTuple([num1[0], num1[1]]); const b = Fp2.fromBigTuple([num2[0], num2[1]]); - deepStrictEqual(Fp2.equals(a, b), num1[0] === num2[0] && num1[1] === num2[1]); - deepStrictEqual(Fp2.equals(b, a), num1[0] === num2[0] && num1[1] === num2[1]); + deepStrictEqual(Fp2.eql(a, b), num1[0] === num2[0] && num1[1] === num2[1]); + deepStrictEqual(Fp2.eql(b, a), num1[0] === num2[0] && num1[1] === num2[1]); }) ); }); @@ -103,7 +103,7 @@ describe('bls12-381 Fp2', () => { ]); a = Fp2.frobeniusMap(a, 0); deepStrictEqual( - Fp2.equals( + Fp2.eql( a, Fp2.fromBigTuple([ 0x00f8d295b2ded9dcccc649c4b9532bf3b966ce3bc2108b138b1a52e0a90f59ed11e59ea221a3b6d22d0078036923ffc7n, @@ -114,7 +114,7 @@ describe('bls12-381 Fp2', () => { ); a = Fp2.frobeniusMap(a, 1); deepStrictEqual( - Fp2.equals( + Fp2.eql( a, Fp2.fromBigTuple([ 0x00f8d295b2ded9dcccc649c4b9532bf3b966ce3bc2108b138b1a52e0a90f59ed11e59ea221a3b6d22d0078036923ffc7n, @@ -125,7 +125,7 @@ describe('bls12-381 Fp2', () => { ); a = Fp2.frobeniusMap(a, 1); deepStrictEqual( - Fp2.equals( + Fp2.eql( a, Fp2.fromBigTuple([ 0x00f8d295b2ded9dcccc649c4b9532bf3b966ce3bc2108b138b1a52e0a90f59ed11e59ea221a3b6d22d0078036923ffc7n, @@ -136,7 +136,7 @@ describe('bls12-381 Fp2', () => { ); a = Fp2.frobeniusMap(a, 2); deepStrictEqual( - Fp2.equals( + Fp2.eql( a, Fp2.fromBigTuple([ 0x00f8d295b2ded9dcccc649c4b9532bf3b966ce3bc2108b138b1a52e0a90f59ed11e59ea221a3b6d22d0078036923ffc7n, diff --git a/test/ed25519.test.js b/test/ed25519.test.js index 79607c1..ff3ca07 100644 --- a/test/ed25519.test.js +++ b/test/ed25519.test.js @@ -79,6 +79,7 @@ describe('ed25519', () => { ); }); const privKey = to32Bytes('a665a45920422f9d417e4867ef'); + const wrongPriv = to32Bytes('a675a45920422f9d417e4867ef'); const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a'); const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c'); describe('basic methods', () => { @@ -87,16 +88,6 @@ describe('ed25519', () => { const signature = ed.sign(msg, privKey); deepStrictEqual(ed.verify(signature, msg, publicKey), true); }); - should('not verify signature with wrong public key', () => { - const publicKey = ed.getPublicKey(12n); - const signature = ed.sign(msg, privKey); - deepStrictEqual(ed.verify(signature, msg, publicKey), false); - }); - should('not verify signature with wrong hash', () => { - const publicKey = ed.getPublicKey(privKey); - const signature = ed.sign(msg, privKey); - deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); - }); }); describe('sync methods', () => { should('sign and verify', () => { @@ -105,7 +96,7 @@ describe('ed25519', () => { deepStrictEqual(ed.verify(signature, msg, publicKey), true); }); should('not verify signature with wrong public key', () => { - const publicKey = ed.getPublicKey(12n); + const publicKey = ed.getPublicKey(wrongPriv); const signature = ed.sign(msg, privKey); deepStrictEqual(ed.verify(signature, msg, publicKey), false); }); @@ -649,7 +640,7 @@ describe('ed25519', () => { should('X25519 base point', () => { const { y } = ed25519.ExtendedPoint.BASE; const { Fp } = ed25519.CURVE; - const u = Fp.create((y + 1n) * Fp.invert(1n - y)); + const u = Fp.create((y + 1n) * Fp.inv(1n - y)); deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu); }); diff --git a/test/ed448.test.js b/test/ed448.test.js index ca48358..12bd87b 100644 --- a/test/ed448.test.js +++ b/test/ed448.test.js @@ -316,14 +316,16 @@ describe('ed448', () => { }, ]; - for (let i = 0; i < VECTORS_RFC8032.length; i++) { - const v = VECTORS_RFC8032[i]; - should(`RFC8032/${i}`, () => { - deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey); - deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature); - deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true); - }); - } + describe('RFC8032', () => { + for (let i = 0; i < VECTORS_RFC8032.length; i++) { + const v = VECTORS_RFC8032[i]; + should(`${i}`, () => { + deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey); + deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature); + deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true); + }); + } + }); should('not accept >57byte private keys', () => { const invalidPriv = @@ -383,7 +385,7 @@ describe('ed448', () => { deepStrictEqual(ed.verify(signature, msg, publicKey), true); }); should('not verify signature with wrong public key', () => { - const publicKey = ed.getPublicKey(12n); + const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey()); const signature = ed.sign(msg, privKey); deepStrictEqual(ed.verify(signature, msg, publicKey), false); }); @@ -400,7 +402,7 @@ describe('ed448', () => { deepStrictEqual(ed.verify(signature, msg, publicKey), true); }); should('not verify signature with wrong public key', () => { - const publicKey = ed.getPublicKey(12n); + const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey()); const signature = ed.sign(msg, privKey); deepStrictEqual(ed.verify(signature, msg, publicKey), false); }); @@ -437,14 +439,14 @@ describe('ed448', () => { } }); - { + describe('wycheproof', () => { for (let g = 0; g < ed448vectors.testGroups.length; g++) { const group = ed448vectors.testGroups[g]; const key = group.key; - should(`Wycheproof/ED448(${g}, public)`, () => { + should(`ED448(${g}, public)`, () => { deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk); }); - should(`Wycheproof/ED448`, () => { + should(`ED448`, () => { for (let i = 0; i < group.tests.length; i++) { const v = group.tests[i]; const index = `${g}/${i} ${v.comment}`; @@ -463,7 +465,7 @@ describe('ed448', () => { } }); } - } + }); // ECDH const rfc7748Mul = [ @@ -482,12 +484,14 @@ describe('ed448', () => { '884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d', }, ]; - for (let i = 0; i < rfc7748Mul.length; i++) { - const v = rfc7748Mul[i]; - should(`RFC7748: scalarMult (${i})`, () => { - deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU); - }); - } + describe('RFC7748', () => { + for (let i = 0; i < rfc7748Mul.length; i++) { + const v = rfc7748Mul[i]; + should(`scalarMult (${i})`, () => { + deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU); + }); + } + }); const rfc7748Iter = [ { @@ -528,9 +532,9 @@ describe('ed448', () => { deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared); }); - { + describe('wycheproof', () => { const group = x448vectors.testGroups[0]; - should(`Wycheproof/X448`, () => { + should(`X448`, () => { for (let i = 0; i < group.tests.length; i++) { const v = group.tests[i]; const index = `(${i}, ${v.result}) ${v.comment}`; @@ -556,7 +560,7 @@ describe('ed448', () => { } else throw new Error('unknown test result'); } }); - } + }); // should('X448: should convert base point to montgomery using fromPoint', () => { // deepStrictEqual( diff --git a/test/nist.test.js b/test/nist.test.js index 4decafb..4c3a1b0 100644 --- a/test/nist.test.js +++ b/test/nist.test.js @@ -11,164 +11,11 @@ import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: ' import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' }; import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' }; -const hex = bytesToHex; - -// prettier-ignore -const NIST = { - secp192r1, P192, - secp224r1, P224, - secp256r1, P256, - secp384r1, P384, - secp521r1, P521, - secp256k1, -}; - -should('Curve Fields', () => { - const vectors = { - secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn, - secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n, - secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn, - secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn, - secp384r1: - 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn, - secp521r1: - 0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, - }; - for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]); -}); - -should('wychenproof ECDSA vectors', () => { - for (const group of ecdsa.testGroups) { - // Tested in secp256k1.test.js - if (group.key.curve === 'secp256k1') continue; - let CURVE = NIST[group.key.curve]; - if (!CURVE) continue; - if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') { - if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256); - } - const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed); - deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`)); - deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`)); - for (const test of group.tests) { - if (['Hash weaker than DL-group'].includes(test.comment)) { - continue; - } - const m = CURVE.CURVE.hash(hexToBytes(test.msg)); - if (test.result === 'valid' || test.result === 'acceptable') { - try { - CURVE.Signature.fromDER(test.sig); - } catch (e) { - // Some test has invalid signature which we don't accept - if (e.message.includes('Invalid signature: incorrect length')) continue; - throw e; - } - const verified = CURVE.verify(test.sig, m, pubKey.toHex()); - deepStrictEqual(verified, true, 'valid'); - } else if (test.result === 'invalid') { - let failed = false; - try { - failed = !CURVE.verify(test.sig, m, pubKey.toHex()); - } catch (error) { - failed = true; - } - deepStrictEqual(failed, true, 'invalid'); - } else throw new Error('unknown test result'); - } - } -}); - -should('wychenproof ECDH vectors', () => { - for (const group of ecdh.testGroups) { - // // Tested in secp256k1.test.js - // if (group.key.curve === 'secp256k1') continue; - // We don't have SHA-224 - const CURVE = NIST[group.curve]; - if (!CURVE) continue; - for (const test of group.tests) { - if (test.result === 'valid' || test.result === 'acceptable') { - try { - const pub = CURVE.ProjectivePoint.fromHex(test.public); - } catch (e) { - if (e.message.includes('Point.fromHex: received invalid point.')) continue; - throw e; - } - const shared = CURVE.getSharedSecret(test.private, test.public); - deepStrictEqual(shared, test.shared, 'valid'); - } else if (test.result === 'invalid') { - let failed = false; - try { - CURVE.getSharedSecret(test.private, test.public); - } catch (error) { - failed = true; - } - deepStrictEqual(failed, true, 'invalid'); - } else throw new Error('unknown test result'); - } - } -}); - import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' }; import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' }; import { default as ecdh_secp256k1_test } from './wycheproof/ecdh_secp256k1_test.json' assert { type: 'json' }; import { default as ecdh_secp384r1_test } from './wycheproof/ecdh_secp384r1_test.json' assert { type: 'json' }; import { default as ecdh_secp521r1_test } from './wycheproof/ecdh_secp521r1_test.json' assert { type: 'json' }; - -// More per curve tests -const WYCHEPROOF_ECDH = { - P224: { - curve: P224, - tests: [ecdh_secp224r1_test], - }, - P256: { - curve: P256, - tests: [ecdh_secp256r1_test], - }, - secp256k1: { - curve: secp256k1, - tests: [ecdh_secp256k1_test], - }, - P384: { - curve: P384, - tests: [ecdh_secp384r1_test], - }, - P521: { - curve: P521, - tests: [ecdh_secp521r1_test], - }, -}; - -for (const name in WYCHEPROOF_ECDH) { - const { curve, tests } = WYCHEPROOF_ECDH[name]; - for (let i = 0; i < tests.length; i++) { - const test = tests[i]; - for (let j = 0; j < test.testGroups.length; j++) { - const group = test.testGroups[j]; - should(`Wycheproof/ECDH ${name} (${i}/${j})`, () => { - for (const test of group.tests) { - if (test.result === 'valid' || test.result === 'acceptable') { - try { - const pub = curve.ProjectivePoint.fromHex(test.public); - } catch (e) { - if (e.message.includes('Point.fromHex: received invalid point.')) continue; - throw e; - } - const shared = curve.getSharedSecret(test.private, test.public); - deepStrictEqual(hex(shared), test.shared, 'valid'); - } else if (test.result === 'invalid') { - let failed = false; - try { - curve.getSharedSecret(test.private, test.public); - } catch (error) { - failed = true; - } - deepStrictEqual(failed, true, 'invalid'); - } else throw new Error('unknown test result'); - } - }); - } - } -} - // Tests with custom hashes import { default as secp224r1_sha224_test } from './wycheproof/ecdsa_secp224r1_sha224_test.json' assert { type: 'json' }; import { default as secp224r1_sha256_test } from './wycheproof/ecdsa_secp224r1_sha256_test.json' assert { type: 'json' }; @@ -199,6 +46,121 @@ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3'; import { sha512, sha384 } from '@noble/hashes/sha512'; import { sha224, sha256 } from '@noble/hashes/sha256'; +const hex = bytesToHex; + +// prettier-ignore +const NIST = { + secp192r1, P192, + secp224r1, P224, + secp256r1, P256, + secp384r1, P384, + secp521r1, P521, + secp256k1, +}; + +describe('NIST curves', () => {}); +should('fields', () => { + const vectors = { + secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn, + secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n, + secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn, + secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn, + secp384r1: + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn, + secp521r1: + 0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, + }; + for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]); +}); + +describe('wycheproof ECDH', () => { + for (const group of ecdh.testGroups) { + // // Tested in secp256k1.test.js + // if (group.key.curve === 'secp256k1') continue; + // We don't have SHA-224 + const CURVE = NIST[group.curve]; + if (!CURVE) continue; + should(group.curve, () => { + for (const test of group.tests) { + if (test.result === 'valid' || test.result === 'acceptable') { + try { + const pub = CURVE.ProjectivePoint.fromHex(test.public); + } catch (e) { + if (e.message.includes('Point.fromHex: received invalid point.')) continue; + throw e; + } + const shared = CURVE.getSharedSecret(test.private, test.public); + deepStrictEqual(shared, test.shared, 'valid'); + } else if (test.result === 'invalid') { + let failed = false; + try { + CURVE.getSharedSecret(test.private, test.public); + } catch (error) { + failed = true; + } + deepStrictEqual(failed, true, 'invalid'); + } else throw new Error('unknown test result'); + } + }); + } + + // More per curve tests + const WYCHEPROOF_ECDH = { + P224: { + curve: P224, + tests: [ecdh_secp224r1_test], + }, + P256: { + curve: P256, + tests: [ecdh_secp256r1_test], + }, + secp256k1: { + curve: secp256k1, + tests: [ecdh_secp256k1_test], + }, + P384: { + curve: P384, + tests: [ecdh_secp384r1_test], + }, + P521: { + curve: P521, + tests: [ecdh_secp521r1_test], + }, + }; + + for (const name in WYCHEPROOF_ECDH) { + const { curve, tests } = WYCHEPROOF_ECDH[name]; + for (let i = 0; i < tests.length; i++) { + const test = tests[i]; + for (let j = 0; j < test.testGroups.length; j++) { + const group = test.testGroups[j]; + should(`additional ${name} (${i}/${j})`, () => { + for (const test of group.tests) { + if (test.result === 'valid' || test.result === 'acceptable') { + try { + const pub = curve.ProjectivePoint.fromHex(test.public); + } catch (e) { + if (e.message.includes('Point.fromHex: received invalid point.')) continue; + throw e; + } + const shared = curve.getSharedSecret(test.private, test.public); + deepStrictEqual(hex(shared), test.shared, 'valid'); + } else if (test.result === 'invalid') { + let failed = false; + try { + curve.getSharedSecret(test.private, test.public); + } catch (error) { + failed = true; + } + deepStrictEqual(failed, true, 'invalid'); + } else throw new Error('unknown test result'); + } + }); + } + } + } +}); + const WYCHEPROOF_ECDSA = { P224: { curve: P224, @@ -344,42 +306,85 @@ function runWycheproof(name, CURVE, group, index) { } } -for (const name in WYCHEPROOF_ECDSA) { - const { curve, hashes } = WYCHEPROOF_ECDSA[name]; - describe('Wycheproof/WYCHEPROOF_ECDSA', () => { - for (const hName in hashes) { - const { hash, tests } = hashes[hName]; - const CURVE = curve.create(hash); - should(`${name}/${hName}`, () => { - for (let i = 0; i < tests.length; i++) { - const groups = tests[i].testGroups; - for (let j = 0; j < groups.length; j++) { - const group = groups[j]; - runWycheproof(name, CURVE, group, `${i}/${j}`); - } +describe('wycheproof ECDSA', () => { + should('generic', () => { + for (const group of ecdsa.testGroups) { + // Tested in secp256k1.test.js + if (group.key.curve === 'secp256k1') continue; + let CURVE = NIST[group.key.curve]; + if (!CURVE) continue; + if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') { + if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256); + } + const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed); + deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`)); + deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`)); + for (const test of group.tests) { + if (['Hash weaker than DL-group'].includes(test.comment)) { + continue; } - }); + const m = CURVE.CURVE.hash(hexToBytes(test.msg)); + if (test.result === 'valid' || test.result === 'acceptable') { + try { + CURVE.Signature.fromDER(test.sig); + } catch (e) { + // Some test has invalid signature which we don't accept + if (e.message.includes('Invalid signature: incorrect length')) continue; + throw e; + } + const verified = CURVE.verify(test.sig, m, pubKey.toHex()); + deepStrictEqual(verified, true, 'valid'); + } else if (test.result === 'invalid') { + let failed = false; + try { + failed = !CURVE.verify(test.sig, m, pubKey.toHex()); + } catch (error) { + failed = true; + } + deepStrictEqual(failed, true, 'invalid'); + } else throw new Error('unknown test result'); + } } }); -} + for (const name in WYCHEPROOF_ECDSA) { + const { curve, hashes } = WYCHEPROOF_ECDSA[name]; + describe(name, () => { + for (const hName in hashes) { + const { hash, tests } = hashes[hName]; + const CURVE = curve.create(hash); + should(`${name}/${hName}`, () => { + for (let i = 0; i < tests.length; i++) { + const groups = tests[i].testGroups; + for (let j = 0; j < groups.length; j++) { + const group = groups[j]; + runWycheproof(name, CURVE, group, `${i}/${j}`); + } + } + }); + } + }); + } +}); const hexToBigint = (hex) => BigInt(`0x${hex}`); -should('RFC6979', () => { +describe('RFC6979', () => { for (const v of rfc6979) { - const curve = NIST[v.curve]; - deepStrictEqual(curve.CURVE.n, hexToBigint(v.q)); - const pubKey = curve.getPublicKey(v.private); - const pubPoint = curve.ProjectivePoint.fromHex(pubKey); - deepStrictEqual(pubPoint.x, hexToBigint(v.Ux)); - deepStrictEqual(pubPoint.y, hexToBigint(v.Uy)); - for (const c of v.cases) { - const h = curve.CURVE.hash(c.message); - const sigObj = curve.sign(h, v.private); - deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R'); - deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S'); - deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)'); - deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)'); - } + should(v.curve, () => { + const curve = NIST[v.curve]; + deepStrictEqual(curve.CURVE.n, hexToBigint(v.q)); + const pubKey = curve.getPublicKey(v.private); + const pubPoint = curve.ProjectivePoint.fromHex(pubKey); + deepStrictEqual(pubPoint.x, hexToBigint(v.Ux)); + deepStrictEqual(pubPoint.y, hexToBigint(v.Uy)); + for (const c of v.cases) { + const h = curve.CURVE.hash(c.message); + const sigObj = curve.sign(h, v.private); + deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R'); + deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S'); + deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)'); + deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)'); + } + }); } }); diff --git a/test/secp256k1.test.js b/test/secp256k1.test.js index 84f8fe5..55737a1 100644 --- a/test/secp256k1.test.js +++ b/test/secp256k1.test.js @@ -279,8 +279,8 @@ describe('secp256k1', () => { }); should(' not verify signature with wrong public key', () => { const MSG = '01'.repeat(32); - const PRIV_KEY = 0x2n; - const WRONG_PRIV_KEY = 0x22n; + const PRIV_KEY = '01'.repeat(32); + const WRONG_PRIV_KEY = '02'.repeat(32); const signature = secp.sign(MSG, PRIV_KEY); const publicKey = Point.fromPrivateKey(WRONG_PRIV_KEY).toHex(); deepStrictEqual(publicKey.length, 66); @@ -469,7 +469,7 @@ describe('secp256k1', () => { }, privateNegate: (privateKey) => { - return numberToBytesBE(Fn.negate(normal(privateKey)), 32); + return numberToBytesBE(Fn.neg(normal(privateKey)), 32); }, pointAddScalar: (p, tweak, isCompressed) => { diff --git a/test/stark/poseidon.test.js b/test/stark/poseidon.test.js index b65121e..e73a492 100644 --- a/test/stark/poseidon.test.js +++ b/test/stark/poseidon.test.js @@ -1,5 +1,5 @@ import { deepStrictEqual, throws } from 'assert'; -import { should } from 'micro-should'; +import { describe, should } from 'micro-should'; import * as starknet from '../../lib/esm/stark.js'; import * as fs from 'fs'; @@ -64,10 +64,12 @@ function poseidonTest(name, parsed) { }); } -poseidonTest('poseidon3', parsed.poseidon3); -poseidonTest('poseidon4', parsed.poseidon4); -poseidonTest('poseidon5', parsed.poseidon5); -poseidonTest('poseidon9', parsed.poseidon9); +describe('poseidon txt vectors', () => { + poseidonTest('poseidon3', parsed.poseidon3); + poseidonTest('poseidon4', parsed.poseidon4); + poseidonTest('poseidon5', parsed.poseidon5); + poseidonTest('poseidon9', parsed.poseidon9); +}); should('Poseidon examples', () => { const p3 = mapPoseidon(parsed.poseidon3);