# Overte si záznam Walk-Check sami

*[In English](README.md)*

Dostali ste odkaz alebo PDF protokol o obchôdzke stroja a máte sa podľa neho
rozhodnúť. Týmto nástrojom si to overíte **bez účtu, bez našich serverov a bez
dôvery v nás**.

Neimportuje kód aplikácie, ktorá tie údaje vyrobila, a nevolá naše API. Verejný
kľúč si prečíta zo súboru a zvyšok si prepočíta sám. O to práve ide: ak nám
neveríte, nemusíte.

---

## Čo potrebujete

| | |
|---|---|
| Dôkaz | PDF protokol, ktorý ste dostali, alebo zdieľaný odkaz `walkcheck.eu/share/<kód>`, ktorý PDF ponúka |
| Python | 3.12 alebo novší |
| Tri balíky | `cryptography`, `pydantic`, `structlog` |
| `pdfdetach` | súčasť Poppleru (`poppler-utils` alebo `poppler`), na vytiahnutie dôkazu z PDF |
| Účet | **nie.** Nič sa tu nikam neprihlasuje |
| Sieť | len na jedno stiahnutie nástroja a kľúča. Samotné overenie je offline |

`psycopg` a `boto3` sú v `pyproject.toml`, ale načítajú sa len vtedy, keď čítate
reťazec priamo z databázy alebo si pýtate kľúč z AWS KMS. Overenie zo súboru ani
jedno nepotrebuje.

## Overenie v jednom bloku

Všetko nižšie sa dá skopírovať tak, ako stojí. Zmeňte len meno svojho PDF.

```bash
# 1. nástroj, kľúč a kotva autority časovej pečiatky (stiahnu sa raz)
curl -O https://walkcheck.eu/verifier/verify_chain.py
curl -O https://walkcheck.eu/verifier/_asn1.py
curl -O https://walkcheck.eu/verifier/_tsa.py
curl -O https://walkcheck.eu/verifier/_anchor.py
curl -O https://walkcheck.eu/verifier/_worm.py
curl -O https://walkcheck.eu/verifier/walkcheck-audit-public-key.der
curl -O https://walkcheck.eu/verifier/tsa-trust-freetsa.pem

# 2. tri balíky, vo vlastnom prostredí
#    (mnohé systémy pip install mimo neho odmietnu)
python3 -m venv venv
. venv/bin/activate
python3 -m pip install "cryptography>=43" "pydantic>=2.9" "structlog>=24.4"

# 3. strojovo čitateľný dôkaz, priložený v PDF
#    (len dôkaz: súbor podstrčený do PDF by vedľa nástroja mohol zaujať jeho miesto)
pdfdetach -savefile walkcheck-proof.json protokol.pdf

# 4. samotné overenie
python3 verify_chain.py \
  --export walkcheck-proof.json \
  --public-key walkcheck-audit-public-key.der \
  --tsa-trust tsa-trust-freetsa.pem
```

Kľúč a kotvu berte z `walkcheck.eu/verifier/`, nikdy nie kľúč, ktorý prišiel spolu
s PDF: kto má súkromný kľúč, podpíše ním čokoľvek, takže overenie je presne také dobré
ako kľúč, ktorý mu dáte.

## Čo výstup znamená

Skutočný beh kroku 4 nad produkčným záznamom vypíše na štandardný výstup presne toto
(záznamy logu idú na chybový výstup; nástroj hovorí po anglicky zámerne: sťahuje si ho
aj ten, kto po slovensky nevie):

```
STORAGE: pilot mode, the record is NOT under an irreversible lock; it is kept for at least four years, then in a deep archive, and only Walk-Check can delete it, as an exception (this does not weaken the chain)
VERIFIED (sealed=True, records=10), NOT CHECKED: global anchor (--require-anchor), WORM attestation (--require-attestation)
SEALED HEAD (record_hash): b321eb4bf2b55b0a57eefaf01f059463f728cb6c1746dd48b8e0b677f0f07e34, WALKAROUND ID: 4b3bcb62-2ea9-4c05-ab88-164e80e3de32
LOCATION (reported by device): none in chain (record predates device location)
```

- `STORAGE:` sa objaví len pri zázname uloženom v pilotnom režime (pozri koniec stránky).
- Riadok verdiktu. `records=` je počet položiek v reťazci tohto záznamu.
- `SEALED HEAD` a `WALKAROUND ID` **sa musia zhodovať s PDF** (polia s tým istým menom).
  Ak nie, príloha nie je záznam, ktorý PDF opisuje: neopierajte sa o ňu.
  Nástroj overuje prílohu, nie vytlačené strany: kde sa PDF a príloha líšia (výsledok,
  registrácia, hash videa), zapečatená je príloha.
