counted

Verificare Counted

La crittografia end-to-end vale qualcosa solo se il codice che gira nel tuo browser è quello che diciamo di servire. Questa pagina esiste perché tu non debba crederci sulla parola.

Cosa viene pubblicato

Ogni versione di Counted viene compilata una sola volta, in un ambiente congelato, e del risultato si prende l’impronta: il server, il modulo WebAssembly, il suo JavaScript e ogni file dell’app, in SHA-256. La lista è servita dalla versione stessa in esecuzione:

https://counted.fr/SHA256SUMS.txt

Ogni riga è l’impronta di un file e il suo percorso su counted.fr: assets/web_bg-….wasm, assets/web-….js, i font e i fogli di stile. La stessa lista si trova nelle note di ogni release, lontano dal server che la serve.

Verifica cosa ricevi

Da un terminale, scarica la lista e ogni file che nomina, poi confronta:

curl -sO https://counted.fr/SHA256SUMS.txt
for f in $(awk '$2 != "server" {print $2}' SHA256SUMS.txt); do
  curl -s --create-dirs -o "$f" "https://counted.fr/$f"
done
sha256sum -c --ignore-missing SHA256SUMS.txt

Ogni riga deve finire con OK. server è l’eseguibile del server; non viene servito, la sua impronta è lì per il confronto con una build.

Compilalo da solo

La build è riproducibile: immagini di base e compilatore fissati per digest, stessi percorsi, stessi flag. Il codice sorgente del client è pubblico su github.com/counted-labs/counted, un tag per versione, con licenza AGPL-3.0. Da lì, tre comandi ricompilano ciò che serve questo sito e confrontano:

git clone --branch vX.Y.Z https://github.com/counted-labs/counted.git && cd counted
docker buildx build --platform linux/amd64 -f Dockerfile.client --target sums --output type=local,dest=out .
curl -s https://counted.fr/SHA256SUMS.txt | grep -v '  server$' | diff - out/SHA256SUMS.txt

Un diff vuoto significa che ogni file che il tuo browser riceve è, byte per byte, ciò che producono quei sorgenti. La riga server è il binario nativo: il suo codice non è pubblicato, viene escluso dal confronto e non vede mai i tuoi dati in chiaro - tutto viene cifrato sul tuo dispositivo prima di essere inviato. Ogni release su GitHub contiene anche il SHA256SUMS.txt della sua versione, una copia indipendente da questo server. Facciamo la stessa ricompilazione noi stessi, senza cache, a ogni release.

Verifica la firma

Ogni SHA256SUMS.txt è firmato dalla nostra pipeline di build con Sigstore, e la firma viene scritta in un registro pubblico in sola aggiunta: la lista di una release non può essere sostituita in seguito senza che si veda. Con cosign:

base=https://github.com/counted-labs/counted/releases/download/vX.Y.Z
curl -sLO $base/SHA256SUMS.txt -sLO $base/SHA256SUMS.txt.sigstore.json
cosign verify-blob --bundle SHA256SUMS.txt.sigstore.json \
  --certificate-identity "https://gitlab.com/jbosi/counted//.gitlab-ci.yml@refs/heads/main" \
  --certificate-oidc-issuer https://gitlab.com SHA256SUMS.txt
curl -s https://counted.fr/SHA256SUMS.txt | cmp - SHA256SUMS.txt

Verified OK, poi nessun output da cmp: la lista servita da questo sito è esattamente quella firmata dalla build di quella release. La firma dimostra da dove viene la lista, non che il codice sia sicuro - a questo servono le due verifiche qui sopra.

Verificato senza di noi

Due verifiche girano sulle macchine di GitHub, che non condividono nulla con le nostre, e i loro risultati sono pubblici:

  • A ogni release, il client viene ricompilato dal suo tag e confrontato con il SHA256SUMS.txt firmato di quella release.
  • Ogni 6 ore, la lista servita da counted.fr viene confrontata con la lista firmata di una release pubblicata; ogni file elencato deve essere servito invariato, e le pagine dell’app devono caricare solo file della lista.

Le app mobili

Le app Android e iOS sono compilate dagli stessi sorgenti. Cosa si può verificare dipende dallo store:

  • F-Droid ricompila ogni versione dal repository pubblico e distribuisce il nostro APK solo se la sua build dà gli stessi byte, firma a parte.
  • Google Play firma l’app con una propria chiave, il cui certificato è qui sotto: dimostra che una copia viene da Play, ma quella copia non può ancora essere confrontata con una build.
  • L’App Store cifra ogni copia per il dispositivo che la installa: nessuno può confrontarla con una build, nemmeno noi. Identificativi: team UK936HFQ24, app fr.counted.app.

Certificato di firma dell’APK di F-Droid e di quello allegato alle release di GitHub, in SHA-256:

D8:D4:02:6F:49:6F:3C:21:54:F1:38:E8:17:10:33:2E:A5:28:8A:AA:B8:56:7A:2F:8D:0E:71:3D:E1:D5:93:E8

Per verificare l’app installata sul tuo telefono, l’app AppVerifier confronta il suo certificato con questo. Da un computer:

adb pull "$(adb shell pm path fr.counted.app | head -1 | cut -d: -f2)" counted.apk
apksigner verify --print-certs counted.apk | grep SHA-256

Certificato di firma di Google Play, in SHA-256:

4C:1E:7D:F5:2C:FB:D6:03:14:C2:0B:E4:AA:7E:9E:7D:30:19:1C:50:24:3B:F5:09:1A:21:F4:B1:30:98:A6:DC

Cosa dimostra

  • Il codice che esegue il tuo browser è il codice di cui è pubblicata l’impronta.
  • Due persone che verificano nello stesso momento ricevono lo stesso codice.
  • Una versione ricompilata dai suoi sorgenti produce gli stessi file - quindi non è stato aggiunto nulla tra il codice e ciò che viene servito.

Cosa non dimostra

  • Che il codice sia corretto: un’impronta attesta l’identità di un file, non la sua qualità.
  • Che un file diverso servito a una sola persona verrebbe scoperto: le verifiche automatiche vedono ciò che ricevono tutti, e il tuo browser non verifica nulla da solo. Se ti preoccupa, usa l’app mobile, il cui codice è fissato all’installazione.
  • Cosa fa il server: il suo codice non è pubblico. Riceve solo dati cifrati, ma come tratta i metadati non può essere verificato dall’esterno. La nostra politica di sicurezza indica, per ogni affermazione, cosa la sostiene.

Un’impronta che non corrisponde? Scrivici con la riga e la data.