Aller au contenu

Vérifier une preuve hors ligne

Un paquet de preuves est un fichier ZIP exporté depuis le tableau de bord. Il contient les enregistrements de consentement, les fichiers nécessaires pour les contrôler et des scripts qui exécutent les contrôles. Rien sur cette page ne nécessite de connexion réseau ni de serveur HashEntry.

Cette page indique ce que chaque contrôle établit. Aucun contrôle ne rend à lui seul un enregistrement « infalsifiable » ; ensemble, ils permettent à un tiers de détecter des modifications et de rattacher un enregistrement à un moment dans le temps.


FichierContenu
consent_data.jsonLes enregistrements exportés. Chaque enregistrement comporte un bloc proof avec le payload haché.
manifest.json, metadata.jsonIdentifiant du paquet, version de la spécification, nombre d’enregistrements, package_hash.
checksums.sha256SHA-256 de chaque fichier du paquet.
timestamps.tsv + *.tsrJetons d’horodatage RFC 3161 (tsa_consent_<id>.tsr, tsa_epoch_<id>.tsr, package.tsr) et ce que chacun couvre.
merkle_proofs.tsvPreuves d’inclusion Merkle (pour les enregistrements horodatés par lots).
verify.sh, verify.ps1, README_*.txt, proof_verification.txtScripts de vérification et instructions.

Les paquets existent en deux types : non signé (sommes de contrôle uniquement) et signé (le hash du paquet porte en plus un horodatage RFC 3161, package.tsr).

Fenêtre de terminal
# Linux / macOS
chmod +x verify.sh && ./verify.sh
Fenêtre de terminal
# Windows
powershell -ExecutionPolicy Bypass -File .\verify.ps1

Les scripts contrôlent les sommes de contrôle des fichiers, le manifeste, les horodatages et les preuves Merkle. Ils ne recalculent pas le hash de preuve, car cela nécessite la clé secrète du responsable du traitement (voir ci-dessous).


Fenêtre de terminal
sha256sum -c checksums.sha256 # macOS : shasum -a 256 -c checksums.sha256
sha256sum consent_data.json # doit être égal à "package_hash" dans manifest.json

Cela montre que les fichiers sont bien ceux listés dans le paquet. À lui seul, cela ne montre pas qui a créé le paquet ni quand — n’importe qui peut recalculer les sommes de contrôle après avoir modifié un fichier. C’est à cela que sert l’horodatage d’un paquet signé.

Chaque enregistrement possède un proof_hash. Pour les enregistrements actuels (hash_version: 2) :

proof_hash = HMAC-SHA256( key = clé secrète du tenant, message = canonical_json(payload) ) → hex en minuscules

Le payload comporte toujours ces onze clés (une valeur manquante vaut null) :

v (= 2), site_id, document_hash, consent_type, decision, categories, timestamp,
pii_digest, signer_digest, custom_document_hash, signature_hash
  • timestamp est en UTC avec une précision à la seconde, YYYY-MM-DDTHH:MM:SS+00:00, et est égal à l’heure de création de l’enregistrement.
  • Les données personnelles n’entrent pas directement dans le hash. Elles y entrent par l’intermédiaire de condensats salés : pii_digest = SHA-256( pii_salt || canonical_json({ip_address, user_agent, visitor_id}) ), et signer_digest = SHA-256( pii_salt || canonical_json(champs de preuve du signataire) ) (ou null lorsqu’il n’y a ni signature ni déclaration d’identité). Lorsqu’un enregistrement est anonymisé, les champs personnels et le sel sont supprimés mais les condensats demeurent ; le hash de preuve peut donc toujours être recalculé, tandis que les condensats ne peuvent plus être reliés à une adresse IP ou à une adresse e-mail.

JSON canonique (HashEntry Canonical JSON v2 — ce n’est pas la RFC 8785) :

  1. UTF-8, aucun espace blanc entre les tokens.
  2. Clés d’objet triées selon l’ordre des octets UTF-8, à chaque niveau.
  3. L’ordre des tableaux est conservé.
  4. Un objet vide et un tableau vide s’écrivent tous deux [].
  5. Dans les chaînes, seuls ", \ et les caractères de contrôle U+0000–U+001F sont échappés ; / et les caractères non ASCII ne le sont pas.
  6. Les nombres sont uniquement des entiers.
  7. true, false, null en minuscules.

Qui peut le contrôler. Le hash de preuve est un hash à clé. Le recalculer nécessite la clé secrète du tenant ; il peut donc être contrôlé par le responsable du traitement (qui détient la clé) ou par un expert auquel le responsable du traitement remet la clé. Sans la clé, un tiers ne peut pas le recalculer — et n’en a pas besoin : les contrôles d’horodatage et Merkle ci-dessous portent sur la valeur proof_hash elle-même et montrent que cette valeur existait à l’heure indiquée.

