Aller au contenu principal
La confiance électronique, du hachage à l'archive

La transaction de signature : lier l'accord à un document

Comment un service lie une approbation à un seul document : authentification ou identité, codes à usage unique, empreinte, rejeu et journal d'audit.

Dans cette série
Partie 5 sur 9
Auteur
Lamine Diallo
Temps de lecture
20 min de lecture
Publié le
Sur cette page

Pour qui

Ingénieurs logiciels et décideurs techniques qui ont lu les articles 1 à 4 et savent ce que sont une signature, un certificat et un HSM.

À lire avant

Vous apprendrez

  • Distinguer l'authentification de la vérification d'identité, et l'intention de s'authentifier du consentement à un document
  • Dire ce que prouve un code à usage unique, et pourquoi il ne peut pas montrer quel document a été approuvé
  • Lier une autorisation de signature à l'empreinte d'un document, au signataire, à l'identifiant de signature, à la session, à un nonce et à une échéance, et expliquer pourquoi un numéro de version ne suffit pas
  • Construire, rejouer et substituer une autorisation en Python, et voir un journal chaîné par hachage se rompre à la ligne modifiée
  • Associer chacune des principales menaces contre une transaction de signature au contrôle qui la bloque ou la détecte

Le problème : une signature valide sur le mauvais document

L’article 1 s’achevait sur une garantie étroite : une signature valide prouve qu’une clé a signé exactement ces octets. Les articles 2 à 4 ont lié la clé à un nom, l’ont gardée dans un appareil et ont permis à Ibrahima de vérifier que le certificat était toujours valable. Rien de tout cela ne lui dit ce que Mariama a accepté.

Mariama lit un contrat de prêt de 5 000 000 GNF et l’approuve. Avant que la clé ne signe, quelque chose remplace le fichier par un autre qui indique 9 000 000 GNF. La clé est la sienne, le certificat est valide, le HSM n’a jamais laissé sortir la clé, et la signature se vérifie. Tous les contrôles des articles 1 à 4 passent, sur un document qu’elle n’a jamais vu.

La faille se trouve dans la transaction de signature : les étapes entre « Mariama ouvre le document » et « la clé signe ». Cet article lie une approbation à un seul document, précise qui ce lien contraint, et journalise la transaction pour que toute altération se voie. La partie pratique construit ce lien en Python, puis l’attaque.

Figer d’abord le document

Une signature porte sur des octets : ces octets doivent donc cesser de bouger. Avant qu’un signataire ne voie un document, le service le fige et calcule son empreinte SHA-256, qui devient son identité. Une modification crée un nouveau document, avec une nouvelle empreinte, et toute approbation donnée pour l’ancien devient nulle.

Une enveloppe regroupe les documents, les signataires, l’ordre de signature et l’état. C’est un conteneur de workflow, pas un objet cryptographique ; chaque approbation qu’elle contient désigne une empreinte, pas un nom de fichier.

L’authentification n’est pas la vérification d’identité

Le SP 800-63-4 du NIST définit l’authentification (authentication) comme « le processus par lequel un demandeur prouve la possession et le contrôle d’un ou plusieurs authentificateurs liés à un compte d’abonné, pour démontrer qu’il est l’abonné associé à ce compte ». La vérification d’identité (identity proofing) désigne « les processus utilisés pour collecter, valider et vérifier des informations sur une personne afin d’établir un niveau de confiance dans l’identité qu’elle revendique » (SP 800-63-4, glossaire).

Mises côte à côte, ces définitions tracent une frontière (le contraste est de nous). L’authentification montre que la personne au clavier contrôle le même compte qu’auparavant, pas qui est cette personne dans la vie réelle. Cela relève de la vérification d’identité, que l’article 2 a confiée à l’autorité d’enregistrement et que l’article 8 traite en profondeur.

