Strojový překlad, nekontrolovaný rodilým mluvčím. Referenční je anglická verze.

Validace a důkazyUpravit na GitHubuZdroj: docs/EVIDENCE-PACKS.md

Balíčky důkazů: podepsaný záznam události důvěry v GNSS

Balíček je technický záznam, nikoli právní stanovisko. Uvádí, co engine Kshana vypočítal z uvedeného záznamu přijímače při uvedené konfiguraci, a umožňuje komukoli ověřit, že se v něm nic dodatečně nezměnilo. Neříká, co událost způsobilo, kdo za ni nesl odpovědnost, zda byla splněna nějaká povinnost, ani že záznam ukazuje to, co přijímač skutečně přijal. Manifest i shrnutí to uvádějí.

Balíček důkazů slouží pro zprávu o incidentu, pojišťovnu nebo dotaz regulátora: „co váš systém řekl o signálu GNSS mezi těmito dvěma časy a můžeme tomuto záznamu věřit?“

Vytvoření balíčku#

# once: a signing key. The private key stays with you; never commit it.
kshana evidence keygen --out signer.key        # writes signer.key (0600) and signer.key.pub

kshana receiver-trust evidence session.toml \
    --from 100 --to 260 \                       # seconds since the first epoch, or ISO-8601 UTC
    --key signer.key --out pack/ \
    --title "Berth 4, 14 June"                  # optional

U balíčku vytvořeného z laboratorní relace --lab-attestation lab.toml zaznamenává do podepsaného manifestu prohlášení operátora o uspořádání (vedené nebo stíněné, útlum nebo kryt, co vysílalo jakékoli RF); viz HARDWARE-SAFETY.md. Zaznamenává se tak, jak je uvedeno, a nikdy se neověřuje.

--from a --to jsou sekundy od první epochy záznamu, nebo čas UTC podle ISO-8601, když záznam uvádí vlastní čas začátku. --out nesmí již obsahovat soubory. --created-utc přijímá skutečný čas UTC, například 2026-01-02T03:04:05Z (cokoli jiného se odmítne), nebo none, což čas vytvoření vynechá a učiní balíček byte po bytu reprodukovatelným. Čas vytvoření je takový, jaký uvede podepisující; nic jej nekontroluje proti hodinám, k čemuž slouží volitelný token časového razítka.

Soubor s klíčem obsahuje 32bajtový seed Ed25519 jako 64 hexadecimálních číslic; dodejte vlastní (například klíč uchovávaný ve vašem systému správy klíčů) nebo nechte keygen vytvořit jeden. kshana varuje, pokud je soubor čitelný pro jiné uživatele. .gitignore vylučuje *.evidence-key; pojmenujte svůj klíč tak, pokud leží v checkoutu.

Co je v balíčku#