- `LOCATION` je poloha, ktorú nahlásil telefón, ak nejakú. Verdikt nemení nikdy.

| Verdikt | Čo hovorí | Kód |
|---|---|---|
| `VERIFIED` | reťazec je neporušený od začiatku, každý hash záznamu aj payloadu sedí, každá položka nesie platný ECDSA podpis a pečať platnú časovú pečiatku | 0 |
| `TAMPERED` | záznam nesedí s tým, čo bolo zapečatené. S kľúčom a kotvou z tejto stránky to znamená zmenu po zapečatení; so zlým kľúčom alebo zlým súborom kotvy tu padne aj zdravý záznam. Riadok menuje položku aj dôvod, napríklad `TAMPERED at seq=284 reason=payload_sha256_mismatch`, a nič iné z reťazca sa nevypíše | 1 |
| `PENDING` | zapečatené, ale atestácia úložiska ho zatiaľ nekryje. Objaví sa len s `--require-attestation` a jeho údajmi (nižšie). Nie je to finalizovaný dôkaz | 0 |
| `UNSEALED` | reťazec je vnútorne konzistentný, ale nikdy nebol zapečatený. Rozpracovaná obchôdzka. **Nie je to dôkaz** | 0 |
| `UNVERIFIED_DEV` | spustili ste to s `--allow-unsigned`, takže sa podpisy nevyžadovali (bez kľúča sa neoverujú vôbec). **Nie je to dôkaz** | 0 |

Keď platí viac naraz, vyhráva ten horší: `TAMPERED` pred `PENDING` pred `UNSEALED`
pred `UNVERIFIED_DEV` pred `VERIFIED`.

**Kód 2 znamená, že sa neoverilo nič:** zlý je príkaz alebo sa súbor nedá prečítať.
Nástroj povie jedným riadkom čo, napríklad bez kľúča:

```
ERROR (config): FAIL-CLOSED: give --kms-key-id OR --public-key (a trusted key to verify signatures with), or --allow-unsigned explicitly for dev (prints UNVERIFIED_DEV, not VERIFIED)
```

alebo so súborom, ktorý sa nedá prečítať:

```
ERROR (input): cannot read the export: FileNotFoundError: [Errno 2] No such file or directory: 'walkcheck-proof.json'
```

Export, ktorý sa prečítať dá, ale je pokazený (chýbajúce pole, zlý čas), skončí chybou
Pythonu a kódom 1, ako `TAMPERED`: neopierajte sa oň.

Nástroj je fail-closed: bez `--public-key` alebo `--kms-key-id` odmietne bežať,
namiesto toho, aby podpisy ticho preskočil.

## Chvost NOT CHECKED a prepínače

Za verdiktom nástroj vymenuje každú vrstvu, ktorú v tom behu **neoveroval**. Ten chvost
je rozsah overenia, nie chyba. Niektoré vrstvy potrebujú údaje, ktoré PDF nenesie;
keď chýbajú alebo sú prázdne, nástroj tie prepínače odmietne (kód 2), namiesto toho,
aby vydal verdikt, ktorý nemal čím overiť.

| Prepínač | Čo potrebuje | Odkiaľ to vezmete |
|---|---|---|
| `--tsa-trust SÚBOR` | certifikát autority časovej pečiatky | táto stránka: `tsa-trust-freetsa.pem` |
| `--require-anchor` | export globálnej kotvy (`--anchor-export SÚBOR`) alebo prístup na čítanie do databázy (`--dsn`) | **v PDF nie je.** Od Walk-Checku alebo od prevádzkovateľa úložiska, na žiadosť |
| `--require-attestation` | globálnu kotvu ako vyššie **a** atestácie úložiska (`--attestation-path PRIEČINOK` alebo `--worm-bucket`) | **v PDF nie sú.** Od Walk-Checku alebo od prevádzkovateľa úložiska, na žiadosť |
| `--no-tsa` | nič. Preskočí časovú pečiatku; chvost potom povie `timestamp (skipped by --no-tsa)` | |
| `--allow-unsigned` | nič. Preskočí podpisy a vypíše `UNVERIFIED_DEV`, nikdy `VERIFIED` | |

