./research / verifiable-audit-layer

Un strat de audit verificabil pentru IA.

Un strat de audit care evidențiază falsificarea pentru inferența modelelor și execuția agenților: serializare canonică, hashing cu separare de domenii, semnături DSSE, un DAG Merkle per execuție și un Merkle Mountain Range între execuții care dovedește că nicio execuție nu a fost ștearsă. Fiecare primitivă a fost aleasă dintr-un benchmark direct față de alternativele reale, cu datele brute versionate. Această notă este proiectarea și experimentele din spatele ei.

auditcriptografiebenchmarkurishipped

Model de amenințare

Presupunem un adversar care controlează stocarea și poate rescrie înregistrări ulterior, inclusiv operatorul care le-a produs. Scopul este ca orice editare a unei execuții sigilate să fie detectabilă, ca înregistrarea să rămână verificabilă pe o perioadă de retenție măsurată în ani, și ca verificarea să nu expună nicio intrare privată. Două proprietăți sunt ținute distincte tot timpul: integritatea unei singure execuții și completitudinea mulțimii de execuții. O chitanță per execuție o dă pe prima și nu spune nimic despre a doua, așa că ambele sunt proiectate separat.

Fiecare alegere de mai jos a fost făcută la fel. Nu afirmăm că o primitivă este cea mai bună; o comparăm cu alternativele credibile pe aceeași mașină și versionăm datele brute, astfel încât orice cifră de aici se reproduce cu o singură comandă. Timpii sunt cel mai rapid din mai multe execuții după eliminarea încălzirii, păstrând media și deviația standard pentru ca o măsurătoare instabilă să fie semnalată în loc de ascunsă. Numerele provin de la un singur host AMD64 pe CPython 3.14 și sunt semnificative ca rapoarte, nu ca valori absolute.

Experimente și rezultate

Fiecare reper a fost un test direct față de alternativele reale, nu o singură măsurătoare. Prezentarea generală este mai jos; rezultatele detaliate ale fiecăruia sunt în secțiunile care urmează.

Reper Testat față de Rezultat
Formă canonică JSON naiv, CBOR JCS, identic octet cu octet, ~3x cost
Hashing SHA-256, BLAKE3, keccak-256 keccak nativ 17x, aceiași octeți
Nucleu nativ backend-uri pip vs Rust sigilare 6x, rădăcină identică octet cu octet
Semnătură ECDSA, BLS, ML-DSA Ed25519, 64 B, deterministă
Post-cuantic ML-DSA 44 / 65 / 87 pe etape, verificare la egalitate, 38x dimensiune
Urmă per execuție 8 atacuri de falsificare, suprasarcină toate prinse, ~121 us / eveniment
Jurnal între execuții reconstrucție Merkle simplă MMR, adăugare plată, 483x

Serializare canonică

O capsulă trebuie să se hasheze la aceiași octeți în nucleul nostru Python, în clientul nostru TypeScript și în unealta proprie a unui auditor, altfel o înregistrare validă eșuează la verificare. Folosim RFC 8785 (JSON Canonicalization Scheme), care fixează formatarea numerelor, ordinea cheilor după unitatea de cod UTF-16 și respingerea valorilor nefinite. L-am testat față de JSON cu chei sortate naiv și față de CBOR.

Abordare Python vs Node Cost
JSON sortat naiv diverge la Unicode și numere referință
RFC 8785 JCS identic octet cu octet aproximativ 3x la serializare
CBOR binar, are nevoie de un decodor pentru a fi citit compact

JSON-ul naiv diverge între runtime-uri la ordonarea Unicode și la formatarea numerelor, deci rupe în tăcere verificarea între limbaje. JCS este identic octet cu octet în ambele, la aproximativ 3x timpul de serializare, plătit o dată per hash și neglijabil față de hashing și munca de rețea din jur. Ținem exact o implementare a lui. Când modulul nostru nativ a adus propriul serializator JSON, l-am scos, fiindcă un al doilea canonicalizator care se potrivește pe intrări obișnuite și diverge pe cazurile-limită pe care le poate alege un adversar este mai rău decât niciunul.

Hashing și nucleul nativ

Frunzele sunt hashate cu BLAKE3 iar nodurile Merkle cu keccak-256, ambele cu separare de domenii RFC 6962 (prefixe distincte pentru frunze și noduri interne) pentru a închide clasa de a doua preimagine în care un nod intern este prezentat ca frunză. keccak-256 nu este SHA3-256; cele două împart o permutare dar diferă printr-un octet de umplere, iar verificatorul on-chain vorbește keccak, deci o substituție tăcută ar face fiecare rădăcină să eșueze on-chain. Stratul de hashing calculează, prin urmare, algoritmul cerut sau aruncă o eroare, niciodată un sosie, și consemnează algoritmul în fiecare capsulă pentru ca un verificator să reproducă funcția exactă în loc să o deducă din ce este instalat local.