Struktura balíčku důkazů. Záznam přijímače (NMEA, u-blox UBX, RINEX 3 nebo záznam z Androidu) a časové okno vstupují do kshana receiver-trust evidence, který zapíše sedm souborů: log-slice.bin, config.json, epochs.json, summary.html, manifest.json, manifest.sig a volitelný timestamp.tsr. Manifest zaznamenává SHA-256 celého záznamu a každého souboru a řetězec hashů přes soubory; podpis pokrývá manifest. kshana evidence verify kontroluje podpis, hash každého souboru, řetězec a že není přítomen žádný nevyjmenovaný soubor, a končí kódem 0, když je vše ověřeno, 1, když něco selhalo, a 3, když podepisující nebyl připnut. Balíček je technický záznam, nikoli právní stanovisko.Struktura balíčku důkazů. Záznam přijímače (NMEA, u-blox UBX, RINEX 3 nebo záznam z Androidu) a časové okno vstupují do kshana receiver-trust evidence, který zapíše sedm souborů: log-slice.bin, config.json, epochs.json, summary.html, manifest.json, manifest.sig a volitelný timestamp.tsr. Manifest zaznamenává SHA-256 celého záznamu a každého souboru a řetězec hashů přes soubory; podpis pokrývá manifest. kshana evidence verify kontroluje podpis, hash každého souboru, řetězec a že není přítomen žádný nevyjmenovaný soubor, a končí kódem 0, když je vše ověřeno, 1, když něco selhalo, a 3, když podepisující nebyl připnut. Balíček je technický záznam, nikoli právní stanovisko.
Soubor Obsah
log-slice.bin surové bajty záznamu přijímače pro dané okno
config.json scénář tak, jak byl spuštěn (každý práh monitoru, událost a pravidlo porovnání), monitory, které běžely, kalibrační základ a hash scénáře; u plavidla také poradní prohlášení, které nese každý výstup pro plavidlo (software pouze poradního charakteru, nikoli typově schválené navigační zařízení); od 0,36 též geometry_model (tabulka [geometry] skutečně použitá, váha monitoru geometrie, každý původ a pravidla tříd). Názvy adresářů a vložená data jsou odstraněny.
epochs.json každá epocha v okně se statistikami enginu, poplachy (důvody) a stavem důvěry; od 0,36 také geometry družic (dobrá, zhoršená s důvody nebo neznámá, kde záznam neuvádí geometrii družic) a class u každého závěru (geometry, environment, inconsistency)
summary.html samostatné shrnutí bez skriptů: identifikace, hashe, výsledky, důvody, dráha geometrie (epochy podle stavu geometrie) oddělená od tříd závěrů (0,36), každý práh, tabulka po epochách a meze záznamu
manifest.json verze enginu, okno, čas vytvoření, formát a název záznamu, SHA-256 úplného záznamu, rozsah a SHA-256 výřezu, veřejný klíč a otisk podepisujícího, velikost a SHA-256 každého souboru a řetězec hashů přes soubory
manifest.sig podpis Ed25519 přes přesné bajty manifest.json
timestamp.tsr volitelný token časového razítka podle RFC 3161

Výřez záznamu. Čtečka uvádí zdrojový bajtový rozsah každé epochy, takže výřez je nejmenší bajtový rozsah, který obsahuje každou epochu v okně (byte-range): NMEA, u-blox UBX, pozorování RINEX 3 a záznamy z Androidu. Hashuje se jako surové bajty, nikdy jako dekódovaný text. U živého přenosu nebo RINEX 2 rozsah neexistuje; pak se přibalí celý záznam a manifest uvádí whole-log (okno omezuje, které epochy se uvádějí, nikoli které bajty se zahrnou). Shrnutí uvádí, který případ platí. Výřez je záznam bajtů okna, nikoli samostatný záznam: například výřez RINEX nemá hlavičku souboru.

Řetězec hashů. Manifest uvádí soubory v pevném pořadí. link₀ = SHA-256("kshana- evidence-chain/1"); pro každý soubor linkᵢ = SHA-256(linkᵢ₋₁ ‖ SHA-256(file) ‖ len(name) ‖ name) (délka jako 4 bajty, big-endian). Poslední článek je chain_head. Přeřazení, odstranění nebo nahrazení souboru změní řetězec; podpis pokrývá řetězec.

Ověření balíčku#

kshana evidence verify pack/ --pubkey signer.key.pub [--log session.nmea] \
    [--require-timestamp] [--allow-unpinned] [--json]

Návratový kód 0 znamená ověřeno; 1 znamená, že něco selhalo; 2 znamená chybu použití; 3 znamená, že balíček je neporušený, ale jeho podepisující nebyl připnut (viz níže). Každá kontrola hlásí splněno, selhalo nebo přeskočeno a každé selhání je pojmenováno:

Kód selhání Význam
manifest-missing, manifest-malformed, unsupported-format manifest nelze přečíst
signature-missing, signature-malformed, signature-invalid žádný podpis, není to 64bajtový podpis v hexadecimálních malých písmenech, nebo se neověří přes manifest.json
public-key-malformed, public-key-mismatch klíč není platný nebo se liší od toho, který jste dodali
file-missing, file-size-mismatch, file-hash-mismatch uvedený soubor chybí nebo se liší od manifestu; soubor je pojmenován
unlisted-file soubor je v balíčku, ale není v manifestu
chain-mismatch, chain-head-mismatch článek neodpovídá tomu, co dávají jeho vstupy; soubor je pojmenován
artifact-list-malformed, slice-record-inconsistent, epoch-count-mismatch manifest si odporuje nebo odporuje epochs.json
full-log-hash-mismatch, slice-not-from-full-log s --log: záznam, který máte, není ten zaznamenaný, nebo výřez není tímto rozsahem z něj
timestamp-missing, timestamp-malformed, timestamp-imprint-mismatch viz níže