L’authentification a aussi une durée de vie. Une session (session) « commence par un événement d’authentification et se termine par un événement de fin de session » (glossaire). Au niveau AAL2, le niveau d’assurance intermédiaire du NIST, le délai d’expiration global ne devrait pas dépasser 24 heures, le délai d’inactivité ne devrait pas dépasser une heure, et au moins un authentificateur doit résister au rejeu (SP 800-63B-4, §2.2.2, §2.2.3). Quiconque détient une session active agit comme le titulaire du compte. C’est pourquoi le modèle ci-dessous inscrit la session dans l’autorisation : une autorisation émise dans une session terminée ou expirée depuis peut ainsi être refusée.

L’intention de s’authentifier n’est pas le consentement à signer

Le SP 800-63B-4 exige une intention d’authentification (authentication intent) : le processus « exige que le demandeur réponde explicitement à chaque demande d’authentification ou de réauthentification ». Saisir un code à usage unique en est l’exemple (§3.2.8). C’est l’intention de se connecter, pas le consentement au contenu d’un document (la distinction est de nous).

Le consentement vient des normes de signature à distance. L’ETSI TS 119 431-1 dispose : « Les clés de signature ne doivent pouvoir être utilisées que dans les cas pour lesquels le consentement du signataire a été obtenu » (SIG-6.3.1-09). Dans sa politique SSAS normalisée, lorsque le signataire est une personne physique et que l’authentification est directement liée à son identité, elle exige « une action explicite (pas une simple case à cocher) » pour approuver « le contenu des documents précis référencés dans les SAD », par exemple faire défiler le document jusqu’au bout avant d’accepter, ou taper « J’accepte de signer ce contrat » (SIG-6.3.1-15). Un écran de signature a donc deux rôles : montrer que le bon titulaire du compte est présent, et recueillir son approbation explicite d’octets précis.

Ce que prouve un code à usage unique

Dans les termes du NIST, un authentificateur OTP (OTP authenticator) est un jeton ou une application qui détient une graine secrète ; saisir son code prouve « la possession et le contrôle de l’authentificateur » (§3.1.4). TOTP, défini par la RFC 6238, en est la forme courante fondée sur le temps (RFC 6238). Un code envoyé par SMS ou par appel vocal est plutôt un secret hors bande (out-of-band secret), remis à un appareil hors bande, et l’authentification doit se terminer en 10 minutes (§3.1.3, §3.1.3.2). Les codes transmis par le réseau téléphonique sont une option restreinte, qui impose de surveiller les changements de carte SIM et les portages de numéro (§3.1.3.3), et l’e-mail « NE DOIT PAS être utilisé pour l’authentification hors bande » (§3.1.3.1).

Les deux familles partagent deux propriétés qui comptent pour la signature.

L’usage unique incombe au vérificateur. Les vérificateurs « DOIVENT n’accepter un OTP donné qu’une seule fois pendant sa validité » (§3.1.4.2 ; §3.1.3.2 pour les secrets hors bande). La RFC 6238 précise que le vérificateur « NE DOIT PAS accepter la seconde tentative de l’OTP après que la validation réussie a été prononcée pour le premier OTP » (§5.2). Rien dans le code lui-même n’empêche une seconde utilisation.

Ils ne résistent pas à l’hameçonnage. « La saisie manuelle ne lie pas la sortie de l’authentificateur à la session précise en cours d’authentification » (§3.2.5). Un faux site peut demander son code à Mariama et le relayer tant qu’il est encore valide.

Un code prouve donc le contrôle d’un authentificateur lié à un compte. Il ne dit rien de l’identité juridique, comme l’a montré l’article 2 dans OTP ≠ identité juridique. Et à lui seul, il ne désigne aucun document : rien, dans six chiffres, ne renvoie à un contrat (notre déduction à partir du §3.2.5). Le lien doit venir d’ailleurs.

Lier l’approbation à la transaction

Les normes de signature à distance donnent un nom à ce lien. Dans l’API du Cloud Signature Consortium (CSC), les données d’activation de signature (Signature Activation Data) (SAD) sont l’« ensemble de données utilisé pour contrôler une opération de signature donnée », et un module d’activation de signature (signature activation module) « utilise les SAD afin que les clés de signature soient utilisées sous le contrôle exclusif du signataire » (CSC API v2.0.0.2, §4.1). L’article 3 a présenté les SAD dans HSM ≠ autorisation ; voici comment on les construit. Les numéros de section CSC de cet article sont ceux de la v2.0.0.2 ; la v2.2, version actuelle, en renumérote certains.

