// Probele @aere/pq-sign: fiecare afirmatie cu perechea ei negativa. Offline; cu AERE_CHEIE in mediu ruleaza si // verificarea INDEPENDENTA pe lant a jumatatii post-cuantice (precompila 2800), in ambele sensuri. import assert from 'node:assert'; import { generateKeyPair, sign, verify, serialize, parse, envelopeHash, toSign, messageHash, fromHex, toHex, PQ_SCHEMES, CLASSICAL_SCHEMES, verifyOnChain } from '../index.mjs'; let treceri = 0; const esecuri = []; async function test(nume, fn) { try { await fn(); treceri++; console.log(' OK ' + nume); } catch (e) { esecuri.push(nume); console.log(' ESEC ' + nume + ' — ' + (e.message || e)); } } const flip = (hex, i = -1) => { const b = fromHex(hex); const k = i < 0 ? b.length - 1 : i; b[k] ^= 0x01; return toHex(b); }; const MSG = 'Invoice 2026-0917: 12,400 EUR payable to Aere Foundation'; for (const alg of ['secp256k1+ml-dsa-65', 'ed25519+ml-dsa-65', 'secp256k1+ml-dsa-44', 'ed25519+ml-dsa-87']) { const keys = generateKeyPair({ alg }); const env = sign(MSG, keys); await test(`${alg}: sign then verify is valid, both halves`, () => { const r = verify(MSG, env); assert.strictEqual(r.valid, true, JSON.stringify(r)); assert.strictEqual(r.classical, true); assert.strictEqual(r.pq, true); }); await test(`${alg}: sizes are the scheme's (key ${PQ_SCHEMES[alg.split('+')[1]].publicKeyBytes} B, signature ${PQ_SCHEMES[alg.split('+')[1]].signatureBytes} B)`, () => { const P = PQ_SCHEMES[alg.split('+')[1]], C = CLASSICAL_SCHEMES[alg.split('+')[0]]; assert.strictEqual(fromHex(env.pqPublicKey).length, P.publicKeyBytes); assert.strictEqual(fromHex(env.pqSignature).length, P.signatureBytes); assert.strictEqual(fromHex(env.classicalSignature).length, C.signatureBytes); assert.strictEqual(fromHex(env.classicalPublicKey).length, C.publicKeyBytes); }); await test(`${alg}: one flipped bit in the post-quantum half invalidates the hybrid (classical alone is not enough)`, () => { const r = verify(MSG, { ...env, pqSignature: flip(env.pqSignature) }); assert.strictEqual(r.valid, false); assert.strictEqual(r.classical, true); assert.strictEqual(r.pq, false); assert.match(r.reason, /post-quantum half/); }); await test(`${alg}: one flipped bit in the classical half invalidates the hybrid (post-quantum alone is not enough)`, () => { const r = verify(MSG, { ...env, classicalSignature: flip(env.classicalSignature, 10) }); assert.strictEqual(r.valid, false); assert.strictEqual(r.pq, true); assert.strictEqual(r.classical, false); assert.match(r.reason, /classical half/); }); await test(`${alg}: another message is refused by both halves`, () => { const r = verify(MSG + '.', env); assert.strictEqual(r.valid, false); assert.strictEqual(r.messageHashMatches, false); assert.strictEqual(r.classical, false); assert.strictEqual(r.pq, false); }); await test(`${alg}: another post-quantum public key is refused`, () => { const alta = generateKeyPair({ alg }); const r = verify(MSG, { ...env, pqPublicKey: alta.pq.publicKey }); assert.strictEqual(r.valid, false); assert.strictEqual(r.pq, false); }); await test(`${alg}: the envelope survives JSON and its digest is stable`, () => { const back = parse(serialize(env)); assert.deepStrictEqual(back, env); assert.strictEqual(verify(MSG, back).valid, true); assert.strictEqual(envelopeHash(back), envelopeHash(env)); assert.match(envelopeHash(env), /^0x[0-9a-f]{64}$/); }); } await test('the signed digest binds domain, algorithm and message: same message under another alg has another toSign', () => { const mh = messageHash(MSG); assert.notStrictEqual(toHex(toSign('secp256k1+ml-dsa-65', mh)), toHex(toSign('ed25519+ml-dsa-65', mh))); assert.strictEqual(toHex(toSign('secp256k1+ml-dsa-65', mh)), toHex(toSign('secp256k1+ml-dsa-65', mh))); }); await test('a deterministic seed gives the same post-quantum key (vectors), classical keys stay fresh', () => { const seed = '0x' + '11'.repeat(32); const a = generateKeyPair({ seed }), b = generateKeyPair({ seed }); assert.strictEqual(a.pq.publicKey, b.pq.publicKey); assert.notStrictEqual(a.classical.publicKey, b.classical.publicKey); }); await test('a mismatched classical key pair is refused at signing time', () => { const k = generateKeyPair(); const alt = generateKeyPair(); assert.throws(() => sign(MSG, { ...k, classical: { ...k.classical, publicKey: alt.classical.publicKey } }), /does not match/); }); await test('an unknown algorithm pair is refused', () => { assert.throws(() => generateKeyPair({ alg: 'rsa+ml-dsa-65' }), /unknown alg/); const r = verify(MSG, { v: 1, kind: 'aere-hybrid-signature', hash: 'sha256', alg: 'secp256k1+sphincs' }); assert.strictEqual(r.valid, false); assert.match(r.reason, /malformed|unknown/); }); await test('a foreign envelope is refused without throwing', () => { const r = verify(MSG, { v: 2, kind: 'jws' }); assert.strictEqual(r.valid, false); assert.strictEqual(r.reason, 'unknown envelope'); }); await test('binary messages sign and verify like text', () => { const k = generateKeyPair(); const m = new Uint8Array([0, 255, 1, 2, 3, 254]); assert.strictEqual(verify(m, sign(m, k)).valid, true); assert.strictEqual(verify(new Uint8Array([0, 255, 1, 2, 3, 253]), sign(m, k)).valid, false); }); // optional: the chain precompile judges the ML-DSA half, independently of this code, in both directions. // The key comes from AERE_CHEIE or is read by this process from the file named in AERE_CHEIE_FISIER (never echoed). const CHEIE = process.env.AERE_CHEIE || (process.env.AERE_CHEIE_FISIER ? (await import('node:fs')).readFileSync(process.env.AERE_CHEIE_FISIER, 'utf8').trim() : ''); if (CHEIE) { const { AereCloud } = await import('../../aere-cloud-sdk/index.mjs'); const cloud = new AereCloud({ apiKey: CHEIE }); const keys = generateKeyPair({ alg: 'secp256k1+ml-dsa-44' }); const env = sign(MSG, keys); await test('on chain (precompile 0x...0ae3): the post-quantum half of a fresh hybrid signature verifies', async () => { const r = await verifyOnChain(env, cloud); assert.strictEqual(r.pqValidOnChain, true, JSON.stringify(r)); assert.ok(r.block > 0); }); await test('on chain: one flipped bit in the post-quantum half is refused by the precompile', async () => { const r = await verifyOnChain({ ...env, pqSignature: flip(env.pqSignature) }, cloud); assert.strictEqual(r.pqValidOnChain, false, JSON.stringify(r)); }); } else console.log(' (sarit) verificarea pe lant a jumatatii ML-DSA: pune AERE_CHEIE in mediu'); console.log(`\n${treceri} treceri, ${esecuri.length} esecuri`); if (esecuri.length) process.exitCode = 1;