## Jedno varovanie, ktoré môžete uvidieť

Podľa verzie knižnice `cryptography` môže beh vypísať:

```
UserWarning: PKCS#7 certificates could not be parsed as DER, falling back to parsing as BER.
```

Zvyšok správy sa medzi verziami líši. Niektoré verzie ho vypíšu pri každom behu, iné
nikdy. Na verdikt nemá vplyv. Tokeny
časovej pečiatky podľa RFC 3161 od skutočných autorít môžu niesť certifikáty v kódovaní
BER, čo CMS pripúšťa, takže knižnica ide svojou BER cestou; samotný podpis overuje nad
presnými bajtmi vlastný dekodér tohto nástroja. Umlčať to vieme jedným riadkom a vedome
sme to neurobili: v nástroji, ktorý kontroluje certifikáty, je schované varovanie
o certifikátoch menej hodné než vysvetlené. Keby z toho knižnica raz spravila výnimku,
nástroj token odmietne, nie prepustí.

## Overte si, že nástroj naozaj kontroluje

Zmeňte vo `walkcheck-proof.json` jednu hodnotu vnútri `payload_json`, napríklad jedno
písmeno registrácie, a spustite ho znova. Musí vypísať `TAMPERED`. Ak aj potom napíše
`VERIFIED`, nemá zmysel opierať sa o zvyšok: overenie by nekontrolovalo nič.

Nie každý znak v súbore je pod hashom. Hlavička súboru (`generated_by`, `region`,
`kms_key_id`) a `kms_key_id` pri každej položke sú len menovky a ich zmena verdikt
nemení. Nemení ho ani preformátovanie, iné poradie kľúčov či pridaný prázdny riadok:
kanonický tvar ostáva ten istý.

## Čo to dokazuje a čo nie

Dokazuje, že **zapečatený záznam sa od zapečatenia nezmenil**: tie isté pozície, tie
isté časy, to isté video, ten istý človek. Položky po pečati sú podpísané každá zvlášť
a stoja v reťazci, a oprava údajov po pečati (napríklad registrácie) vie zmeniť, čo PDF
ukazuje; PDF potom tlačí opravenú hodnotu.

**Nedokazuje** ani jedno z toho, čo nasleduje, a nikto by to nemal v spore
naťahovať ďalej:

- **nie** že je diel v poriadku. Obchôdzka je záznam o tom, čo bolo v zábere, nie
  posudok letovej spôsobilosti. Žiadny verdikt tu nehovorí nič o stroji.
- **nie** že video nebolo pred nahratím zostrihané. Pečať začína v okamihu, keď
  server záznam prijal. Čo sa dialo pred kamerou predtým, je mimo reťazca.
- **nie** že človek stál pri tom stroji. Záznam nesie pohyb telefónu, názov miesta
  tak, ako bol zadaný, a čas. Nie je to meranie toho, kde kto stál.
- **nie** že obchôdzka bola úplná. Vynechané pozície ostávajú v zázname viditeľné
  ako chýbajúce; je to údaj, nie chyba.
- **nie** že súbor nesie každú položku zapísanú po pečati. Položky po pečati, napríklad
  archivácia alebo oprava údajov, sú podpísané každá zvlášť, ale ak sa tie posledné
  vynechajú, zo súboru samotného sa to nedá zistiť: reťazec po pečať ostane neporušený
  a verdikt ostane `VERIFIED`, len s menším `records=`.

Automatické rozpoznávanie dielov, kde vôbec je, je experimentálne a do verdiktu
nevstupuje.

## Dve veci hovoríme rovno

Časová pečiatka v pilotnej prevádzke je od freetsa.org. Je to skutočná RFC 3161
autorita, ale nie kvalifikovaná podľa eIDAS.

Úložisko beží v pilotnom režime, takže záznam nie je pod nezvratným zámkom. Pred
pečaťou si záznam môže zmazať používateľ sám. Po pečati sa už len skryje a ide do
archívu, uchováva sa najmenej štyri roky a natrvalo ho môže zmazať len Walk-Check,
výnimočne. Zapečatený obsah sa nezmení nikdy. Overovač režim úložiska vypíše sám,
nenecháva to na nás (riadok `STORAGE:` vyššie).

Ani jedno neoslabuje reťazec. Ak záznam existuje, dá sa dokázať, že sa nezmenil.

---

Otázky: [office@walkcheck.eu](mailto:office@walkcheck.eu)