Změna jediného bajtu kteréhokoli souboru způsobí selhání ověření s odpovídajícím důvodem: změněný artefakt se hlásí jako neshoda hashe tohoto souboru a nic jiného; změněný manifest způsobí selhání podpisu. Přepočet hashů a řetězce po úpravě souboru bez podpisového klíče nepomůže; selže pak pouze podpis.

Připněte klíč. Bez --pubkey balíček dokazuje jen to, že je neporušený vůči klíči, který sám uvádí a který může vygenerovat kdokoli. Ověření tedy končí kódem 3 a hlásí „INTACT, BUT THE SIGNER IS NOT PINNED“ místo „VERIFIED“; --allow-unpinned přijme vlastní klíč balíčku vědomě a vypíše „VERIFIED against the key the pack names itself (signer NOT pinned; pass --pubkey)“. Veřejný klíč podepisujícího získejte od podepisujícího cestou, které věříte (nikoli z balíčku), a předejte jej pomocí --pubkey: 64 hexadecimálních číslic nebo soubor .pub. Omylem předaný soukromý klíč se rozpozná a odmítne. Otisk (prvních 128 bitů SHA-256 klíče, 32 hexadecimálních číslic) se vypisuje k porovnání pohledem; spolehlivou cestou je připnutí celého klíče.

Formát balíčku a postup ověření (normativní)#

Tento oddíl je specifikace, podle níž se píše nezávislý ověřovač. Slova psaná velkými písmeny jsou požadavky. Balíček je adresář (nebo libovolné zobrazení názvu souboru na bajty) souborů pojmenovaných přesně jako níže; ověřovač pracuje s bajty každého souboru tak, jak je uložen, a nikdy nic znovu neserializuje.

Soubory. Soubory artefaktů jsou v tomto pevném pořadí log-slice.bin, config.json, epochs.json, summary.html. Balíček má také manifest.json a manifest.sig a MŮŽE mít timestamp.tsr. Jakýkoli jiný název je nevyjmenovaný soubor.

Hex. Každý hexadecimální řetězec je pouze malými písmeny 0-9a-f, bez bílých znaků; velká písmena se nepřijímají. Hodnoty SHA-256 mají 64 číslic, veřejný klíč má 64 číslic, podpis má 128 číslic.

manifest.sig. Hexadecimální zápis malými písmeny o 128 číslicích 64bajtového podpisu Ed25519 (RFC 8032, čisté schéma Ed25519) přes přesné bajty manifest.json, následovaný právě jedním \n a ničím jiným. Podpis MUSÍ být kontrolován striktně: podpis, jehož hodnota S není menší než řád grupy L, je neplatný.

manifest.json. Objekt JSON v UTF-8. Jeho členové (ověřovač MUSÍ najít každého s uvedeným typem JSON; další členové se ignorují):

člen typ význam
format řetězec MUSÍ se rovnat kshana-evidence/1
title, engine_version, disclaimer řetězec popisný
created_utc řetězec nebo null čas vytvoření, popisný
window objekt {from_s, to_s} čísla, sekundy
epochs_in_window nezáporné celé číslo počet položek pole JSON v epochs.json
log objekt format řetězec, file_name řetězec, full_sha256 hex, full_bytes nezáporné celé číslo, start_label řetězec nebo null a slice
log.slice objekt kind (přesně byte-range nebo whole-log; jakákoli jiná hodnota činí manifest chybně utvořeným), start a end nezáporná celá čísla (bajtové posuny, konec je vyloučen), sha256 hex
signer objekt algorithm řetězec (ed25519), public_key hex (32 bajtů), fingerprint řetězec
artifacts pole objektů jeden pro každý soubor artefaktu, v pevném pořadí: name řetězec, role řetězec, bytes nezáporné celé číslo, sha256 hex, link hex
chain_head hex poslední článek
lab_attestation objekt, volitelný přítomen pouze v balíčku vytvořeném z laboratorní relace (--lab-attestation): prohlášení operátora operator, organisation, date, setup (conducted nebo shielded), attenuation_db nebo enclosure, emitter, statement a volitelně signature, zaznamenané tak, jak jsou uvedeny. Je pokryto podpisem manifestu a nikdy se neověřuje; ověřovač je hlásí jako „attested by the named operator; not verified by Kshana“ (potvrzeno jmenovaným operátorem; Kshana neověřuje). Jinak chybí a balíček bez něj se nemění