L’appel CSC credentials/authorize transmet l’identifiant de signature, le nombre de signatures et les empreintes des documents. Ces empreintes permettent « au serveur de lier les SAD à la ou aux empreintes, empêchant ainsi qu’une autorisation serve à signer un contenu différent » ; par défaut, les SAD expirent au bout de 3 600 secondes (§11.6). La spécification CSC résume le niveau SCAL2 de la norme CEN EN 419 241-1 : les SAD sont « liées au document ou aux documents à signer » (§8.2). Le serveur invalide les SAD une fois atteint le nombre de signatures autorisé (§11.9). La TS 119 431-1 ajoute la session : lorsque l’authentification est liée à l’identité du signataire, les SAD doivent contenir l’identifiant unique de la session de signature (SIG-6.3.1-11).

Le paiement est arrivé à la même idée : les règles européennes de 2018 sur l’authentification forte exigent que le code soit « propre au montant de l’opération de paiement et au bénéficiaire », et que « toute modification du montant ou du bénéficiaire entraîne l’invalidation du code d’authentification » (règlement délégué 2018/389, art. 5, paragraphe 1). C’est la règle d’un autre secteur, citée pour comparaison : approuver ceci, et seulement ceci.

Le modèle de cet article réunit ces sources en un seul jeu de champs ; aucune norme ne le dresse tel quel, c’est notre synthèse.

Champ Ce qu’il fixe D’où vient l’idée
Empreinte du document Ces octets et aucun autre CSC hashes (§11.6)
Signataire À qui appartient l’approbation Le modèle de l’article
Identifiant de signature Quelle clé de signature peut servir CSC credentialID (§11.6)
Session La session de signature à laquelle elle appartient TS 119 431-1, SIG-6.3.1-11 (sa « session de signature ») ; SP 800-63B-4 §5.1 pour la session de connexion
Nonce Une valeur jamais utilisée, consommée au premier usage Glossaire du SP 800-63-4 ; SP 800-63B-4 §3.1.4.2
Échéance Une courte fenêtre de validité CSC expiresIn (§11.6) ; SP 800-63B-4 §3.1.3.2

Le service signe l’ensemble avec sa propre clé : aucun champ ne peut changer sans invalider l’autorisation. Un nonce (nonce) est « une valeur utilisée dans les protocoles de sécurité qui n’est jamais répétée avec la même clé », et il « n’est pas nécessairement imprévisible » (glossaire). C’est l’unicité qui compte, et le vérificateur doit garder en mémoire les nonces déjà consommés.

Le code authentifie Mariama ; l'autorisation lie son approbation à une seule empreinte. Le modèle de l'article, tiré de CSC API §11.6, TS 119 431-1 §6.3.1 et SP 800-63B-4.

Un numéro de version n’est pas un lien

« Mariama approuve la version 3 du contrat » : la formule semble précise. Elle ne l’est pas. Un numéro de version est une étiquette attribuée par le serveur. Si un élément situé entre l’approbation et la signature (une file d’attente, un stockage, un autre composant) remplace les octets derrière « v3 », une approbation qui ne nomme que « v3 » correspond toujours.

Une empreinte est calculée à partir des octets. Un document différent donne, avec une très forte probabilité, une empreinte SHA-256 différente, comme l’article 1 l’a montré avec la modification d’un seul octet. Une approbation liée à l’empreinte refuse tout substitut, y compris une version ultérieure authentique. C’est, mis en pratique, ce que la CSC appelle « empêcher qu’une autorisation serve à signer un contenu différent » ; le raisonnement est le nôtre. Gardez les numéros de version pour les personnes ; liez les approbations aux empreintes.

Qui l’autorisation contraint

