counted

Counted controleren

End-to-end-versleuteling is alleen iets waard als de code in je browser de code is die wij zeggen te leveren. Deze pagina bestaat zodat je ons niet op ons woord hoeft te geloven.

Wat er gepubliceerd wordt

Elke versie van Counted wordt één keer gebouwd, in een bevroren omgeving, en van het resultaat wordt een vingerafdruk gemaakt: de server, de WebAssembly-module, het JavaScript ervan en elk bestand van de app, als SHA-256. De lijst wordt geleverd door de versie die op dat moment draait:

https://counted.fr/SHA256SUMS.txt

Elke regel is de vingerafdruk van een bestand en het pad ervan op counted.fr: assets/web_bg-….wasm, assets/web-….js, de lettertypen en stylesheets. Dezelfde lijst staat in de notities van elke release, los van de server die hem levert.

Controleer wat je ontvangt

Download vanuit een terminal de lijst en elk bestand dat erin staat, en vergelijk:

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

Elke regel moet eindigen op OK. server is het uitvoerbare bestand van de server; het wordt niet geleverd, de vingerafdruk staat er om te vergelijken met een build.

Bouw het zelf

De build is reproduceerbaar: basisimages en compiler vastgezet op digest, dezelfde paden, dezelfde vlaggen. De broncode van de client is openbaar op github.com/counted-labs/counted, één tag per versie, onder de AGPL-3.0-licentie. Daarmee bouwen drie commando’s opnieuw wat deze site levert en vergelijken ze:

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

Een lege diff betekent dat elk bestand dat je browser ontvangt byte voor byte is wat die bronnen opleveren. De regel server is het native binaire bestand: de code ervan is niet gepubliceerd, het wordt overgeslagen bij de vergelijking, en het ziet je gegevens nooit onversleuteld - alles wordt op je apparaat versleuteld voordat het verstuurd wordt. Elke GitHub-release bevat ook de SHA256SUMS.txt van die versie, een kopie die los staat van deze server. We voeren dezelfde rebuild zelf uit, zonder cache, voor elke release.

Controleer de handtekening

Elke SHA256SUMS.txt wordt door onze build-pipeline ondertekend met Sigstore, en de handtekening wordt in een openbaar log geschreven waaraan alleen kan worden toegevoegd: de lijst van een release kan achteraf niet worden vervangen zonder dat het opvalt. Met 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, en daarna geen uitvoer van cmp: de lijst die deze site levert is precies die welke de build van die release heeft ondertekend. De handtekening bewijst waar de lijst vandaan komt, niet dat de code veilig is - daarvoor dienen de twee controles hierboven.

Gecontroleerd zonder ons

Twee controles draaien op de machines van GitHub, die niets met de onze delen, en hun resultaten zijn openbaar:

  • Bij elke release wordt de client opnieuw gebouwd vanaf zijn tag en vergeleken met de ondertekende SHA256SUMS.txt van die release.
  • Elke 6 uur wordt de lijst die counted.fr levert vergeleken met de ondertekende lijst van een gepubliceerde release; elk vermeld bestand moet ongewijzigd geleverd worden, en de pagina’s van de app mogen alleen bestanden van de lijst laden.

De mobiele apps

De Android- en iOS-apps worden uit dezelfde bronnen gebouwd. Wat je kunt controleren hangt af van de store:

  • F-Droid bouwt elke versie opnieuw vanuit de openbare repository en verspreidt onze APK alleen als zijn eigen build dezelfde bytes oplevert, de handtekening buiten beschouwing gelaten.
  • Google Play ondertekent de app met een eigen sleutel, waarvan het certificaat hieronder staat: het bewijst dat een kopie van Play komt, maar die kopie kan nog niet met een build worden vergeleken.
  • De App Store versleutelt elke kopie voor het apparaat dat hem installeert: niemand kan hem met een build vergelijken, wij ook niet. Identificatoren: team UK936HFQ24, app fr.counted.app.

Ondertekeningscertificaat van de F-Droid-APK en van de APK bij de GitHub-releases, als 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

Om de app op je telefoon te controleren, vergelijkt de app AppVerifier het certificaat ervan met dit certificaat. Vanaf een 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

Ondertekeningscertificaat van Google Play, als 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

Wat dit bewijst

  • De code die je browser uitvoert is de code waarvan de vingerafdruk gepubliceerd is.
  • Twee mensen die tegelijk controleren, ontvangen dezelfde code.
  • Een versie die opnieuw uit de bronnen wordt gebouwd, levert dezelfde bestanden op - er is dus niets toegevoegd tussen de code en wat er geleverd wordt.

Wat dit niet bewijst

  • Dat de code correct is: een vingerafdruk bevestigt de identiteit van een bestand, niet de kwaliteit ervan.
  • Dat een ander bestand dat aan één persoon geleverd wordt, zou worden opgemerkt: de automatische controles zien wat iedereen ontvangt, en je browser controleert zelf niets. Maak je je daar zorgen over, gebruik dan de mobiele app, waarvan de code bij de installatie vastligt.
  • Wat de server doet: de code ervan is niet openbaar. Hij ontvangt alleen versleutelde gegevens, maar hoe hij met metadata omgaat, kan van buitenaf niet worden gecontroleerd. Ons beveiligingsbeleid zegt per bewering waar die op steunt.

Een vingerafdruk die niet klopt? Schrijf ons met de regel en de datum.