'use strict'; // AERE Proof of everything (roadmap #56-66): un constructor generic de plicuri AERE Proof Protocol (AIP-23) pentru felurile de // dovada standard - date, executie, identitate, conformitate, runtime, locatie, dispozitiv, plata, proprietate, timp, bloc, si din // 2026-09-28 autorizare, decontare, provenienta. Toate produc // ACELASI plic (statement + statementHash SHA-256 al textului canonic) pe care il verifica ACELASI verificator (tools/aere-proof- // protocol/verify.mjs), fara niciun cod nou de verificare. Notarizarea + finalitatea post-cuantica vin din AIP-23. Numai Node 24. // // Un fel de dovada = o schema: campurile obligatorii (unele DIGESTURI, ca nimic sensibil sa nu intre brut) + createdAt. Constructorul // hashuieste continutul brut daca i se da, sau accepta un digest gata (0x+64). Un camp obligatoriu lipsa -> eroare (nu un plic gol). import crypto from 'node:crypto'; const sha256 = (b) => '0x' + crypto.createHash('sha256').update(b).digest('hex'); export const isDigest = (s) => typeof s === 'string' && /^0x[0-9a-fA-F]{64}$/.test(s); // hashuieste un continut in digest; daca e deja digest, il pastreaza // OCTETII unui Buffer/Uint8Array se hashuiesc ca atare. Platit 2026-09-27: un Buffer trecea prin JSON.stringify si se hashuia // forma {"type":"Buffer","data":[...]}, deci digestul unui artefact dat ca fisier (sidecar record --kind deployment) nu se mai // putea reface din fisier cu sha256sum. Sirurile si obiectele raman exact ca inainte (vectorii AIP-23 publicati nu se schimba). const dg = (v) => (v == null ? null : (isDigest(v) ? v.toLowerCase() : (Buffer.isBuffer(v) || v instanceof Uint8Array) ? sha256(Buffer.from(v)) : sha256(Buffer.from(typeof v === 'string' ? v : JSON.stringify(v), 'utf8')))); // schema fiecarui fel: campuri de tip 'digest' (se hashuiesc), 'plain' (raman ca atare), 'bool', 'list'. Toate obligatorii daca in `req`. export const SCHEME = { 'data': { digest: { sha256: 'content' }, plain: { name: 'subject' }, req: ['sha256', 'name'] }, 'execution': { digest: { inputHash: 'input', outputHash: 'output', programSha256: 'programContent' }, plain: { program: 'program', exitCode: 'exitCode' }, req: ['program', 'inputHash', 'outputHash'] }, 'identity': { digest: { publicKeyHash: 'publicKey', subjectHash: 'subject' }, plain: { method: 'method', subjectId: 'subjectId' }, req: ['subjectId', 'publicKeyHash'] }, 'compliance': { digest: { evidenceHash: 'evidence' }, plain: { subject: 'subject', policy: 'policy', result: 'result' }, req: ['subject', 'policy', 'result'] }, 'runtime': { digest: { artifactSha256: 'artifactContent', attestedSha256: 'attested' }, plain: { host: 'host', unit: 'unit', matches: 'matches' }, req: ['host', 'artifactSha256'] }, 'location': { digest: { evidenceHash: 'evidence' }, plain: { subject: 'subject', region: 'region', method: 'method' }, req: ['subject', 'region'] }, 'device': { digest: { attestationHash: 'attestation', publicKeyHash: 'publicKey' }, plain: { deviceId: 'deviceId', posture: 'posture' }, req: ['deviceId', 'attestationHash'] }, 'payment': { digest: { txHash: 'tx' }, plain: { from: 'from', to: 'to', amount: 'amount', asset: 'asset' }, req: ['from', 'to', 'amount'] }, 'ownership': { digest: { assetHash: 'asset' }, plain: { owner: 'owner', assetId: 'assetId' }, req: ['owner', 'assetId'] }, 'time': { digest: { subjectHash: 'subject' }, plain: { source: 'source', at: 'at' }, req: ['subjectHash', 'at'] }, // A4: plicul de consens al unui bloc (blockHash, stateRoot ancorate de certificatul de ancora PQ). Dovada de executie per bloc // (stateTransitionProof) e optionala si vine cand exista proverul; fara ea, plicul poarta increderea CONSENSULUI, nu a executiei. 'block': { digest: { stateTransitionProof: 'stateTransitionProof' }, plain: { blockHash: 'blockHash', stateRoot: 'stateRoot', anchorHeight: 'anchorHeight', certificateDigest: 'certificateDigest' }, req: ['blockHash', 'stateRoot', 'certificateDigest'] }, // 2026-09-28 (roadmap master, punctul 11): cele trei feluri numite in plan care lipseau. `hash` = campuri care SUNT deja un hash de // 32 de octeti (0x+64 hex; o valoare de alta forma e refuzata, nu hashuita), `list` = o lista nevida de digesturi (fiecare element // brut se hashuieste, un digest ramane). Felurile vechi nu au nici una, deci plicurile si vectorii lor raman octet cu octet aceiasi. // authorization: cine (grantor) a dat cui (grantee) dreptul de a face ce (scope), sub ce politica, pana cand 'authorization': { digest: { policyHash: 'policy' }, plain: { grantor: 'grantor', grantee: 'grantee', scope: 'scope', expiresAt: 'expiresAt' }, req: ['grantor', 'grantee', 'scope'] }, // settlement: o decontare incheiata pe un lant: tranzactia, blocul care o contine, suma; finalitatea blocului o da AIP-23 (notarizare // + ancora) sau clientul usor PQ (interop_core::pq), nu plicul 'settlement': { digest: { instructionHash: 'instruction' }, hash: ['txHash', 'blockHash'], plain: { chainId: 'chainId', from: 'from', to: 'to', amount: 'amount', asset: 'asset' }, req: ['chainId', 'txHash', 'blockHash', 'amount', 'asset'] }, // provenance: din ce a iesit un artefact (parents), prin ce proces, cine l-a produs 'provenance': { digest: { subjectHash: 'subject', processHash: 'process' }, list: { parents: 'parentsContent' }, plain: { actor: 'actor' }, req: ['subjectHash', 'parents'] }, }; export const KINDS = Object.keys(SCHEME); /** * Construieste un plic AIP-23 pentru un fel de dovada. * @param {string} kind unul din KINDS * @param {object} p campurile (brute sau digesturi); vezi SCHEME * @returns {object} plicul { v, kind: 'aere-proof-of--attestation', statement, statementHash } */ export function buildProof(kind, p = {}) { const s = SCHEME[kind]; if (!s) throw new Error('proof-kinds: unknown kind "' + kind + '" (' + KINDS.join(', ') + ')'); if (!p.createdAt) throw new Error('proof-kinds: createdAt is required (RFC 3339)'); const statement = { v: 1, kind: 'aere-proof-of-' + kind }; // campuri digest: se hashuiesc din sursa (numele sursei din schema) sau se iau gata din campul de digest for (const [camp, sursa] of Object.entries(s.digest || {})) { statement[camp] = p[camp] !== undefined ? dg(p[camp]) : dg(p[sursa]); } for (const camp of s.hash || []) { if (p[camp] === undefined || p[camp] === null) continue; if (!isDigest(p[camp])) throw new Error('proof-kinds: ' + kind + ' needs "' + camp + '" to be a 32-byte hash (0x + 64 hex)'); statement[camp] = p[camp].toLowerCase(); } for (const [camp, sursa] of Object.entries(s.list || {})) { const v = p[camp] !== undefined ? p[camp] : p[sursa]; if (v === undefined || v === null) continue; if (!Array.isArray(v) || v.length === 0) throw new Error('proof-kinds: ' + kind + ' needs "' + camp + '" to be a non-empty list'); statement[camp] = v.map(dg); } for (const [camp] of Object.entries(s.plain || {})) { if (p[camp] !== undefined) statement[camp] = p[camp]; } statement.createdAt = p.createdAt; for (const r of s.req) { if (statement[r] === undefined || statement[r] === null) throw new Error('proof-kinds: ' + kind + ' needs the field "' + r + '"'); } const text = JSON.stringify(statement); // canonic AIP-23 return { v: 1, kind: 'aere-proof-of-' + kind + '-attestation', statement, statementHash: sha256(Buffer.from(text, 'utf8')) }; }