Bevispakker: en signeret registrering af en GNSS-tillidshændelse
En pakke er en teknisk registrering, ikke en juridisk udtalelse. Den angiver, hvad Kshana-motoren beregnede ud fra en angivet modtagerlog under en angiven konfiguration, og lader enhver kontrollere, at intet i den er ændret bagefter. Den siger ikke, hvad der forårsagede en hændelse, hvem der var ansvarlig, om nogen forpligtelse blev nået, eller at loggen viser, hvad modtageren i virkeligheden modtog. Manifestet og oversigten siger begge dette.
En bevispakke er til en hændelsesrapport, en forsikringsgiver eller en myndigheds spørgsmål: »hvad sagde jeres system om GNSS-signalet mellem disse to tidspunkter, og kan vi stole på den registrering?«
Lav en pakke#
# 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
For en pakke lavet ud fra en laboratoriesession registrerer --lab-attestation lab.toml operatørens erklæring om opstillingen (ledningsbåret eller afskærmet, dæmpning eller kabinet, hvad der udsendte eventuel RF) i det signerede manifest; se HARDWARE-SAFETY.md. Den registreres, som den er givet, og verificeres aldrig.
--from og --to er sekunder siden logens første epoke eller et UTC-tidspunkt i ISO-8601, når loggen angiver sin egen starttid. --out må ikke allerede indeholde filer. --created-utc tager et rigtigt UTC-tidspunkt som 2026-01-02T03:04:05Z (alt andet afvises) eller none, som udelader oprettelsestidspunktet og gør pakken reproducerbar byte for byte. Oprettelsestidspunktet er det, underskriveren angiver; intet kontrollerer det mod et ur, og det er det, det valgfrie tidsstempelbevis er til.
Nøglefilen indeholder et Ed25519-seed på 32 byte som 64 hexcifre; angiv din egen (for eksempel en, der opbevares i dit nøglestyringssystem) eller lad keygen lave en. kshana advarer, hvis filen kan læses af andre brugere. .gitignore udelukker *.evidence-key; navngiv din nøgle sådan, hvis den ligger i et checkout.
Hvad der er i en pakke#
| Fil | Indhold |
|---|---|
log-slice.bin |
de rå bytes fra modtagerloggen for vinduet |
config.json |
scenariet som kørt (hver monitortærskel, hændelse og sammenligningsregel), de monitorer, der kørte, kalibreringsbaselinen og scenariehashen; for et fartøj også den vejledende erklæring, som alt output for fartøjer bærer (vejledende software, ikke typegodkendt navigationsudstyr); fra 0,36 geometry_model (tabellen [geometry], som faktisk blev brugt, geometrimonitorens vægt, hver proveniens og klassereglerne). Mappenavne og indlejrede nyttelaster fjernes. |
epochs.json |
hver epoke i vinduet med motorens statistik, alarmerne (årsagerne) og tillidstilstanden; fra 0,36 også dens satellit-geometry (god, forringet med årsager eller ukendt, hvor loggen ikke giver nogen satellitgeometri) og en class på hver slutning (geometry, environment, inconsistency) |
summary.html |
en selvstændig oversigt uden scripts: identifikation, hashes, resultater, årsager, et geometrispor (epoker efter geometristatus) adskilt fra klasserne af slutninger (0,36), hver tærskel, tabellen pr. epoke og registreringens begrænsninger |
manifest.json |
motorversion, vindue, oprettelsestidspunkt, logens format og navn, SHA-256 af hele loggen, udsnittets interval og SHA-256, underskriverens offentlige nøgle og fingeraftryk, hver fils størrelse og SHA-256 og en hashkæde over filerne |
manifest.sig |
Ed25519-signatur over de nøjagtige bytes i manifest.json |
timestamp.tsr |
valgfrit RFC 3161-tidsstempelbevis |
Logudsnit. Læseren rapporterer kildebyteintervallet for hver epoke, så udsnittet er det mindste byteinterval, der rummer hver epoke i vinduet (byte-range): NMEA, u-blox UBX, RINEX 3-observationer og Android-logs. Det hashes som rå bytes, aldrig som afkodet tekst. For en live-strøm eller RINEX 2 findes der intet interval; hele loggen medfølger da, og manifestet siger whole-log (vinduet begrænser, hvilke epoker der rapporteres, ikke hvilke bytes der medtages). Oversigten angiver, hvilket der gælder. Et udsnit er en registrering af vinduets bytes, ikke en selvstændig log: for eksempel har et RINEX-udsnit ingen filheader.
Hashkæde. Manifestet oplister filerne i en fast rækkefølge. link₀ = SHA-256("kshana- evidence-chain/1"); for hver fil linkᵢ = SHA-256(linkᵢ₋₁ ‖ SHA-256(file) ‖ len(name) ‖ name) (længde som 4 bytes, big-endian). Det sidste led er chain_head. At omordne, fjerne eller udskifte en fil ændrer kæden; signaturen dækker kæden.
Verificér en pakke#
kshana evidence verify pack/ --pubkey signer.key.pub [--log session.nmea] \
[--require-timestamp] [--allow-unpinned] [--json]
Afslutningsstatus 0 betyder verificeret; 1 betyder, at noget fejlede; 2 betyder en brugsfejl; 3 betyder, at pakken er intakt, men at dens underskriver ikke var fastlåst (se nedenfor). Hver kontrol rapporterer bestået, fejlet eller sprunget over, og hver fejl navngives:
| Fejlkode | Betydning |
|---|---|
manifest-missing, manifest-malformed, unsupported-format |
manifestet kan ikke læses |
signature-missing, signature-malformed, signature-invalid |
ingen signatur, ikke en hexsignatur på 64 byte med små bogstaver, eller den kan ikke verificeres over manifest.json |
public-key-malformed, public-key-mismatch |
nøglen er ugyldig eller afviger fra den, du angav |
file-missing, file-size-mismatch, file-hash-mismatch |
en opført fil mangler eller afviger fra manifestet; filen navngives |
unlisted-file |
en fil er i pakken, men ikke i manifestet |
chain-mismatch, chain-head-mismatch |
et led er ikke det, dets input giver; filen navngives |
artifact-list-malformed, slice-record-inconsistent, epoch-count-mismatch |
manifestet modsiger sig selv eller epochs.json |
full-log-hash-mismatch, slice-not-from-full-log |
med --log: den log, du har, er ikke den registrerede, eller udsnittet er ikke det interval af den |
timestamp-missing, timestamp-malformed, timestamp-imprint-mismatch |
se nedenfor |
At ændre ét byte i en hvilken som helst fil får verifikationen til at fejle med den tilsvarende årsag: en ændret artefakt rapporteres som den fils hash-mismatch og intet andet; et ændret manifest får signaturen til at fejle. At genberegne hashes og kæden efter redigering af en fil hjælper ikke uden signeringsnøglen; kun signaturen fejler da.
Fastlås nøglen. Uden --pubkey beviser en pakke kun, at den er intakt i forhold til den nøgle, den selv navngiver, og som enhver kan generere. Verifikationen afslutter derfor med 3 og siger »INTACT, BUT THE SIGNER IS NOT PINNED« i stedet for »VERIFIED«; --allow-unpinned accepterer pakkens egen nøgle med vidende og udskriver »VERIFIED against the key the pack names itself (signer NOT pinned; pass --pubkey)«. Få underskriverens offentlige nøgle fra underskriveren ad en vej, du stoler på (ikke fra pakken), og angiv den med --pubkey: 64 hexcifre eller filen .pub. Hvis den private nøgle angives ved en fejl, genkendes det og afvises. Fingeraftrykket (de første 128 bit af nøglens SHA-256, 32 hexcifre) udskrives, så det kan sammenlignes med øjet; at fastlåse hele nøglen er den pålidelige vej.
Pakkeformat og verifikationsprocedure (normativ)#
Dette afsnit er den specifikation, en uafhængig verifikator skrives ud fra. Ord med store bogstaver er krav. En pakke er en mappe (eller et hvilket som helst kort fra filnavn til bytes) med filer, der hedder præcis som nedenfor; en verifikator arbejder på bytes i hver fil, som den er gemt, og serialiserer aldrig noget igen.
Filer. Artefaktfilerne er i denne faste rækkefølge log-slice.bin, config.json, epochs.json, summary.html. En pakke har også manifest.json og manifest.sig og KAN have timestamp.tsr. Ethvert andet navn er en fil uden for listen.
Hex. Hver hexstreng består kun af små bogstaver og 0-9a-f, uden blanktegn; store bogstaver accepteres ikke. SHA-256-værdier er 64 cifre, den offentlige nøgle er 64 cifre, signaturen er 128 cifre.
manifest.sig. Hex med små bogstaver og 128 cifre for Ed25519-signaturen på 64 byte (RFC 8032, den rene Ed25519-ordning) over de nøjagtige bytes i manifest.json, efterfulgt af præcis ét \n og intet andet. Signaturen SKAL kontrolleres strengt: en signatur, hvis S-værdi ikke er mindre end gruppens orden L, er ugyldig.
manifest.json. Et UTF-8 JSON-objekt. Dets medlemmer (en verifikator SKAL finde hvert med den angivne JSON-type; yderligere medlemmer ignoreres):
| medlem | type | betydning |
|---|---|---|
format |
streng | SKAL være lig med kshana-evidence/1 |
title, engine_version, disclaimer |
streng | beskrivende |
created_utc |
streng eller null | oprettelsestidspunkt, beskrivende |
window |
objekt {from_s, to_s} |
tal, sekunder |
epochs_in_window |
ikke-negativt heltal | antallet af poster i JSON-arrayet i epochs.json |
log |
objekt | format streng, file_name streng, full_sha256 hex, full_bytes ikke-negativt heltal, start_label streng eller null og slice |
log.slice |
objekt | kind (præcis byte-range eller whole-log; enhver anden værdi gør manifestet misdannet), start og end ikke-negative heltal (byteforskydninger, slut eksklusiv), sha256 hex |
signer |
objekt | algorithm streng (ed25519), public_key hex (32 bytes), fingerprint streng |
artifacts |
array af objekter | én pr. artefaktfil, i den faste rækkefølge: name streng, role streng, bytes ikke-negativt heltal, sha256 hex, link hex |
chain_head |
hex | det sidste led |
lab_attestation |
objekt, valgfrit | findes kun i en pakke lavet ud fra en laboratoriesession (--lab-attestation): operatørens erklæring operator, organisation, date, setup (conducted eller shielded), attenuation_db eller enclosure, emitter, statement og eventuelt signature, registreret som givet. Den dækkes af manifestets signatur og verificeres aldrig; en verifikator rapporterer den som »attested by the named operator; not verified by Kshana«. Fraværende ellers, og en pakke uden den er uændret |
signer.fingerprint er de første 32 hexcifre af SHA-256 over de 32 bytes i den offentlige nøgle.
Hashkæde. link0 er SHA-256 af de 23 ASCII-bytes kshana-evidence-chain/1. For hver artefakt i rækkefølge, link = SHA-256( previous_link (32 bytes) ‖ SHA-256(file) (32 bytes) ‖ len(name) as 4 bytes big-endian ‖ name as UTF-8 ), hvor SHA-256(file) tages fra manifestets registrerede sha256 for den artefakt (så kæden er en egenskab ved manifestet og kontrolleres mod det, ikke mod filerne).
Procedure. En verifikator udfører disse trin i rækkefølge og samler hver fejl; resultatet er mængden af fejl, hver en code og, hvor det er angivet, filen name. Den MÅ IKKE standse ved den første fejl, undtagen hvor det er angivet. pin er den offentlige nøgle, verifikatoren fik besked om at stole på, hvis nogen.
- Intet
manifest.json: fejlmanifest-missing; stop. - Hvis
manifest.jsonikke er et JSON-objekt med hvert medlem ovenfor i de angivne typer (herunderartifactsposter med hvert medlem oglog.slice): fejlmanifest-malformed. Ellers, hvisformatikke er værdien ovenfor:unsupported-format. - Signaturfase. Denne fase udføres, uanset om manifestet kunne læses. Hvis manifestet kunne læses, er den navngivne nøgle
signer.public_key; hvis den ikke er 64 hexcifre med små bogstaver, fejlpublic-key-malformed(ingen navngiven nøgle); ellers, hvissigner.fingerprintikke er dens fingeraftryk, fejlpublic-key-malformed(nøglen forbliver brugbar). Hvis både enpinog en navngiven nøgle findes og er forskellige: fejlpublic-key-mismatch. Kontrolnøglen erpin, hvis en blev givet, ellers den navngivne nøgle, hvis der er en; med ingen af dem er der ingen kontrolnøgle (det gælder også alle tilfælde, hvor manifestet ikke kunne læses, og ingenpinblev givet). Hvis der ikke er nogenmanifest.sig:signature-missing. Ellers, hvis filen ikke er præcis 128 hexcifre med små bogstaver plus ét\n:signature-malformed. Ellers, hvis der er en kontrolnøgle, som ikke er en gyldig Ed25519-offentlig-nøgle-kodning:public-key-malformed. Ellers, hvis der er en kontrolnøgle, og signaturen ikke kan verificeres strengt over bytes imanifest.json(uanset hvad de er):signature-invalid. (Uden kontrolnøgle kontrolleres signaturen ikke, og der rejses ingen fejl for det.) - Hvis manifestet ikke kunne læses (trin 2 gav
manifest-malformed): stop her. - Artefakter. Hvis navnene i
artifactsikke præcis er de fire ovenfor i rækkefølge:artifact-list-malformed. Derefter for hver manifestpost i rækkefølge: hvis pakken ingen fil har med det navn,file-missing(navn); ellers, hvis dens længde afviger frabytes,file-size-mismatch(navn); og uafhængigt heraf, hvis SHA-256 af filen afviger frasha256,file-hash-mismatch(navn). - Filer uden for listen: for hver fil i pakken udover
manifest.json,manifest.sig,timestamp.tsrog navnene i manifestetsartifacts:unlisted-file(navn). - Kæde. Gå gennem manifestets
artifactsi rækkefølge fralink0. Hvis en postssha256ikke er 64 hexcifre med små bogstaver:manifest-malformed, og kæden opgives (ingen kontrol af hovedet). Ellers beregnes leddet; hvis det afviger fra postenslink:chain-mismatch(navn). Efter gennemgangen, hvis kæden ikke blev opgivet, og det sidste led afviger frachain_head:chain-head-mismatch. - Udsnit og epoker. Rejs
slice-record-inconsistent(højst én gang pr. årsag, og en verifikator rapporterer koden én gang, uanset hvor mange årsager der gælder), hvis noget af følgende gælder:start > end;end > full_bytes; postenlog-slice.binharsha256, der afviger fraslice.sha256; den postsbytesafviger fraend - start; ellerkinderwhole-log, og et afstart != 0,end != full_bytes,slice.sha256 != full_sha256gælder. Hvisepochs.jsonfindes og kan fortolkes som et JSON-array, hvis længde afviger fraepochs_in_window:epoch-count-mismatch. - Hel log. Kun hvis verifikatoren fik de oprindelige logbytes: hvis deres SHA-256 afviger fra
log.full_sha256,full-log-hash-mismatch; ellers, hvis intervalletstart..endikke ligger inde i loggen, eller logbytes dér afviger fralog-slice.bin,slice-not-from-full-log. - Tidsstempel. Uden
timestamp.tsr: hvis verifikatoren fik besked om at kræve et,timestamp-missing; ellers intet. Med et: hvis det ikke kan læses som et RFC 3161-bevis eller -svar (se nedenfor),timestamp-malformed; ellers, hvis dets meddelelsesaftryk ikke er hashen, i aftrykkets egen algoritme (SHA-256, SHA-384 eller SHA-512), af bytes imanifest.json:timestamp-imprint-mismatch.
Pakken er verificeret netop når mængden af fejl er tom. Om underskriveren var fastlåst, rapporteres separat og ændrer ikke mængden af fejl.
Tidsstempelbevis. Et bevis er en DER-ContentInfo, hvis indholdstype er CMS SignedData (1.2.840.113549.1.7.2), og hvis indkapslede indholdstype er id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4); et svar er en DER-TimeStampResp (en PKIStatusInfo efterfulgt af beviset), hvis status er 0 eller 1. Meddelelsesaftrykkets algoritme i TSTInfo skal være SHA-256, SHA-384 eller SHA-512 med en digest af den algoritmes længde, og dens genTime et rigtigt UTC-kalendertidspunkt. BER med ubestemt længde accepteres ikke. Autoritetens signatur og certifikatkæde kontrolleres kun, når betroede ankre er givet (--tsa-ca, nedenfor).
Evidensetiketten (0,40)#
En pakke bygget af 0,40 eller senere bærer inde i config.json en post evidence (niveauet, VALIDATED, MODELLED eller PARTNER, som er den svageste status blandt dens rækker, og id'erne og kravene for de verifikationsmatrixrækker, pakkens indhold hviler på: rækken for pakkeintegritet, rækkerne for receiver-trust og rækkerne for jammertest-scoring eller rundtur for disse pakker) og config_version "1.1". Den ligger inde i config.json, så manifestets filhash, hashkæden og signaturen dækker den: at ændre den får verificeringen til at fejle. kshana evidence verify rapporterer niveauet og række-id'erne.
Ændringen er additiv og kun fremadrettet. Manifestets format forbliver kshana-evidence/1: den normative tekst ovenfor siger, at en verifikator SKAL finde den værdi, og clean-room-verifikatoren i scripts/ gør det. Den mindre version bæres derfor af config_version (fraværende i en pakke lavet før 0,40, hvilket betyder 1,0), ikke af en ny streng format. En pakke lavet før 0,40 har ingen etiket og verificeres uændret; kshana evidence verify siger "no evidence label (pre-0.40 pack)". Testene er tests/evidence_pack_label.rs (herunder de indlagte uafhængige pakker fra før 0.40) og tests/evidence_labels_doc_sync.rs.
RFC 3161-tidsstempel (valgfrit)#
Et tidsstempelbevis fra en tidsstemplingsautoritet viser, at manifestet fandtes på et tidspunkt, autoriteten bekræfter. Beviset er over manifest.json, så det rekvireres, efter pakken er lavet (der er ingen måde at levere et på, når pakken oprettes, og create_bundle afviser et bevis, der ikke er over netop det 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 læser beviset (et nøgent bevis eller et fuldt svar), kontrollerer, at det blev givet, og at dets meddelelsesaftryk svarer til hashen af manifest.json, og udskriver det tidspunkt, det angiver. Uden betroede ankre tilføjer den »timestamp authority signature not verified by Kshana« og rapporterer authority_signature_verified: false; kør linjen openssl ts -verify ovenfor for den del. Et bevis, hvis aftryk afviger, eller som ikke kan fortolkes, er en fejl. Bevisets indholdstyper, aftryks længde og genereringstidspunkt kontrolleres for rimelighed.
Med --tsa-ca authority-ca.pem (ét eller flere PEM-certifikater, alle betroede) kontrollerer verify også autoriteten, offline, på samme måde som openssl ts -verify: de signerede attributters meddelelsesdigest, signaturen (RSA PKCS#1 v1.5, RSA-PSS, ECDSA P-256 eller P-384), ESS-bindingen til signeringscertifikatet, underskriverens kritiske udvidede nøgleanvendelse timeStamping og kæden til et selvudstedt anker, hvor hvert certifikat er gyldigt ved beviset genTime. En fejl rapporteres som timestamp-authority-invalid med den første kontrol, der fejlede, og authority_signature_verified er kun true, når alt bestod. Tilbagekaldelse (CRL eller OCSP) kontrolleres ikke. Denne verifikator stemmer overens med OpenSSL 3.0.13 på 27 selvgenererede tilfælde (tests/fixtures/evidence_tsa/PREREGISTRATION.md).
Beviset ligger ved siden af det signerede manifest, ikke inde i det. Intet i signaturen eller kæden dækker det, så et bevis kan fjernes, eller et andet gyldigt bevis kan sættes ind, uden at signaturen fejler. verify noterer derfor, når en pakke ikke har noget, og --require-timestamp gør et manglende bevis til en fejl (timestamp-missing); et indsat bevis opdages kun, hvis det ikke passer til manifestet. attach-timestamp afviser at overskrive et eksisterende bevis, medmindre du angiver --replace. Hvis et tidsstempel betyder noget for dig, så opbevar beviset og verifikationsresultatet hos dine egne optegnelser, og kræv det.
Fra kode#
kshana::evidence::build_receiver_trust_pack er det ene indgangspunkt, som kommandolinjen og enhver anden flade kalder for at lave en pakke: et scenarie, logens bytes (og navigationsfilens, for RINEX), vinduet som tekst, en titel og et valgfrit oprettelsestidspunkt samt signeringsseedet ind; filerne, antallet af epoker, udsnittet, manifesthashen og underskriverens fingeraftryk ud. Den læser ingen filer og intet ur, så de samme input giver de samme bytes. Under den er create_bundle og verify_bundle rene funktioner: bytes ind (EvidenceInput, et seed på 32 byte, et BTreeMap<String, Vec<u8>> af filer), serialiserbare resultater ud (Files, VerifyReport). De bruger ingen filer, intet ur og ingen processtilstand og bygges til wasm32, så Python-, WebAssembly- og serverfladerne kalder dem uændret. Kommandolinjen er en tynd indpakning om dem.
Hvad en pakke ikke gør#
- Den kører ikke vurderingen igen. Enhver med den fulde log og
config.jsonkan kørekshana receiver-trustigen og sammenligne medepochs.json; pakken beviser, hvad der blev registreret, ikke at motoren havde ret. - Den beviser ikke, at loggen er ægte eller fuldstændig. Hashen af den fulde log binder registreringen til én fil; hvor den fil kom fra, er det operatørens sag at fastslå.
- Den indeholder intet krav om ydeevne for detektion eller afvisning af interferens.
- Den er en teknisk registrering og ikke en juridisk udtalelse eller en certificering.