Dans ce modèle, le service signe l’autorisation avec sa propre clé, et le module d’activation vérifie cette signature ainsi que les champs de l’autorisation par rapport à la demande : empreinte, signataire, identifiant de signature, session, nonce et échéance. Rien de ce qui vient de Mariama n’atteint le module. Cela arrête les attaquants extérieurs : un rejeu, un document remplacé dans le stockage, une approbation expirée. Cela ne peut pas contraindre celui qui contrôle le service : il détient la clé du serveur et peut émettre à volonté une autorisation neuve et valide sur l’empreinte du contrat à 9 000 000 GNF.

Pour contraindre celui qui exploite l’application frontale, il faut des données d’autorisation que le service ne peut pas produire seul. Dans l’API CSC, les données d’authentification du signataire et les empreintes des documents sont envoyées au service distant qui héberge le module d’activation, et celui-ci émet des SAD liées à ces empreintes ; pour SCAL2, « une autorisation à deux facteurs est nécessaire » (§8.2, §11.6). Le module utilise les SAD pour que les clés soient utilisées « sous le contrôle exclusif du signataire » (§4.1), et la TS 119 431-1 ne rend les clés utilisables qu’avec le consentement du signataire (SIG-6.3.1-09). L’application frontale ne peut pas fabriquer de SAD sans le signataire (notre lecture du §8.2).

Le journal d’audit rend un abus détectable, pas impossible : une signature sans authentification correspondante, dans un journal ancré hors de portée de l’exploitant, constitue une preuve après coup (Schneier et Kelsey, §1).

La transaction de signature comme machine à états

Vue du service, une transaction traverse quelques états gardés. C’est le modèle de l’article, pas une liste tirée d’une norme.

Une transaction, sept états. Une modification après l'envoi fait tout repartir avec une nouvelle empreinte ; une approbation n'est jamais reportée.

En signature séquentielle, la boucle « signataire suivant » suit l’ordre prévu ; en signature parallèle, chaque signataire suit le parcours de façon indépendante.

Un journal d’audit qui révèle les altérations

Le journal qui dira plus tard qui s’est authentifié, quand, et quelle empreinte a été approuvée doit résister aux modifications, y compris de la part du personnel du service.

Le journal d’audit sécurisé de Schneier et Kelsey rend les entrées écrites avant une compromission « impossibles à lire pour l’attaquant, et également impossibles à modifier ou à détruire sans que cela se voie ». Ce n’est « pas un système qui empêche toutes les manipulations possibles … c’est un système qui détecte ces manipulations après coup » (résumé, §1).

Le cœur du dispositif est une chaîne de hachage (hash chain) : la valeur de chaînage de chaque entrée est le hachage de la valeur de chaînage précédente et des données de l’entrée, en partant d’un bloc de zéros (§3.1). Modifiez une entrée, et toutes les valeurs suivantes changent. Chaque valeur est authentifiée par un MAC, sous une clé qui évolue après chaque entrée, si bien qu’« un MAC correct sur une valeur de la chaîne de hachage est, pour l’essentiel, un MAC sur toutes les entrées précédentes également » (§3.5).

Un attaquant qui prend le contrôle de la machine de journalisation « peut supprimer un bloc d’entrées (ou le fichier journal entier), mais il ne peut pas créer de nouvelles entrées » qui passent la vérification (§3.1). Une troncature n’est détectable que si quelque chose, hors du journal, a enregistré l’endroit où il s’arrêtait ; sinon, « un attaquant peut tronquer le journal sans bruit » (§4.1).

Notre déduction : une chaîne nue, sans clé, stockée en un seul endroit ne suffit pas, puisque quiconque peut réécrire le stockage peut recalculer chaque maillon à partir de la ligne modifiée. La chaîne a besoin d’un ancrage hors de portée de cette personne : une clé MAC qu’elle ne détient pas, une valeur de tête scellée ou publiée ailleurs, ou un horodatage. La chaîne de la partie pratique est volontairement sans clé, pour que vous voyiez cette limite.

Signature ou cachet ?

Ce que signifie une signature dépend de la personne dont la clé l’a produite.

Pour comparaison : le cachet dans le règlement eIDAS de l'UE