signer.fingerprint je prvních 32 hexadecimálních číslic SHA-256 přes 32 bajtů veřejného klíče.

Řetězec hashů. link0 je SHA-256 z 23 bajtů ASCII kshana-evidence-chain/1. Pro každý artefakt v pořadí link = SHA-256( previous_link (32 bytes) ‖ SHA-256(file) (32 bytes) ‖ len(name) as 4 bytes big-endian ‖ name as UTF-8 ), kde SHA-256(file) se bere ze zaznamenaného sha256 manifestu pro daný artefakt (takže řetězec je vlastností manifestu a kontroluje se proti němu, nikoli proti souborům).

Postup. Ověřovač provádí tyto kroky v pořadí a shromažďuje každé selhání; výsledkem je množina selhání, každé je code a tam, kde je to uvedeno, soubor name. NESMÍ se zastavit u prvního selhání, kromě uvedených případů. pin je veřejný klíč, kterému má ověřovač důvěřovat, pokud nějaký byl zadán.

  1. Žádný manifest.json: selhání manifest-missing; zastavit.
  2. Pokud manifest.json není objekt JSON se všemi výše uvedenými členy v uvedených typech (včetně položek artifacts se všemi členy a log.slice): selhání manifest-malformed. Jinak, pokud format není výše uvedenou hodnotou: unsupported-format.
  3. Fáze podpisu. Tato fáze se provádí bez ohledu na to, zda byl manifest čitelný. Pokud byl manifest čitelný, uvedeným klíčem je signer.public_key; pokud to není 64 hexadecimálních číslic malými písmeny, selhání public-key-malformed (žádný uvedený klíč); jinak, pokud signer.fingerprint není jeho otiskem, selhání public-key-malformed (klíč zůstává použitelný). Pokud existuje pin i uvedený klíč a liší se: selhání public-key-mismatch. Kontrolním klíčem je pin, pokud byl zadán, jinak uvedený klíč, pokud existuje; pokud není ani jeden, kontrolní klíč neexistuje (to zahrnuje každý případ, kdy manifest nebyl čitelný a nebyl zadán žádný pin). Pokud neexistuje manifest.sig: signature-missing. Jinak, pokud soubor není přesně 128 hexadecimálních číslic malými písmeny plus jedno \n: signature-malformed. Jinak, pokud existuje kontrolní klíč, který není platným kódováním veřejného klíče Ed25519: public-key-malformed. Jinak, pokud existuje kontrolní klíč a podpis se striktně neověří přes bajty manifest.json (ať jsou jakékoli): signature-invalid. (Bez kontrolního klíče se podpis nekontroluje a pro to se nevyvolává žádné selhání.)
  4. Pokud manifest nebyl čitelný (krok 2 dal manifest-malformed): zde se zastavit.
  5. Artefakty. Pokud názvy artifacts nejsou přesně čtyři výše uvedené v pořadí: artifact-list-malformed. Poté pro každou položku manifestu v pořadí: pokud balíček nemá soubor tohoto názvu, file-missing (název); jinak, pokud se jeho délka liší od bytes, file-size-mismatch (název); a nezávisle, pokud se SHA-256 souboru liší od sha256, file-hash-mismatch (název).
  6. Nevyjmenované soubory: pro každý soubor v balíčku jiný než manifest.json, manifest.sig, timestamp.tsr a názvy v artifacts manifestu: unlisted-file (název).
  7. Řetězec. Projděte artifacts manifestu v pořadí od link0. Pokud sha256 položky není 64 hexadecimálních číslic malými písmeny: manifest-malformed a řetězec se opustí (žádná kontrola hlavy). Jinak vypočtěte článek; pokud se liší od link položky: chain-mismatch (název). Po průchodu, pokud řetězec nebyl opuštěn a poslední článek se liší od chain_head: chain-head-mismatch.
  8. Výřez a epochy. Vyvolejte slice-record-inconsistent (nejvýše jednou za důvod a ověřovač uvádí kód jednou, bez ohledu na to, kolik důvodů platí), pokud platí cokoli z: start > end; end > full_bytes; sha256 položky log-slice.bin se liší od slice.sha256; bytes této položky se liší od end - start; nebo kind je whole-log a platí cokoli z start != 0, end != full_bytes, slice.sha256 != full_sha256. Pokud epochs.json existuje a je zpracováno jako pole JSON, jehož délka se liší od epochs_in_window: epoch-count-mismatch.
  9. Úplný záznam. Pouze pokud ověřovač dostal původní bajty záznamu: pokud se jejich SHA-256 liší od log.full_sha256, full-log-hash-mismatch; jinak, pokud rozsah start..end není uvnitř záznamu nebo se bajty záznamu tam liší od log-slice.bin, slice-not-from-full-log.
  10. Časové razítko. Bez timestamp.tsr: pokud byl ověřovač instruován, aby je vyžadoval, timestamp-missing; jinak nic. S ním: pokud jej nelze přečíst jako token nebo odpověď podle RFC 3161 (viz níže), timestamp-malformed; jinak, pokud jeho otisk zprávy není hashem, v algoritmu vlastním tomuto otisku (SHA-256, SHA-384 nebo SHA-512), bajtů manifest.json: timestamp-imprint-mismatch.