Primitivă, intrare de 64 B Debit Notă
SHA-256 (stdlib) 1,50M ops/s referință, mereu prezentă
BLAKE3 (pip) 1,44M ops/s la egalitate cu SHA-256
keccak-256 (pip) 99k ops/s de 15x mai lent, gâtuitura
keccak-256 (nucleu Rust) 1,69M ops/s de 17x mai rapid decât pip

keccak nu are o implementare în biblioteca standard, iar asta a făcut modulul nativ demn de construit. Diferența este cea mai mare la intrări de mărimea unui eveniment fiindcă costul pycryptodome acolo este alocarea de obiecte per apel și nu hashing-ul, exact mărimea pe care o urmă de audit o hashează cel mai des; cap la cap, nucleul nativ ridică sigilarea unei execuții de 1000 de evenimente de la 78 la 491 de sigilări pe secundă, aproximativ 6x. Crucial: activarea acceleratorului nu poate schimba rezultatul: sigilarea aceleiași execuții cu și fără crate produce o rădăcină identică bit cu bit, o suită de conformitate verifică ambele căi față de vectorii publicați de autorii algoritmului, iar CI construiește un singur wheel cu ABI stabil (abi3) și verifică acel unic artefact pe fiecare Python de la 3.10 la 3.14. Instalarea lui schimbă viteza și nimic altceva.

Semnături pentru o înregistrare de un deceniu

O semnătură pe o înregistrare de audit trebuie să reziste atâta timp cât înregistrarea poate fi contestată, iar obligațiile de logare a IA se apropie acum de un deceniu. Dacă schema se rupe cât timp o înregistrare este încă în fereastra ei de retenție, un adversar o poate falsifica și antedata, deci întreaga arhivă cedează dintr-odată. Am comparat Ed25519 față de ECDSA, BLS și familia post-cuantică NIST ML-DSA, pe debit și dimensiuni.

Schemă Verificare (ops/s) Dimensiune semnătură
ECDSA P-256 16 887 ~72 B
ML-DSA-44 (post-cuantic) 10 725 2420 B
Ed25519 10 375 64 B
BLS12-381 doar impl. de referință 96 B, agregă

Două rezultate au răsturnat ipoteze pe care le aduseserăm cu noi, și le păstrăm pe amândouă la dosar. Ed25519 nu este cel mai rapid de verificat: ECDSA P-256 a măsurat aproximativ 1,6x mai rapid, pe asamblare de bibliotecă optimizată. Ed25519 rămâne implicitul pe dimensiunea semnăturii și pe determinism (RFC 8032), care elimină defecțiunea de reutilizare a nonce-ului din ECDSA ce scurge o cheie privată cât timp semnăturile încă se verifică. Iar semnăturile post-cuantice nu sunt lente la verificare: ML-DSA-44 a întrecut la limită Ed25519, costul său fiind semnarea (aproximativ 13x mai lent) și dimensiunea (aproximativ 38x octeții). Fiindcă o urmă este semnată o dată și verificată ani de zile, partea scumpă este cea pe care o facem cel mai puțin. BLS a fost respinsă pe o cerință, nu pe un număr: agregarea comprimă multe semnături în 96 de octeți, dar un agregat se verifică totul-sau-nimic și are nevoie de fiecare cheie și mesaj, ceea ce este incompatibil cu dezvăluirea unei ramuri a unei decizii fără a arăta restul.

Reîncadrarea care contează: semnăturile nu sunt confidențiale, deci „recoltează acum, decriptează mai târziu" nu se aplică, iar amenințarea reală este falsificarea retroactivă în fereastra de retenție. Ancorarea unei rădăcini la un registru public dovedește când a existat o înregistrare independent de schema de semnătură, deci o cheie compromisă costă capacitatea de a dovedi cine a sigilat o înregistrare, dar nu când sau ce. Ancorarea este, prin urmare, pe calea critică iar ML-DSA este eșalonat în spatele ei, cu plicul DSSE ținut agil față de algoritm și algoritmul consemnat per capsulă.

Urma de audit per execuție

Fiecare execuție este o secvență de evenimente doar-adăugare și înlănțuită prin hash (apeluri LLM, apeluri de unelte, recuperări, generări de sub-agenți, aprobări umane, decizii de balustradă), cu o rădăcină Merkle peste toate și o semnătură DSSE peste rădăcină. Se stochează doar hash-uri ale intrărilor și ieșirilor, niciodată sarcinile utile, deci urma păstrează confidențialitatea implicit. Verificatorul nu returnează doar valid sau invalid; numește garanția care s-a rupt.

Operație (execuție de 102 evenimente) Cost Scalare
Înregistrarea unui eveniment ~121 us plat, sub 0,01% dintr-un pas
Sigilare, construirea rădăcinii 0,15 ms liniar în evenimente
Verificare, recalcularea tuturor frunzelor 9,2 ms liniar, aproximativ 60x sigilarea