Le signataire d’une signature électronique est « une personne physique » (art. 3, point 9) ; le créateur d’un cachet électronique est « une personne morale » (art. 3, point 24), et un cachet garantit « l’origine et l’intégrité » des données auxquelles il est joint (art. 3, point 25). Un cachet qualifié bénéficie d’une « présomption d’intégrité des données et d’exactitude de l’origine » (art. 35, paragraphe 2). C’est le cadre de l’Union européenne, présenté pour comparaison, pas le droit guinéen. Source : eIDAS, version consolidée de 2024.

Notre point pédagogique : la clé d’une organisation apposée sur un document se comporte comme un cachet. Elle montre que l’organisation se porte garante d’un document inchangé, pas qu’une personne précise l’a approuvé (le point de l’article 1 sur une clé unique pour toute l’entreprise). L’approbation d’une personne exige le lien de consentement décrit plus haut. Pour l’équivalent papier, lisez le cachet de l’entreprise, et ce qui le remplace en ligne.

Scénarios de menace

Chaque ligne associe une menace à son contrôle et à la source qui énonce ce contrôle.

Menace Ce qui la bloque ou la détecte Source
Rejeu d’une autorisation ou d’un code intercepté Nonce à usage unique ; échéance courte SP 800-63B-4 §3.2.7, §3.1.4.2 ; RFC 6238 §5.2 ; CSC §11.9
Substitution de document, y compris par une nouvelle « version » Autorisation liée à l’empreinte CSC §11.6 ; TS 119 431-1 SIG-6.3.1-13, -15 ; RTS DSP2 art. 5, paragraphe 1
Usage croisé entre signataires : l’approbation de Mariama avec une autre clé Signataire et identifiant de signature dans l’autorisation signée CSC §11.6 ; TS 119 431-1 SIG-6.3.1-11
Relais de code : un faux site transmet le code de Mariama Le code ne l’arrête pas. L’attaquant ne peut approuver que des documents déjà figés et adressés à Mariama, mais l’approbation est la sienne, pas celle de Mariama (notre remarque). Le vrai contrôle, c’est une authentification résistante à l’hameçonnage SP 800-63B-4 §3.2.5
Lassitude d’authentification : demandes d’approbation répétées Exiger la transmission d’un secret ; limiter la fréquence des demandes SP 800-63B-4 §3.1.3, §3.1.3.2
Échange de carte SIM ou portage de numéro pour les codes SMS Signaux de risque ; une alternative non restreinte SP 800-63B-4 §3.1.3.3
Service malveillant ou compromis, ou usage détourné du HSM : signer sans son approbation Ce modèle ne l’arrête pas : le service détient la clé d’autorisation. Il faut des SAD issues de sa propre authentification, vérifiées par un module d’activation distinct ; un journal ancré rend l’abus détectable. Un HSM ne fait ni l’un ni l’autre TS 119 431-1 SIG-6.3.1-09 ; CSC §4.1, §8.2, §11.6 ; Schneier et Kelsey
Falsification de l’audit par un initié Chaîne de hachage et ancrage externe : détectable, pas évitable Schneier et Kelsey §1, §3.1, §4.1
Session périmée utilisée pour approuver Limites de réauthentification ; l’identifiant de session dans l’autorisation permet au vérificateur de les appliquer SP 800-63B-4 §2.2.3, §5.1

Mariama signe, Ibrahima vérifie

  1. L’entreprise d’Ibrahima fige le contrat de 5 000 000 GNF, calcule son empreinte et envoie l’enveloppe à Mariama.
  2. Mariama s’authentifie ; sa session est sess-7f3a. Elle lit jusqu’au bout et approuve explicitement.
  3. Le service construit une autorisation portant sur l’empreinte, « Mariama », cred-mariama-01, sess-7f3a, un nonce aléatoire et une échéance de deux minutes, et la signe avec sa propre clé.
  4. Côté signature, l’autorisation est vérifiée, le nonce consommé, puis la clé signe. Chaque étape est inscrite dans le journal chaîné par hachage.

Un rejeu est refusé parce que son nonce est déjà consommé ; une autorisation neuve présentée avec le contrat à 9 000 000 GNF l’est aussi, parce que l’empreinte diffère. Une ligne modifiée dans le journal rompt la chaîne. Ibrahima vérifie la signature et le certificat comme dans les articles 1 à 4, puis la chaîne jusqu’à son ancrage.