Balíček je ověřen právě tehdy, když je množina selhání prázdná. Zda byl podepisující připnut, se hlásí zvlášť a množinu selhání to nemění.

Token časového razítka. Token je DER ContentInfo, jehož typ obsahu je CMS SignedData (1.2.840.113549.1.7.2) a jehož zapouzdřený typ obsahu je id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4); odpověď je DER TimeStampResp (PKIStatusInfo následovaná tokenem), jejíž stav je 0 nebo 1. Algoritmus otisku zprávy v TSTInfo musí být SHA-256, SHA-384 nebo SHA-512 s otiskem délky odpovídající tomuto algoritmu a jeho genTime skutečný kalendářní čas UTC. BER s neurčenou délkou se nepřijímá. Podpis autority a řetězec certifikátů se kontrolují, pouze když jsou zadány důvěryhodné kotvy (--tsa-ca, níže).

Štítek důkazů (0,40)#

Balíček vytvořený verzí 0,40 nebo novější nese uvnitř config.json položku evidence (úroveň, VALIDATED, MODELLED nebo PARTNER, což je nejslabší stav mezi jeho řádky, a id a požadavky řádků verifikační matice, na nichž obsah balíčku stojí: řádek integrity balíčku, řádky receiver-trust a řádky jammertest-scoring nebo round-trip pro tyto balíčky) a config_version "1.1". Je uvnitř config.json, takže hash souboru z manifestu, hashový řetězec a podpis jej pokrývají: jeho změna ověření nesplní. kshana evidence verify uvádí úroveň a id řádků.

Změna je aditivní a jen dopředná. format manifestu zůstává kshana-evidence/1: normativní text výše říká, že ověřovač MUSÍ najít tuto hodnotu, a ověřovač clean-room v scripts/ ji najde. Podverze je proto nesena v config_version (u balíčku vytvořeného před 0,40 chybí, což znamená 1,0), nikoli novým řetězcem format. Balíček vytvořený před 0,40 nemá štítek a ověřuje se beze změny; kshana evidence verify říká „no evidence label (pre-0.40 pack)“. Testy jsou tests/evidence_pack_label.rs (včetně odevzdaných balíčků orákul před 0.40) a tests/evidence_labels_doc_sync.rs.

Časové razítko podle RFC 3161 (volitelné)#

Token časového razítka od autority časových razítek ukazuje, že manifest existoval v čase, který autorita potvrzuje. Token je přes manifest.json, takže se žádá až po vytvoření balíčku (neexistuje způsob, jak jej dodat při vytvoření balíčku, a create_bundle odmítne token, který není přesně přes tento manifest):

