// Proof of Software, B-18 (2026-09-29): o semnatura valida spune ca NISTE chei au semnat, nu CARE. Pe 1.4.0 un strain isi semna cu // cheile lui declaratia despre artefactul lui (acelasi nume si versiune) si verificarea spunea VALID fara sa numeasca semnatarul. Acum // numele verificarii poarta amprenta cheilor, `--signer` cere cheile asteptate, iar fara el (si fara o acreditare judecata) se spune // nejudecat. Fiecare afirmatie cu perechea ei negativa. Offline. // node test/semnatar.test.mjs iesire 0 = toate cum trebuia import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import { attest, verify, publicKeysOf, issueCredential } from '../pos.mjs'; import * as pq from '../../../sdk-pq-sign/index.mjs'; const AICI = path.dirname(fileURLToPath(import.meta.url)); 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 cere = (c, m) => { if (!c) throw new Error(m); }; const check = (r, re) => r.checks.find((c) => re.test(c.name)); const D = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-pos-semnatar-')); const BUN = path.join(D, 'app.tgz'); fs.writeFileSync(BUN, 'artefactul constructorului'); fs.mkdirSync(path.join(D, 'strain')); const RAU = path.join(D, 'strain', 'app.tgz'); fs.writeFileSync(RAU, 'artefactul unui strain'); const dev = pq.generateKeyPair(), strain = pq.generateKeyPair(), org = pq.generateKeyPair(); const aBun = await attest({ artifacts: [BUN], name: 'app', version: '1.0.0', keys: dev, cwd: D }); const aRau = await attest({ artifacts: [RAU], name: 'app', version: '1.0.0', keys: strain, cwd: D }); await test('1. numele verificarii semnaturii poarta amprenta cheilor care au semnat (doua chei -> doua amprente)', async () => { const r1 = await verify(aBun, [BUN]), r2 = await verify(aRau, [RAU]); const n1 = check(r1, /^hybrid signature/).name, n2 = check(r2, /^hybrid signature/).name; cere(/by keys 0x[0-9a-f]{16}/.test(n1) && n1 !== n2, `${n1} | ${n2}`); }); await test('2. fara --signer si fara acreditare judecata: cine a semnat e raportat NEJUDECAT, nu tacut', async () => { const r = await verify(aRau, [RAU]); cere(r.valid && check(r, /^signer$/) && check(r, /^signer$/).pass === null && /anyone can sign/.test(check(r, /^signer$/).detail), JSON.stringify(check(r, /^signer$/))); }); await test('3. ATAC: atestarea strainului, verificata cu --signer = cheile constructorului -> INVALIDA', async () => { const r = await verify(aRau, [RAU], { signer: publicKeysOf(dev) }); cere(!r.valid && check(r, /signed by the keys you gave/).pass === false, JSON.stringify(check(r, /signed by the keys you gave/))); }); await test('4. CONTROL: atestarea constructorului, cu --signer = cheile lui (fisierul public) -> VALIDA', async () => { const r = await verify(aBun, [BUN], { signer: publicKeysOf(dev) }); cere(r.valid && check(r, /signed by the keys you gave/).pass === true, JSON.stringify(r.checks.filter((c) => c.pass === false))); }); await test('5. o atestare nesemnata, cu --signer -> INVALIDA (nu "absenta")', async () => { const a = await attest({ artifacts: [BUN], name: 'app', version: '1.0.0', cwd: D }); const r = await verify(a, [BUN], { signer: publicKeysOf(dev) }); cere(!r.valid && /not signed/.test(check(r, /signed by the keys you gave/).detail), JSON.stringify(check(r, /signed by the keys you gave/))); }); await test('6. cu o acreditare judecata (--trust-issuer), semnatarul e judecat de acreditare si randul "signer" nu mai apare', async () => { const cred = issueCredential({ issuerKeys: org, issuerName: 'Org', subjectKeys: publicKeysOf(dev), subjectName: 'Dev', validFrom: '2026-01-01T00:00:00Z', validUntil: '2027-01-01T00:00:00Z' }); const a = await attest({ artifacts: [BUN], name: 'app', version: '1.0.0', keys: dev, credential: cred, cwd: D, now: new Date('2026-06-01T00:00:00Z') }); const r = await verify(a, [BUN], { trustIssuer: publicKeysOf(org) }); cere(!check(r, /^signer$/) && check(r, /names the keys that signed/).pass === true, JSON.stringify(r.checks.map((c) => c.name))); }); await test('7. linia de comanda: verify --signer (0 pentru constructor, 1 pentru strain), si "not judged" numarat pe randul de verdict', async () => { const pos = path.join(AICI, '..', 'pos.mjs'); const f = (n) => path.join(D, n); fs.writeFileSync(f('dev.json'), JSON.stringify(dev)); execFileSync(process.execPath, [pos, 'pubkey', '--keys', f('dev.json'), '--out', f('dev.pub.json')], { stdio: 'pipe' }); fs.writeFileSync(f('bun.json'), JSON.stringify(aBun)); fs.writeFileSync(f('rau.json'), JSON.stringify(aRau)); const run = (args) => { try { return { cod: 0, out: execFileSync(process.execPath, [pos, ...args], { stdio: 'pipe' }).toString() }; } catch (e) { return { cod: e.status, out: String(e.stdout || '') }; } }; const b = run(['verify', f('bun.json'), '--signer', f('dev.pub.json'), BUN]), r = run(['verify', f('rau.json'), '--signer', f('dev.pub.json'), RAU]); const fara = run(['verify', f('rau.json'), RAU]); cere(b.cod === 0 && r.cod === 1 && fara.cod === 0 && /not judged/.test(fara.out), `${b.cod} ${r.cod} ${fara.cod} ${fara.out.split('\n').slice(-2).join(' ')}`); }); fs.rmSync(D, { recursive: true, force: true }); console.log(`\n${treceri} treceri, ${esecuri.length} esecuri`); process.exitCode = esecuri.length ? 1 : 0;