À vous : lier, rejouer, substituer, falsifier

Le script implémente le modèle en Python. Il s’exécute dans un conteneur jetable et génère toutes ses clés à la volée ; il n’écrit rien sur le disque. Ne l’utilisez jamais avec une vraie clé.

  • build_authorization sérialise les six champs de façon canonique (clés triées, sans espaces) et les signe avec une clé serveur ECDSA P-256, le schéma de l’article 1.
  • verify_authorization vérifie la signature, puis le nonce, puis l’empreinte, puis l’échéance, et n’enregistre le nonce comme consommé que si tous ces contrôles passent.
  • Cas 1 à 3 : vérification, rejeu, puis une autorisation neuve (nouvelle session) présentée avec un autre contrat.
  • Le cas 4 construit un journal de quatre lignes dont le hachage de chaque ligne couvre la précédente, en partant de 64 zéros et sans MAC, puis modifie la ligne 2.

Trois simplifications. Le script signe le signataire, l’identifiant de signature et la session, mais n’a aucune demande à laquelle les comparer : il n’exerce donc que l’empreinte, le nonce et l’échéance. Son ensemble de nonces consommés vit en mémoire ; un vrai vérificateur les stocke de façon durable et consomme chacun de façon atomique. Enfin, le même serveur émet et vérifie l’autorisation : le script ne montre donc que la protection contre les attaquants extérieurs.

Exemple pédagogique exécutable · Docker · python:3.12-alpine (Python 3.12.14) · cryptography==50.0.1

Enregistrez le script sous le nom signing_transaction.py, puis lancez, depuis le même répertoire :

docker run --rm --mount type=bind,src="$PWD/signing_transaction.py",dst=/in/signing_transaction.py,readonly -w /work python:3.12-alpine sh -c '
python3 --version &&
pip install cryptography==50.0.1 >/dev/null 2>&1 &&
python3 /in/signing_transaction.py'
import cryptography
print("cryptography", cryptography.__version__)

import hashlib
import json
import secrets
from datetime import datetime, timedelta, timezone

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature

CONTRACT = b"Contrat de pret 2026-001 : montant 5000000 GNF\n"
OTHER = b"Contrat de pret 2026-001 : montant 9000000 GNF\n"

server_key = ec.generate_private_key(ec.SECP256R1())
server_pub = server_key.public_key()

used_nonces = set()


def canonical(payload):
    return json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()


def build_authorization(document, signer, credential_id, session, ttl_seconds=120):
    payload = {
        "document_digest": hashlib.sha256(document).hexdigest(),
        "signer": signer,
        "credential_id": credential_id,
        "session": session,
        "nonce": secrets.token_hex(16),
        "expiry": (datetime.now(timezone.utc) + timedelta(seconds=ttl_seconds)).isoformat(),
    }
    signature = server_key.sign(canonical(payload), ec.ECDSA(hashes.SHA256()))
    return payload, signature


def verify_authorization(payload, signature, document):
    try:
        server_pub.verify(signature, canonical(payload), ec.ECDSA(hashes.SHA256()))
    except InvalidSignature:
        return False, "signature invalid"
    if payload["nonce"] in used_nonces:
        return False, "nonce already used (replay)"
    if payload["document_digest"] != hashlib.sha256(document).hexdigest():
        return False, "document digest mismatch"
    if datetime.fromisoformat(payload["expiry"]) < datetime.now(timezone.utc):
        return False, "authorization expired"
    used_nonces.add(payload["nonce"])
    return True, "authorization valid"


# 1. Build + verify a signing authorization bound to the contract.
auth, sig = build_authorization(CONTRACT, "Mariama", "cred-mariama-01", "sess-7f3a")
ok, reason = verify_authorization(auth, sig, CONTRACT)
print(f"[1] first use: {'OK' if ok else 'REJECTED'} ({reason})")