Fenêtre de terminal
# Avec la clé : recalculer le hash de preuve d'un enregistrement (payload canonique dans payload.json, sans saut de ligne final)
openssl dgst -sha256 -hmac "$TENANT_SECRET_KEY" payload.json

Les anciens enregistrements (hash_version: 1) utilisent SHA-256( json(payload avec les clés de premier niveau triées) || key ) et contiennent des données personnelles directement dans le payload.

timestamps.tsv comporte une ligne par jeton :

<file.tsr> <kind> <stamped data> <provider> <level at issuance>

La donnée horodatée (<stamped data>) est une chaîne hexadécimale en minuscules : le proof_hash d’un enregistrement, une racine Merkle ou le package_hash. L’autorité d’horodatage (TSA) a signé le SHA-256 des caractères ASCII de cette chaîne, sans saut de ligne final :

Fenêtre de terminal
printf '%s' "<stamped data>" > data.txt
openssl ts -verify -data data.txt -in <file.tsr> -CAfile <tsa_root.pem>
  • Le certificat racine de la TSA ne se trouve pas dans le paquet. Obtenez-le auprès de l’autorité d’horodatage elle-même ; une racine fournie par la partie dont vous contrôlez les données ne prouverait rien.
  • Les jetons de certaines autorités échouent au contrôle ESSCertID de openssl ts -verify. Vérifiez alors la signature et la chaîne en deux étapes :
Fenêtre de terminal
openssl ts -reply -in <file.tsr> -token_out -out token.der
openssl cms -verify -inform DER -in token.der -CAfile <tsa_root.pem> \
-purpose timestampsign -binary -out tstinfo.der
openssl ts -reply -in <file.tsr> -text # comparez "Message data" avec le SHA-256 de la donnée horodatée

Une ligne dont le niveau est simulated est un horodatage de test : elle n’a pas de fichier .tsr et aucune valeur probante.

Un jeton valide montre que la valeur horodatée existait au plus tard à l’heure figurant dans le jeton, selon cette autorité d’horodatage. Le fait que les horodatages d’une autorité donnée bénéficient d’une présomption légale (par exemple les horodatages qualifiés au titre d’eIDAS, ou les horodatages d’un prestataire autorisé en droit turc) dépend de l’autorité et de la juridiction.

Lorsque les enregistrements sont horodatés par lots, un seul jeton couvre la racine Merkle d’un lot et chaque enregistrement reçoit une preuve d’inclusion. merkle_proofs.tsv :

<consent_id> <leaf> <index> <leaf_count> <root> <epoch.tsr> <path>

L’arbre est le schéma Merkle binaire SHA-256 propre à HashEntry. Ce n’est pas l’arbre de la RFC 6962 (Certificate Transparency), et les outils RFC 6962 ne le vérifieront pas :

  • feuille = le proof_hash de l’enregistrement (64 caractères hexadécimaux en minuscules), sans hachage supplémentaire ;
  • nœud = SHA-256 sur les 128 caractères ASCII hex(left) || hex(right) — les chaînes hexadécimales, pas les octets bruts ;
  • une couche comportant un nombre impair de nœuds duplique son dernier nœud ;
  • il n’y a pas de préfixe de séparation de domaine feuille/nœud.

Pour vérifier un chemin, commencez avec cur = leaf et appliquez chaque étape :

L:<h> → cur = sha256(<h> || cur)
R:<h> → cur = sha256(cur || <h>)
Fenêtre de terminal
# une étape, p. ex. "R:<h>"
cur=$(printf '%s%s' "$cur" "$h" | sha256sum | cut -d' ' -f1)

Le résultat doit être égal à <root>. Vérifiez également que 0 <= index < leaf_count et que le nombre d’étapes est égal à la profondeur de l’arbre pour leaf_count — la règle de duplication ne lie pas la taille de l’arbre à la racine ; ces valeurs font donc partie de la preuve. Enfin, vérifiez <epoch.tsr> par rapport à <root> comme à la section 3 : c’est ce qui prouve à quel moment la racine, et donc le hash de preuve de l’enregistrement, existait.


ContrôleÉtablit
Sommes de contrôleLes fichiers du paquet sont cohérents entre eux.
Hash de preuve (avec la clé)Le contenu de l’enregistrement correspond au hash calculé lors de son écriture.
Jeton RFC 3161La valeur horodatée existait au plus tard à l’heure du jeton, selon cette autorité.
Preuve Merkle + jeton d’epochLe hash de preuve de l’enregistrement faisait partie d’un lot dont la racine existait à ce moment-là.

Ce qu’ils n’établissent pas : qui était la personne (voir identity_assurance dans Approbation de documents et signature), ni que l’enregistrement a été créé correctement à l’origine. Les enregistrements sans jeton d’horodatage reposent uniquement sur le hash de preuve, et donc sur le secret de la clé.