Bevispaket: en signerad registrering av en GNSS-förtroendehändelse
Ett paket är en teknisk registrering, inte ett juridiskt utlåtande. Det anger vad Kshana-motorn beräknade utifrån en angiven mottagarlogg under en angiven konfiguration, och låter vem som helst kontrollera att inget i det ändrades efteråt. Det säger inte vad som orsakade en händelse, vem som var ansvarig, om någon skyldighet uppfylldes, eller att loggen visar vad mottagaren verkligen tog emot. Manifestet och sammanfattningen säger båda detta.
Ett bevispaket är till för en incidentrapport, en försäkringsgivare eller en myndighetsfråga: "vad sade ert system om GNSS-signalen mellan dessa två tidpunkter, och kan vi lita på den registreringen?"
Skapa ett paket#
# 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
För ett paket som skapats från en laboratoriesession registrerar --lab-attestation lab.toml operatörens uttalande om uppställningen (ledningsbunden eller skärmad, dämpning eller kapsling, vad som sände ut eventuell RF) i det signerade manifestet; se HARDWARE-SAFETY.md. Det registreras som det angavs och verifieras aldrig.
--from och --to är sekunder sedan loggens första epok, eller en ISO-8601 UTC-tid när loggen anger sin egen starttid. --out får inte redan innehålla filer. --created-utc tar en verklig UTC-tid som 2026-01-02T03:04:05Z (allt annat avvisas) eller none, vilket utelämnar skapandetiden och gör paketet byte för byte reproducerbart. Skapandetiden är vad undertecknaren anger; ingenting kontrollerar den mot en klocka, vilket är vad den valfria tidsstämpelstoken är till för.
Nyckelfilen innehåller ett Ed25519-frö på 32 byte som 64 hexadecimala siffror; ange ditt eget (till exempel ett som förvaras i ditt nyckelhanteringssystem) eller låt keygen skapa ett. kshana varnar om filen kan läsas av andra användare. .gitignore utesluter *.evidence-key; ge din nyckel ett sådant namn om den ligger i en utcheckad kopia.
Vad ett paket innehåller#
| Fil | Innehåll |
|---|---|
log-slice.bin |
de råa bytena i mottagarloggen för fönstret |
config.json |
scenariot som körts (varje övervakartröskel, händelse och jämförelseregel), de övervakare som kördes, kalibreringens baslinje och scenariots hashvärde; för ett fartyg även det rådgivande uttalande som varje fartygsutdata bär (rådgivande programvara, inte typgodkänd navigationsutrustning); från 0,36 även geometry_model (den tabell [geometry] som faktiskt användes, geometriövervakarens vikt, varje ursprung och klassreglerna). Katalognamn och inbäddade nyttolaster tas bort. |
epochs.json |
varje epok i fönstret med motorns statistik, larmen (skälen) och förtroendetillståndet; från 0,36 även dess satellit-geometry (bra, försämrad med skäl, eller okänd där loggen inte ger någon satellitgeometri) och en class för varje slutsats (geometry, environment, inconsistency) |
summary.html |
en fristående, skriptfri sammanfattning: identifiering, hashvärden, resultat, skäl, ett geometrispår (epoker efter geometristatus) skilt från slutsatsernas klasser (0,36), varje tröskel, tabellen per epok och registreringens begränsningar |
manifest.json |
motorversion, fönster, skapandetid, loggens format och namn, SHA-256 för hela loggen, utsnittets intervall och SHA-256, undertecknarens publika nyckel och fingeravtryck, varje fils storlek och SHA-256 samt en hashkedja över filerna |
manifest.sig |
Ed25519-signatur över de exakta bytena i manifest.json |
timestamp.tsr |
valfri tidsstämpelstoken enligt RFC 3161 |
Loggutsnitt. Läsaren redovisar källans byteintervall för varje epok, så utsnittet är det minsta byteintervall som rymmer varje epok i fönstret (byte-range): NMEA, u-blox UBX, RINEX 3-observationer och Android-loggar. Det hashas som råa bytes, aldrig som avkodad text. För en direktström eller RINEX 2 finns inget intervall; hela loggen medföljer då och manifestet säger whole-log (fönstret begränsar vilka epoker som redovisas, inte vilka bytes som ingår). Sammanfattningen anger vilket som gäller. Ett utsnitt är en registrering av fönstrets bytes, inte en fristående logg: till exempel har ett RINEX-utsnitt inget filhuvud.
Hashkedja. Manifestet listar filerna i fast ordning. link₀ = SHA-256("kshana- evidence-chain/1"); för varje fil, linkᵢ = SHA-256(linkᵢ₋₁ ‖ SHA-256(file) ‖ len(name) ‖ name) (längd som 4 bytes, big-endian). Den sista länken är chain_head. Att ändra ordning på, ta bort eller byta ut en fil ändrar kedjan; signaturen täcker kedjan.
Verifiera ett paket#
kshana evidence verify pack/ --pubkey signer.key.pub [--log session.nmea] \
[--require-timestamp] [--allow-unpinned] [--json]
Slutstatus 0 betyder verifierat; 1 betyder att något misslyckades; 2 betyder ett användningsfel; 3 betyder att paketet är intakt men att dess undertecknare inte var fastlåst (se nedan). Varje kontroll rapporterar godkänd, underkänd eller hoppad över, och varje fel namnges:
| Felkod | Betydelse |
|---|---|
manifest-missing, manifest-malformed, unsupported-format |
manifestet kan inte läsas |
signature-missing, signature-malformed, signature-invalid |
ingen signatur, ingen hexadecimal signatur på 64 byte med gemener, eller så verifieras den inte över manifest.json |
public-key-malformed, public-key-mismatch |
nyckeln är inte giltig, eller skiljer sig från den du angav |
file-missing, file-size-mismatch, file-hash-mismatch |
en listad fil saknas eller skiljer sig från manifestet; filen namnges |
unlisted-file |
en fil finns i paketet men inte i manifestet |
chain-mismatch, chain-head-mismatch |
en länk är inte vad dess indata ger; filen namnges |
artifact-list-malformed, slice-record-inconsistent, epoch-count-mismatch |
manifestet motsäger sig självt eller epochs.json |
full-log-hash-mismatch, slice-not-from-full-log |
med --log: loggen du har är inte den som registrerades, eller så är utsnittet inte det intervallet av den |
timestamp-missing, timestamp-malformed, timestamp-imprint-mismatch |
se nedan |
Att ändra en enda byte i någon fil gör att verifieringen misslyckas med motsvarande skäl: en ändrad artefakt rapporteras som den filens hashavvikelse och inget annat; ett ändrat manifest gör att signaturen misslyckas. Att räkna om hashvärden och kedjan efter att en fil redigerats hjälper inte utan signeringsnyckeln; då är det bara signaturen som misslyckas.
Lås fast nyckeln. Utan --pubkey bevisar ett paket bara att det är intakt mot den nyckel det själv anger, som vem som helst kan generera. Verifieringen avslutas därför med 3 och säger "INTACT, BUT THE SIGNER IS NOT PINNED" i stället för "VERIFIED"; --allow-unpinned godtar paketets egen nyckel med öppna ögon och skriver "VERIFIED against the key the pack names itself (signer NOT pinned; pass --pubkey)". Hämta undertecknarens publika nyckel från undertecknaren på en väg du litar på (inte från paketet) och ange den med --pubkey: 64 hexadecimala siffror eller filen .pub. Att av misstag ange den privata nyckeln känns igen och avvisas. Fingeravtrycket (de första 128 bitarna av nyckelns SHA-256, 32 hexadecimala siffror) skrivs ut för att jämföras med blotta ögat; att låsa fast hela nyckeln är den pålitliga vägen.
Paketformat och verifieringsprocedur (normativt)#
Det här avsnittet är den specifikation som en oberoende verifierare skrivs utifrån. Ord med versaler är krav. Ett paket är en katalog (eller vilken avbildning som helst från filnamn till bytes) med filer som heter exakt som nedan; en verifierare arbetar med bytena i varje fil så som de är lagrade och serialiserar aldrig om något.
Filer. Artefaktfilerna, i denna fasta ordning, är log-slice.bin, config.json, epochs.json, summary.html. Ett paket har också manifest.json och manifest.sig, och KAN ha timestamp.tsr. Varje annat namn är en olistad fil.
Hex. Varje hexsträng består enbart av gemena 0-9a-f, utan blanksteg; versaler godtas inte. SHA-256-värden är 64 siffror, den publika nyckeln är 64 siffror, signaturen är 128 siffror.
manifest.sig. Den hexadecimala texten med 128 siffror och gemener för den 64 byte långa Ed25519-signaturen (RFC 8032, det rena Ed25519-schemat) över de exakta bytena i manifest.json, följd av exakt en \n och inget annat. Signaturen MÅSTE kontrolleras strikt: en signatur vars S-värde inte är mindre än gruppordningen L är ogiltig.
manifest.json. Ett UTF-8 JSON-objekt. Dess medlemmar (en verifierare MÅSTE hitta var och en med den angivna JSON-typen; ytterligare medlemmar ignoreras):
| medlem | typ | betydelse |
|---|---|---|
format |
sträng | MÅSTE vara lika med kshana-evidence/1 |
title, engine_version, disclaimer |
sträng | beskrivande |
created_utc |
sträng eller null | skapandetid, beskrivande |
window |
objekt {from_s, to_s} |
tal, sekunder |
epochs_in_window |
icke-negativt heltal | antalet poster i JSON-matrisen i epochs.json |
log |
objekt | format sträng, file_name sträng, full_sha256 hex, full_bytes icke-negativt heltal, start_label sträng eller null, och slice |
log.slice |
objekt | kind (exakt byte-range eller whole-log; varje annat värde gör manifestet felformat), start och end icke-negativa heltal (byteavstånd, slutet exkluderat), sha256 hex |
signer |
objekt | algorithm sträng (ed25519), public_key hex (32 bytes), fingerprint sträng |
artifacts |
matris av objekt | ett per artefaktfil, i den fasta ordningen: name sträng, role sträng, bytes icke-negativt heltal, sha256 hex, link hex |
chain_head |
hex | den sista länken |
lab_attestation |
objekt, valfritt | finns bara i ett paket som skapats från en laboratoriesession (--lab-attestation): operatörens uttalande operator, organisation, date, setup (conducted eller shielded), attenuation_db eller enclosure, emitter, statement och eventuellt signature, registrerat som det angavs. Det täcks av manifestets signatur och verifieras aldrig; en verifierare rapporterar det som "attested by the named operator; not verified by Kshana". Saknas annars, och ett paket utan det är oförändrat |
signer.fingerprint är de första 32 hexadecimala siffrorna av SHA-256 över de 32 byten i den publika nyckeln.
Hashkedja. link0 är SHA-256 av de 23 ASCII-byten kshana-evidence-chain/1. För varje artefakt i ordning, link = SHA-256( previous_link (32 bytes) ‖ SHA-256(file) (32 bytes) ‖ len(name) as 4 bytes big-endian ‖ name as UTF-8 ), där SHA-256(file) hämtas från manifestets registrerade sha256 för den artefakten (så kedjan är en egenskap hos manifestet och kontrolleras mot det, inte mot filerna).
Procedur. En verifierare utför dessa steg i ordning och samlar varje fel; resultatet är mängden fel, vart och ett en code och, där det anges, filens name. Den FÅR INTE stanna vid det första felet utom där det anges. pin är den publika nyckel som verifieraren fått veta att den ska lita på, om någon.
- Ingen
manifest.json: felmanifest-missing; stanna. - Om
manifest.jsoninte är ett JSON-objekt med varje medlem ovan i angivna typer (inklusiveartifacts-poster med varje medlem, ochlog.slice): felmanifest-malformed. Annars, omformatinte är det värde som anges ovan:unsupported-format. - Signaturfas. Den här fasen utförs oavsett om manifestet gick att läsa. Om manifestet gick att läsa är den angivna nyckeln
signer.public_key; om den inte är 64 hexadecimala siffror med gemener, felpublic-key-malformed(ingen angiven nyckel); annars, omsigner.fingerprintinte är fingeravtrycket av den, felpublic-key-malformed(nyckeln förblir användbar). Om både enpinoch en angiven nyckel finns och skiljer sig åt: felpublic-key-mismatch. Kontrollnyckeln ärpinom en angavs, annars den angivna nyckeln om det finns en; med ingen av dem finns ingen kontrollnyckel (detta gäller varje fall där manifestet inte gick att läsa och ingenpinangavs). Om det inte finns någonmanifest.sig:signature-missing. Annars, om filen inte är exakt 128 hexadecimala siffror med gemener plus en\n:signature-malformed. Annars, om det finns en kontrollnyckel som inte är en giltig kodning av en publik Ed25519-nyckel:public-key-malformed. Annars, om det finns en kontrollnyckel och signaturen inte verifieras strikt över bytena imanifest.json(vad de än är):signature-invalid. (Utan kontrollnyckel kontrolleras inte signaturen och inget fel uppstår för det.) - Om manifestet inte gick att läsa (steg 2 gav
manifest-malformed): stanna här. - Artefakter. Om namnen i
artifactsinte exakt är de fyra ovan i ordning:artifact-list-malformed. Sedan, för varje manifestpost i ordning: om paketet inte har någon fil med det namnet,file-missing(namn); annars, om dess längd skiljer sig frånbytes,file-size-mismatch(namn); och, oberoende av det, om SHA-256 för filen skiljer sig frånsha256,file-hash-mismatch(namn). - Olistade filer: för varje fil i paketet utöver
manifest.json,manifest.sig,timestamp.tsroch namnen i manifestetsartifacts:unlisted-file(namn). - Kedja. Gå igenom manifestets
artifactsi ordning frånlink0. Om en postssha256inte är 64 hexadecimala siffror med gemener:manifest-malformedoch kedjan överges (ingen kontroll av huvudet). Annars beräknas länken; om den skiljer sig från postenslink:chain-mismatch(namn). Efter genomgången, om kedjan inte övergavs och den sista länken skiljer sig frånchain_head:chain-head-mismatch. - Utsnitt och epoker. Utlös
slice-record-inconsistent(högst en gång per skäl, och en verifierare rapporterar koden en gång oavsett hur många skäl som gäller) om något av:start > end;end > full_bytes; postenslog-slice.binhar ettsha256som skiljer sig frånslice.sha256; den postensbytesskiljer sig frånend - start; ellerkindärwhole-logoch något avstart != 0,end != full_bytes,slice.sha256 != full_sha256. Omepochs.jsonfinns och tolkas som en JSON-matris vars längd skiljer sig frånepochs_in_window:epoch-count-mismatch. - Hela loggen. Endast om verifieraren fick de ursprungliga logg-bytena: om deras SHA-256 skiljer sig från
log.full_sha256,full-log-hash-mismatch; annars, om intervalletstart..endinte ligger inom loggen eller om loggens bytes där skiljer sig frånlog-slice.bin,slice-not-from-full-log. - Tidsstämpel. Utan
timestamp.tsr: om verifieraren fått veta att den ska kräva en,timestamp-missing; annars ingenting. Med en: om den inte kan läsas som en token eller ett svar enligt RFC 3161 (se nedan),timestamp-malformed; annars, om dess meddelandeavtryck inte är hashvärdet, med avtryckets egen algoritm (SHA-256, SHA-384 eller SHA-512), av bytena imanifest.json:timestamp-imprint-mismatch.
Paketet är verifierat exakt när felmängden är tom. Om undertecknaren var fastlåst rapporteras separat och ändrar inte felmängden.
Tidsstämpelstoken. En token är en DER-kodad ContentInfo vars innehållstyp är CMS SignedData (1.2.840.113549.1.7.2) och vars inkapslade innehållstyp är id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4); ett svar är ett DER-kodat TimeStampResp (en PKIStatusInfo följd av token) vars status är 0 eller 1. Algoritmen för meddelandeavtrycket i TSTInfo måste vara SHA-256, SHA-384 eller SHA-512 med ett kondensat av den algoritmens längd, och dess genTime en verklig UTC-kalendertid. BER med obestämd längd godtas inte. Myndighetens signatur och certifikatkedja kontrolleras endast när betrodda ankare anges (--tsa-ca, nedan).
Evidensetiketten (0,40)#
Ett paket byggt med 0,40 eller senare bär, inuti config.json, en post evidence (nivån, VALIDATED, MODELLED eller PARTNER, som är den svagaste statusen bland dess rader, samt id:na och kraven för de rader i verifieringsmatrisen som paketets innehåll vilar på: raden för paketets integritet, raderna för receiver-trust, och raderna för jammertest-poängsättning eller rundtur för sådana paket) och config_version "1.1". Den ligger inuti config.json, så manifestets filhash, hashkedjan och signaturen täcker den: att ändra den gör att verifieringen misslyckas. kshana evidence verify redovisar nivån och radernas id.
Ändringen är additiv och gäller bara framåt. Manifestets format förblir kshana-evidence/1: den normativa texten ovan säger att en verifierare MÅSTE hitta det värdet, och verifieraren byggd från rent blad i scripts/ gör det. Den lägre versionen bärs därför av config_version (saknas för ett paket gjort före 0,40, vilket betyder 1,0), inte av en ny sträng format. Ett paket gjort före 0,40 har ingen etikett och verifieras oförändrat; kshana evidence verify säger "no evidence label (pre-0.40 pack)". Testerna är tests/evidence_pack_label.rs (inklusive de incheckade oberoende paketen från före 0.40) och tests/evidence_labels_doc_sync.rs.
Tidsstämpel enligt RFC 3161 (valfri)#
En tidsstämpelstoken från en tidsstämpelmyndighet visar att manifestet fanns vid en tidpunkt som myndigheten intygar. Token gäller manifest.json, så den begärs efter att paketet skapats (det går inte att ange en när paketet skapas, och create_bundle avvisar en token som inte gäller exakt det manifestet):
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 token (en ren token eller ett fullständigt svar), kontrollerar att den beviljades och att dess meddelandeavtryck är lika med hashvärdet av manifest.json, och skriver ut den tid den anger. Utan betrodda ankare lägger den till "timestamp authority signature not verified by Kshana" och rapporterar authority_signature_verified: false; kör raden openssl ts -verify ovan för den delen. En token vars avtryck skiljer sig, eller som inte går att tolka, är ett fel. Tokenens innehållstyper, avtryckslängd och genereringstid kontrolleras för rimlighet.
Med --tsa-ca authority-ca.pem (ett eller flera PEM-certifikat, alla betrodda) kontrollerar verify också myndigheten, offline, på det sätt som openssl ts -verify gör: de signerade attributens meddelandekondensat, signaturen (RSA PKCS#1 v1.5, RSA-PSS, ECDSA P-256 eller P-384), ESS-bindningen till signeringscertifikatet, undertecknarens kritiska utökade nyckelanvändning timeStamping och kedjan till ett självutfärdat ankare, där varje certifikat är giltigt vid tokenens genTime. Ett fel rapporteras som timestamp-authority-invalid med den första kontroll som misslyckades, och authority_signature_verified är sant endast när allt godkändes. Återkallelse (CRL eller OCSP) kontrolleras inte. Den här verifieraren stämmer överens med OpenSSL 3.0.13 på 27 självgenererade fall (tests/fixtures/evidence_tsa/PREREGISTRATION.md).
Token ligger bredvid det signerade manifestet, inte i det. Ingenting i signaturen eller kedjan täcker den, så en token kan tas bort, eller en annan giltig token sättas in, utan att signaturen misslyckas. verify noterar därför när ett paket saknar en, och --require-timestamp gör en saknad token till ett fel (timestamp-missing); en utbytt token upptäcks bara om den inte matchar manifestet. attach-timestamp vägrar skriva över en befintlig token om du inte anger --replace. Om en tidsstämpel är viktig för dig, behåll token och verifieringsresultatet bland dina egna uppgifter, och kräv den.
Från kod#
kshana::evidence::build_receiver_trust_pack är den enda ingångspunkt som kommandoraden och varje annan yta anropar för att skapa ett paket: ett scenario, loggens bytes (och navigeringsfilens, för RINEX), fönstret som text, en titel och en valfri skapandetid, plus signeringsfröet, in; filerna, antalet epoker, utsnittet, manifestets hashvärde och undertecknarens fingeravtryck ut. Den läser inga filer och ingen klocka, så samma indata ger samma bytes. Under den är create_bundle och verify_bundle rena funktioner: bytes in (EvidenceInput, ett frö på 32 byte, en BTreeMap<String, Vec<u8>> av filer), serialiserbara resultat ut (Files, VerifyReport). De använder inga filer, ingen klocka och inget processtillstånd, och bygger för wasm32, så ytorna för Python, WebAssembly och server anropar dem oförändrade. Kommandoraden är ett tunt skal runt dem.
Vad ett paket inte gör#
- Det kör inte om bedömningen. Den som har hela loggen och
config.jsonkan köra omkshana receiver-trustoch jämföra medepochs.json; paketet bevisar vad som registrerades, inte att motorn hade rätt. - Det bevisar inte att loggen är äkta eller fullständig. Hashvärdet av hela loggen knyter registreringen till en fil; var den filen kommer ifrån är upp till operatören att fastställa.
- Det innehåller inget prestandapåstående om detektering eller avvisning av störning.
- Det är en teknisk registrering och inte ett juridiskt utlåtande eller en certifiering.