# 2. Replay the same authorization: nonce already consumed above.
ok, reason = verify_authorization(auth, sig, CONTRACT)
print(f"[2] replay: {'OK' if ok else 'REJECTED'} ({reason})")

# 3. Substitute the document under a fresh authorization: digest won't match.
auth2, sig2 = build_authorization(CONTRACT, "Mariama", "cred-mariama-01", "sess-9b21")
ok, reason = verify_authorization(auth2, sig2, OTHER)
print(f"[3] substituted document: {'OK' if ok else 'REJECTED'} ({reason})")


# 4. Hash-chained audit log: 4 rows, tamper row 2, verification breaks there.
def row_hash(prev_hash, seq, event, actor):
    data = f"{prev_hash}|{seq}|{event}|{actor}".encode()
    return hashlib.sha256(data).hexdigest()


events = [
    ("session_started", "Ibrahima"),
    ("otp_verified", "Mariama"),
    ("authorization_signed", "server"),
    ("document_signed", "Mariama"),
]

log = []
prev = "0" * 64
for seq, (event, actor) in enumerate(events, start=1):
    row = {"seq": seq, "event": event, "actor": actor, "prev_hash": prev}
    row["hash"] = row_hash(prev, seq, event, actor)
    log.append(row)
    prev = row["hash"]


def verify_chain(log):
    prev = "0" * 64
    for row in log:
        expected = row_hash(prev, row["seq"], row["event"], row["actor"])
        if row["prev_hash"] != prev or row["hash"] != expected:
            return False, row["seq"]
        prev = row["hash"]
    return True, None

ok, broken_at = verify_chain(log)
print(f"[4a] audit log (4 rows, untouched): {'OK' if ok else f'BROKEN at row {broken_at}'}")

log[1]["event"] = "otp_verified_TAMPERED"
ok, broken_at = verify_chain(log)
print(f"[4b] audit log (row 2 edited): {'OK' if ok else f'BROKEN at row {broken_at}'}")

Résultat attendu

Python 3.12.14
cryptography 50.0.1
[1] first use: OK (authorization valid)
[2] replay: REJECTED (nonce already used (replay))
[3] substituted document: REJECTED (document digest mismatch)
[4a] audit log (4 rows, untouched): OK
[4b] audit log (row 2 edited): BROKEN at row 2

Ces lignes sont identiques d’une exécution à l’autre ; Docker peut d’abord afficher la progression du téléchargement.

Essayez une modification : après la falsification, recalculez tous les hachages à partir de la ligne 2 et vérifiez de nouveau. La chaîne passe : c’est la limite d’une chaîne sans clé, et la raison pour laquelle un vrai journal a besoin d’un ancrage externe.

La place de SEDEYA