openssl ts -query -data pack/manifest.json -sha256 -cert -out req.tsq
# send req.tsq to your authority, get the response, then:
kshana evidence attach-timestamp pack/ response.tsr
openssl ts -verify -in response.tsr -data pack/manifest.json -CAfile authority-ca.pem

kshana evidence verify přečte token (holý token nebo úplnou odpověď), zkontroluje, že byl udělen a že jeho otisk zprávy se rovná hashi manifest.json, a vypíše čas, který uvádí. Bez důvěryhodných kotev přidá „timestamp authority signature not verified by Kshana“ (podpis autority časových razítek Kshana neověřila) a hlásí authority_signature_verified: false; pro tuto část spusťte výše uvedený řádek openssl ts -verify. Token, jehož otisk se liší, nebo který nelze zpracovat, je selhání. Typy obsahu tokenu, délka otisku a čas vytvoření se kontrolují z hlediska smyslu.

S --tsa-ca authority-ca.pem (jeden nebo více certifikátů PEM, všechny důvěryhodné) verify kontroluje také autoritu, offline, tak jako openssl ts -verify: otisk zprávy v podepsaných atributech, podpis (RSA PKCS#1 v1.5, RSA-PSS, ECDSA P-256 nebo P-384), vazbu na podpisový certifikát ESS, kritické rozšířené použití klíče timeStamping u podepisujícího a řetězec k samovydané kotvě, přičemž každý certifikát platí v genTime tokenu. Selhání se hlásí jako timestamp-authority-invalid s první kontrolou, která selhala, a authority_signature_verified je pravda pouze tehdy, když prošlo vše. Odvolání (CRL nebo OCSP) se nekontroluje. Tento ověřovač se shoduje s OpenSSL 3.0.13 na 27 vlastních vygenerovaných případech (tests/fixtures/evidence_tsa/PREREGISTRATION.md).

Token leží vedle podepsaného manifestu, nikoli uvnitř něj. Nic v podpisu ani řetězci jej nepokrývá, takže token lze odstranit nebo nahradit jiným platným tokenem, aniž by podpis selhal. verify proto upozorní, když jej balíček nemá, a --require-timestamp činí chybějící token selháním (timestamp-missing); nahrazený token se zachytí jen tehdy, když neodpovídá manifestu. attach-timestamp odmítne přepsat existující token, pokud nepředáte --replace. Pokud na časovém razítku záleží, uchovejte token a výsledek ověření ve vlastních záznamech a vyžadujte jej.

Z kódu#

kshana::evidence::build_receiver_trust_pack je jediný vstupní bod, který příkazový řádek i každý další povrch volá k vytvoření balíčku: dovnitř jde scénář, bajty záznamu (a navigačního souboru, u RINEX), okno jako text, název a volitelný čas vytvoření plus podpisový seed; ven jdou soubory, počet epoch, výřez, hash manifestu a otisk podepisujícího. Nečte žádné soubory ani hodiny, takže stejné vstupy dávají stejné bajty. Pod ním jsou create_bundle a verify_bundle čisté funkce: bajty dovnitř (EvidenceInput, 32bajtový seed, BTreeMap<String, Vec<u8>> souborů), serializovatelné výsledky ven (Files, VerifyReport). Nepoužívají soubory, hodiny ani stav procesu a sestavují se pro wasm32, takže je povrchy Python, WebAssembly a serveru volají beze změny. Příkazový řádek je jejich tenký obal.

Co balíček nedělá#

  • Neprovádí znovu vyhodnocení. Kdokoli s úplným záznamem a config.json může znovu spustit kshana receiver-trust a porovnat s epochs.json; balíček dokazuje, co bylo zaznamenáno, nikoli že engine měl pravdu.
  • Nedokazuje, že záznam je pravý nebo úplný. Hash úplného záznamu váže záznam k jednomu souboru; odkud tento soubor pochází, musí prokázat operátor.
  • Nenese žádné tvrzení o výkonu při detekci nebo odmítnutí interference.
  • Je to technický záznam, nikoli právní stanovisko ani certifikace.