Înregistrarea este sub o sutime de procent dintr-un pas tipic de agent, dominat de dus-întorsul către model, deci suprasarcina nu este un motiv de eșantionare iar urma rămâne completă în loc de statistică. Verificarea este mult mai grea decât sigilarea fiindcă recalculează fiecare frunză din conținut în timp ce sigilarea construiește arborele doar peste hash-uri deja prezente. Acea asimetrie este corectă pentru o urmă de audit, unde înregistrarea este constantă iar verificarea rară, și marchează reverificarea în masă drept locul unde nucleul nativ și paralelismul dau roade în continuare. Pentru a face garanțiile concrete, o demonstrație atacă o execuție sigilată în opt feluri, fiecare respins cu un motiv specific:

Atac Respins ca
Rescrierea unei ieșiri a modelului nepotrivire de hash de frunză
Ștergerea sau inserarea unui eveniment nepotrivire a numărului de evenimente
Reordonarea evenimentelor rupere de lanț
Trunchierea execuției nepotrivire de cap de lanț
Retrogradarea algoritmului de hash nepotrivire de hash de frunză
Prezentarea sub cheia greșită semnătura nu se verifică

Jurnalul de transparență între execuții

O capsulă per execuție validă nu poate dovedi că mulțimea execuțiilor este completă. Un operator care aruncă o execuție incomodă deține o colecție de capsule individual perfecte și o istorie cu o gaură. Închidem asta cu un singur jurnal doar-adăugare de rădăcini de capsulă care susține dovezi de includere (o execuție este în jurnal) și dovezi de consistență (jurnalul la o dimensiune anterioară este un prefix exact al jurnalului de acum). Consistența prinde cenzura: un jurnal reconstruit cu o execuție eliminată este intern bine format iar fiecare capsulă supraviețuitoare încă se verifică, dar eșuează la o verificare de consistență față de rădăcina publicată înainte, iar aceeași verificare respinge o inserare antedatată. Asta funcționează doar față de o rădăcină angajată înainte de manipulare, deci acea rădăcină trebuie să trăiască undeva unde operatorul nu poate revizui, precum o ancoră publică.

Folosim un Merkle Mountain Range în loc de un arbore Merkle simplu fiindcă o adăugare doar adaugă noduri și nu rescrie niciodată vreunul, ceea ce lasă jurnalul să trăiască pe stocare cu scriere-unică sau de obiecte și ține dovezile de includere vechi valide pe măsură ce jurnalul crește. Acea proprietate este aproape optimă, nu doar comodă: ePrint 2025/234 dovedește că orice angajament succint doar-adăugare trebuie să inducă un număr superliniar de actualizări de martori, iar o variantă apropiată de MMR atinge în esență limita.

Dimensiune jurnal Construcție MMR Reconstrucție simplă
500 1,3 ms 158 ms (120x)
1000 2,6 ms 618 ms (237x)
2000 5,2 ms 2491 ms (483x)

Adăugarea rămâne plată pe măsură ce jurnalul crește (aproximativ 390k adăugări pe secundă atât la 100 cât și la 10 000 de intrări) în timp ce reconstruirea unui arbore simplu scade de la 8176 la 84 de rădăcini pe secundă pe același interval, semnătura pătratic-versus-liniaritmică. Stocarea se stabilizează la 2,00 hash-uri stocate per intrare iar dovezile de includere cresc doar de la 7 la 17 pași între 100 și 100 000 de intrări. Unde pierdem: dovezile noastre de consistență sunt O(log^2 n), purtând o cale per vârf al jurnalului vechi în loc de O(log n) realizabil, câțiva kilobytes la 100 000 de intrări și un compromis deliberat pentru o construcție pe care un recenzent o poate verifica citind.

Limite pe care nu le depășim

Hashing-ul dă evidența falsificării, nu imunitatea la falsificare. Un adversar care reconstruiește o execuție și îi recalculează rădăcina produce o înregistrare auto-consistentă, fiindcă o rădăcină Merkle dovedește că evenimentele se potrivesc cu rădăcina, nu că rădăcina este cea publicată. Semnătura prinde asta, iar ancora publică dovedește momentul. Jurnalul de transparență se sprijină la scară mare pe aceeași ipoteză: detectează o execuție ștearsă doar față de o rădăcină angajată înainte de ștergere. Declarăm aceste limite în produs și în demonstrațiile lui, iar un test afirmă că demonstrația de falsificare continuă să arate atacul pe care nu îl oprim, pentru ca acest caz onest să nu poată dispărea în tăcere.

Reproductibilitate

Fiecare cifră de aici provine dintr-un benchmark versionat care rulează din nou cu o singură comandă, alături de teste, de suitele de conformitate între limbaje și între implementări, și de matricea CI care exersează Python 3.10 până la 3.14 cu și fără nucleul nativ. Metodologia, JSON-ul brut și notele de decizie (inclusiv dimensiunile în care fiecare alegere pierde) sunt în depozit, fiindcă un strat care face IA auditabilă trebuie să fie el însuși auditabil.