SEDEYA consigne chaque transaction de signature dans un journal d’audit chaîné par hachage, et scelle ce journal dans le PDF signé : l’historique voyage avec le document au lieu de vivre uniquement dans une base de données. Les enveloppes peuvent être signées de façon séquentielle ou parallèle. Les clés de signature sont conservées dans un HSM (PKCS#11) ou un KMS. Les signatures sont au format PAdES, jusqu’au niveau B-LTA, et Ibrahima peut contrôler un PDF signé sur la page de vérification publique de SEDEYA, en le déposant ou en scannant son QR code. Les prochains articles de cette série montrent où cette signature se loge dans un PDF, et combien de temps elle reste vérifiable.

Risques et idées reçues

« Les clés sont dans un HSM, donc personne ne peut en abuser. »

Le HSM protège la clé, pas la décision de s’en servir. C’est le consentement, prouvé par des données d’autorisation que le service ne peut pas forger, qui protège cette décision (TS 119 431-1, SIG-6.3.1-09 ; API CSC, §4.1).

En résumé

  • Figez d’abord le document ; son empreinte est son identité, et une modification annule les anciennes approbations.
  • L’authentification montre le contrôle des authentificateurs d’un compte ; la vérification d’identité établit qui en est le titulaire.
  • Un code à usage unique ne résiste pas à l’hameçonnage, dépend du vérificateur pour l’usage unique et ne désigne aucun document.
  • Une autorisation de signature lie l’approbation à l’empreinte, au signataire, à l’identifiant de signature, à la session, à un nonce et à une échéance. Un numéro de version en est incapable.
  • Signée par la clé du service, elle arrête les attaquants extérieurs, pas l’exploitant ; pour cela, il faut des SAD issues de l’authentification du signataire lui-même.
  • Une chaîne de hachage détecte les falsifications, n’en empêche aucune, et a besoin d’un ancrage externe.

Vérifiez votre compréhension

Vérifiez votre compréhension

Mariama a saisi un code correct, et la signature se vérifie avec son certificat. A-t-elle approuvé ce contrat précis ?

Afficher la réponse

Pas d’après ces seuls faits. Le code ne désigne aucun document, et la signature prouve seulement que sa clé a signé ces octets. Ibrahima a besoin d’une trace montrant que son approbation était liée à l’empreinte de ce document, dans un journal qui ne présente aucune falsification.

Vérifiez votre compréhension

Une autorisation désigne « contrat v3 » au lieu d'une empreinte. Que laisse-t-elle ouvert ?

Afficher la réponse

La substitution. Quiconque contrôle le stockage entre l’approbation et la signature peut placer d’autres octets derrière « v3 ». Lier l’autorisation à l’empreinte SHA-256 fait échouer ce substitut, comme l’a montré le cas 3. Cela n’arrête pas celui qui détient la clé du service, qui peut simplement émettre une nouvelle autorisation.

Vérifiez votre compréhension

Après la falsification, vous recalculez tous les hachages à partir de la ligne 2, et la chaîne se vérifie. Que manquait-il ?

Afficher la réponse

Un ancrage hors du stockage. Schneier et Kelsey utilisent une clé MAC évolutive et un tiers de confiance ; une valeur de tête scellée ou publiée, ou un horodatage, joue un rôle similaire pour toutes les entrées jusqu’à la tête ancrée.

Prochaine étape

L’autorisation a lié l’approbation de Mariama à une empreinte. L’article 6 montre où l’empreinte signée se loge dans un PDF, et pourquoi ce n’est pas toujours l’empreinte du fichier qu’elle a relu. D’ici là, parlez-nous : nous vous montrerons un PDF signé avec SEDEYA, avec son journal d’audit scellé, et comment n’importe qui peut le vérifier.

Références

  1. Digital Identity Guidelines: Authentication and Authenticator Management, SP 800-63B-4, NIST, July 2025 (§2.2.2, §2.2.3, §3.1.3, §3.1.3.1–3.1.3.3, §3.1.4, §3.1.4.2, §3.2.5, §3.2.7, §3.2.8, §5.1)
  2. Digital Identity Guidelines, SP 800-63-4, NIST, July 2025 (Glossary: authentication, identity proofing, nonce, replay attack, session)
  3. Architectures and protocols for remote signature applications (CSC API) v2.0.0.2, Cloud Signature Consortium, v2.0.0.2 (§4.1, §8.2, §11.6, §11.9)
  4. Policy and security requirements for TSPs; Part 1: TSP services operating a remote QSCD / SCDev, ETSI TS 119 431-1, ETSI, V1.3.1 (2024-12) (§6.3.1: SIG-6.3.1-09, -11, -13, -15)
  5. Commission Delegated Regulation (EU) 2018/389, regulatory technical standards for strong customer authentication (PSD2), Publications Office of the European Union, Original text, OJ L 69, 13 March 2018 (Art. 5(1))
  6. TOTP: Time-Based One-Time Password Algorithm, RFC 6238, IETF, May 2011 (§5.2)
  7. Secure Audit Logs to Support Computer Forensics, Bruce Schneier and John Kelsey, Counterpane, Counterpane version of the USENIX Security 1998 paper (Abstract, §1, §3.1, §3.5, §4.1)
  8. Regulation (EU) No 910/2014 (eIDAS), consolidated text, Publications Office of the European Union, Consolidated 18 October 2024 (Art. 3(9), 3(24), 3(25), art. 35(2))
  9. cryptography 50.0.1 changelog, Python Cryptographic Authority, 50.0.1, 25 August 2026