Procédé de signature

SLH-DSA expliqué mathématiquement

Comment SLH-DSA-SHA2-256f (FIPS 205) signe sans recourir à aucun problème difficile autre que les fonctions de hachage — des chaînes de hachage aux arbres de Merkle, jusqu'à la forêt d'arbres FORS.

Mise à jour : 2026-11-09

1. De quoi il s'agit

Chez McGesund, les clients des formules Avis Premium et Signaux Premium peuvent choisir le niveau de signature PQ-Dual-Diversified. Chaque QR code porte alors, en plus de la signature Ed25519 classique, deux signatures résistantes au quantique : ML-DSA-87 et SLH-DSA-SHA2-256f.

Cet article explique la seconde. Elle est construite différemment de tout ce que nous utilisons par ailleurs.

SLH-DSA n'est pas un chiffrement. Le contenu reste lisible publiquement. SLH-DSA prouve l'origine et l'intégrité — comme toute signature numérique.

SLH-DSA signifie en toutes lettres Stateless Hash-Based Digital Signature Algorithm. Il est issu de SPHINCS+ et a été normalisé en 2024 sous le nom FIPS 205. Le nom trahit déjà sa particularité : le procédé n'utilise que des fonctions de hachage. Pas de réseaux, pas de courbes elliptiques, pas de grands nombres premiers.


2. Qu'est-ce qui est signé exactement ?

Chez McGesund, SLH-DSA signe le même jeu de données que celui couvert par la signature Ed25519 du QR code : le payload du QR code. Il indique notamment à quelle entreprise appartient le code (fid), de quel type il est (typ), quand il a été émis (iat) et jusqu'à quand il est valable (exp) — sous la forme d'une suite d'octets encodée de manière compacte (CBOR).

Cette suite d'octets est notre message MM. Si quelqu'un modifie après coup un seul bit, plus aucune des signatures ne correspond.

La signature SLH-DSA ne se trouve pas dans le QR code lui-même. Avec environ 49 Ko, elle est bien trop volumineuse pour cela. Elle est conservée sous forme de « sceau » distinct auprès du jeu de données du QR code dans le backend de McGesund, et chargée dans le navigateur lorsque vous cliquez sur « Vérifier l'authenticité ».


3. Pourquoi un procédé fait uniquement de fonctions de hachage ?

Tout procédé de signature repose sur une tâche pratiquement insoluble sans la clé secrète :

ProcédéLa sécurité repose sur
Ed25519le logarithme discret sur une courbe elliptique
FALCONles vecteurs courts dans les réseaux NTRU
ML-DSAModule-LWE et Module-SIS (réseaux)
SLH-DSAuniquement les propriétés de la fonction de hachage

Un grand ordinateur quantique casserait Ed25519. FALCON et ML-DSA sont considérés comme résistants au quantique — mais tous deux s'appuient sur des problèmes de réseaux. Si l'on découvrait un jour une attaque fondamentale contre les réseaux, les deux seraient touchés en même temps.

SLH-DSA, en revanche, a seulement besoin que la fonction de hachage fasse ce pour quoi elle a été conçue :

Aˋ partir de H(x), on ne peut pas retrouver x, et personne ne trouve un second x′ tel que H(x′)=H(x).\text{À partir de } H(x) \text{, on ne peut pas retrouver } x \text{, et personne ne trouve un second } x' \text{ tel que } H(x') = H(x) \text{.}

C'est l'hypothèse la plus ancienne et la plus minutieusement étudiée de toute la cryptographie. Même un ordinateur quantique n'y aide que de façon limitée : l'algorithme de Grover divise par deux les bits de sécurité. C'est pourquoi la variante 256 utilise des condensés de n=32n = 32 octets =256= 256 bits — après Grover, il reste encore 128 bits.

C'est précisément pour cela que le niveau s'appelle Diversified : ML-DSA (réseaux) et SLH-DSA (hachage) appartiennent à des familles mathématiques différentes. Si l'une tombe, l'autre tient.


4. Brique 1 : signer avec des chaînes de hachage (WOTS+)

L'idée de base est étonnamment simple. Commençons par un seul chiffre.

Prenez une valeur aléatoire secrète ss et hachez-la encore et encore :

s  →  H(s)  →  H2(s)  →  …  →  H15(s)=ps \;\to\; H(s) \;\to\; H^2(s) \;\to\; \dots \;\to\; H^{15}(s) = p

La fin de la chaîne, pp, est publiée. C'est la clé publique pour ce seul chiffre.

Pour signer un chiffre dd compris entre 0 et 15, on divulgue la valeur de la chaîne à la position dd :

σ=Hd(s)\sigma = H^{d}(s)

Qui veut vérifier hache σ\sigma encore 15−d15 - d fois. S'il obtient exactement pp, la signature est authentique :

H15−d(σ)=?pH^{15-d}(\sigma) \stackrel{?}{=} p

Un mini-exemple

Nous signons le chiffre d=6d = 6 :

  • Le signataire publie σ=H6(s)\sigma = H^6(s).
  • Le vérificateur calcule H9(σ)=H15(s)H^{9}(\sigma) = H^{15}(s) et compare avec pp. Cela correspond.

Un faussaire qui a vu σ\sigma peut sans peine en déduire H7(s)H^7(s), H8(s)H^8(s), etc. — et donc signer des chiffres plus grands. Il ne peut pas en signer de plus petits : il lui faudrait pour cela hacher à rebours.

La somme de contrôle contre le « comptage vers le haut »

Pour que personne ne puisse simplement augmenter des chiffres, on signe en plus une somme de contrôle :

C=∑i(15−di)C = \sum_{i} (15 - d_i)

Si un faussaire augmente un chiffre du message, CC devient plus petit — et il lui faudrait alors calculer à rebours dans la chaîne de la somme de contrôle. Les deux directions sont ainsi verrouillées.

Extrapolé à un condensé entier

Un condensé de 32 octets compte 64 chiffres en base 16. S'y ajoutent 3 chiffres de somme de contrôle :

len=64+3=67 chaıˆnes\text{len} = 64 + 3 = 67 \text{ chaînes}

Une telle signature s'appelle WOTS+ (Winternitz One-Time Signature). Son gros défaut est dans son nom : one-time, à usage unique. Qui signe deux messages différents avec la même clé divulgue tant de valeurs de chaîne que des falsifications deviennent possibles.


5. Brique 2 : de nombreuses clés à usage unique sous un même toit (arbre de Merkle)

Si chaque clé ne peut servir qu'une seule fois, il en faut énormément — mais on ne veut publier qu'une seule clé publique.

La solution est un arbre de Merkle : les clés publiques de 2h′2^{h'} paires WOTS+ forment les feuilles. Deux voisines sont à chaque fois hachées ensemble, les résultats à nouveau par paires, jusqu'à ce qu'il ne reste en haut qu'une seule racine.

nœud=H(gauche ∥ droite)\text{nœud} = H(\text{gauche} \,\|\, \text{droite})

Dans SLH-DSA-SHA2-256f, chaque arbre a une hauteur h′=4h' = 4, soit 24=162^4 = 16 feuilles.

Pour montrer qu'une feuille donnée appartient à l'arbre, on fournit son chemin d'authentification : les 4 nœuds frères rencontrés en remontant. Grâce à eux, le vérificateur remonte en hachant de la feuille jusqu'à la racine. S'il obtient la racine connue, la feuille fait partie de l'arbre.


6. Brique 3 : des arbres sur des arbres (hyperarbre)

Seize clés à usage unique ne suffisent évidemment pas. SLH-DSA empile donc des arbres les uns sur les autres : la racine d'un arbre est signée avec une clé WOTS+ de l'arbre situé au-dessus. Cette tour s'appelle l'hyperarbre.

SLH-DSA-SHA2-256f compte d=17d = 17 couches, chacune de hauteur 4 :

h=d⋅h′=17⋅4=68h = d \cdot h' = 17 \cdot 4 = 68

Tout en bas, on dispose ainsi de

268≈2,95⋅10202^{68} \approx 2{,}95 \cdot 10^{20}

feuilles. La racine de l'arbre le plus haut s'appelle PK.root\text{PK.root} — c'est la véritable clé publique.

Point important : personne ne calcule ces arbres à l'avance. Toutes les valeurs secrètes sont dérivées à la demande d'une valeur de départ secrète SK.seed\text{SK.seed}. Seules quatre valeurs de 32 octets chacune sont stockées :

PartieContenuTaille
SK.seedvaleur de départ de toutes les valeurs de chaîne secrètes32 o
SK.prfvaleur de départ de la composante aléatoire RR32 o
PK.seedvaleur de départ publique, sépare les paires de clés les unes des autres32 o
PK.rootracine de l'arbre le plus haut32 o

La clé privée se compose des quatre (128 octets), la clé publique seulement de PK.seed et PK.root (64 octets).


7. Brique 4 : une forêt contre les coïncidences (FORS)

Voici maintenant le vrai problème : comment le signataire sait-il laquelle des 2682^{68} feuilles est encore inutilisée ?

Les procédés à base de hachage plus anciens (XMSS, LMS) tiennent pour cela un compteur. Celui-ci ne doit jamais être remis en arrière — ni par une sauvegarde, ni par un second serveur. Si cela arrive malgré tout, une clé à usage unique est utilisée deux fois, et le procédé est cassé. De tels procédés sont dits à état.

SLH-DSA est sans état : la feuille est choisie de façon pseudo-aléatoire à partir du message lui-même. Il peut alors arriver que deux messages tombent sur la même feuille. C'est pourquoi on ne trouve pas tout en bas une signature à usage unique, mais une signature à usage restreint : FORS (Forest of Random Subsets).

FORS se compose de k=35k = 35 petits arbres de Merkle comptant chacun 2a=29=5122^a = 2^9 = 512 feuilles. Les feuilles sont des valeurs aléatoires secrètes.

Pour signer, le condensé du message est découpé en 35 nombres de 9 bits chacun. Chaque nombre sélectionne exactement une feuille dans « son » arbre. Pour chaque arbre, la signature contient :

  • la valeur secrète de cette feuille
  • son chemin d'authentification (9 nœuds)

Pour un autre message, un faussaire devrait tomber, dans les 35 arbres, sur des feuilles dont les secrets ont déjà été divulgués. Même si la même instance FORS a servi quelques fois, c'est sans espoir.


8. Le déroulement complet de la signature

  1. Composante aléatoire. À partir de SK.prf\text{SK.prf}, d'un nombre aléatoire frais et du message, on obtient une valeur RR de 32 octets.
  2. Condensé du message.
    digest=Hmsg(R, PK.seed, PK.root, M)\text{digest} = H_{\text{msg}}(R,\ \text{PK.seed},\ \text{PK.root},\ M)
    Les 49 octets du digest sont répartis : 40 octets sélectionnent les 35 feuilles FORS, le reste détermine quel arbre (idx_tree) et quelle feuille (idx_leaf) tout en bas de l'hyperarbre sont utilisés.
  3. Signature FORS sur le message. Il en résulte une clé publique FORS.
  4. Signature de l'hyperarbre sur cette clé FORS : 17 signatures WOTS+, chacune avec un chemin d'authentification, couche après couche jusqu'à PK.root\text{PK.root}.

La signature finale est simplement la concaténation :

σ=R ∥ σFORS ∥ σHT\sigma = R \,\|\, \sigma_{\text{FORS}} \,\|\, \sigma_{\text{HT}}

9. Pourquoi la signature pèse 49 856 octets

Avec les paramètres de SLH-DSA-SHA2-256f, la taille se recalcule exactement (n=32n = 32 octets par condensé) :

PartieCalculOctets
RRnn32
FORSk⋅(1+a)⋅n=35⋅10⋅32k \cdot (1 + a) \cdot n = 35 \cdot 10 \cdot 3211 200
Hyperarbre(h+d⋅len)⋅n=(68+17⋅67)⋅32(h + d \cdot \text{len}) \cdot n = (68 + 17 \cdot 67) \cdot 3238 624
Total49 856

À titre de comparaison : une signature Ed25519 fait 64 octets, une signature ML-DSA-87 4 627 octets. La taille est le prix à payer pour que SLH-DSA se contente de fonctions de hachage.


10. Comment le navigateur vérifie

Avec SLH-DSA, vérifier signifie : refaire les hachages.

  1. Recalculer le digest à partir de RR, de la clé publique et du message.
  2. À partir des 35 feuilles FORS divulguées et de leurs chemins, calculer les 35 racines d'arbre, puis la clé publique FORS.
  3. Avec chacune des 17 signatures WOTS+, hacher les chaînes jusqu'au bout, former la clé publique WOTS+ et remonter par hachage le long du chemin jusqu'à la racine de l'arbre.
  4. À la fin, on doit obtenir exactement PK.root\text{PK.root}.

Cela représente quelques milliers d'appels de hachage — une broutille pour un ordinateur. Dans le navigateur, la vérification prend chez nous environ 6 à 12 millisecondes.


11. Que se passe-t-il si quelqu'un modifie le jeu de données ?

Si ne serait-ce qu'un bit du message MM change, le digest change complètement. Les 35 nombres pointent alors vers d'autres feuilles FORS, et le procédé vers un autre emplacement de l'hyperarbre.

Les secrets divulgués ne correspondent plus : les racines recalculées diffèrent, et l'on n'obtient pas PK.root\text{PK.root} à la fin. La vérification échoue.

Pour construire malgré tout une signature valide, un attaquant devrait connaître des secrets qui n'ont jamais été publiés — autrement dit, inverser une fonction de hachage.


12. Pourquoi précisément 256f ?

FIPS 205 définit douze jeux de paramètres : deux familles de hachage (SHA-2 et SHAKE), trois niveaux de sécurité (128, 192, 256) et, pour chacun, une variante rapide (« f ») et une variante compacte (« s »).

VarianteSignatureTemps de signature (chez nous)Vérification
SHA2-256s29 792 oenv. 2,6 senv. 3 ms
SHA2-256f49 856 oenv. 0,26 senv. 6 ms

256 correspond à la catégorie de sécurité NIST 5 — le même niveau que FALCON-1024 et ML-DSA-87.

Nous avons choisi « f » parce que les entreprises peuvent émettre jusqu'à 500 QR codes d'un seul coup. Avec « s », la signature prendrait plus de 20 minutes, avec « f » moins d'une minute. En contrepartie, la signature est plus volumineuse — comme elle ne se trouve pas dans le QR code, c'est acceptable.

SHA-2 plutôt que SHAKE, parce que SHA-256 et SHA-512 sont accélérés sur pratiquement tout matériel.


13. L'aléa lors de la signature

FIPS 205 autorise deux manières de produire RR :

  • déterministe : même message, même signature
  • randomisée (« hedged ») : un nombre aléatoire frais entre dans le calcul

Nous utilisons la variante randomisée. Deux signatures sur le même message ont donc un aspect différent — et toutes deux sont valides. Cela complique les attaques qui combinent des mesures sur l'appareil (consommation électrique, temps) avec des signatures répétées.


14. Ce que McGesund en fait concrètement

Le niveau. PQ-Dual-Diversified est disponible dans les formules Avis Premium et Signaux Premium. Un QR code porte alors trois signatures :

SignatureFamilleEmplacement
Ed25519courbe elliptiquedans le QR code
ML-DSA-87réseauxsceau dans le backend
SLH-DSA-SHA2-256ffonctions de hachagesceau dans le backend

La signature. Une signature SLH-DSA demande environ un quart de seconde de calcul. Pour que le serveur reste pendant ce temps disponible pour tous les autres, des threads d'arrière-plan dédiés effectuent les calculs.

La vérification. Le navigateur du lecteur charge les deux sceaux et les vérifie avec le même module WASM que toutes les autres signatures PQ. Nous fournissons les données ; le calcul se fait sur l'appareil du visiteur.

La comparaison avec le NIST. Notre module passe les vecteurs de test officiels du NIST pour SLH-DSA-SHA2-256f : à partir des valeurs de départ du NIST, il produit octet pour octet les mêmes clés, et toutes les signatures volontairement manipulées sont rejetées. Vous pouvez le vérifier vous-même dans votre navigateur sur la page de test NIST.

L'ancrage temporel. Comme pour toutes les clés, l'empreinte de la clé SLH-DSA est ancrée dans un bloc Bitcoin via OpenTimestamps.


15. SLH-DSA, ML-DSA et FALCON côte à côte

PropriétéFALCON (FN-DSA)ML-DSA-87SLH-DSA-SHA2-256f
Fondementréseaux NTRUréseaux modulairesuniquement des fonctions de hachage
NormeFIPS 206 (en cours)FIPS 204, finaliséeFIPS 205, finalisée
Clé publique1 793 o2 592 o64 o
Signature1 280 o4 627 o49 856 o
Temps de signaturemillisecondesmillisecondesenv. 0,26 s
Vérificationmillisecondesmillisecondesmillisecondes
Hypothèseproblème de réseauproblème de réseaufonction de hachage

En résumé : SLH-DSA est le plus lent et le plus volumineux des trois procédés — et le plus conservateur. Il ne repose sur rien que toute autre signature ne suppose déjà de toute façon : une fonction de hachage sûre.


16. Une phrase à retenir

SLH-DSA signe avec des chaıˆnes de hachage, les regroupe en arbres de Merkleet choisit la feuille aˋ partir du message lui-meˆme — suˆr tant quela fonction de hachage est suˆre.\boxed{ \begin{array}{c} \text{SLH-DSA signe avec des chaînes de hachage, les regroupe en arbres de Merkle}\\ \text{et choisit la feuille à partir du message lui-même — sûr tant que}\\ \text{la fonction de hachage est sûre.} \end{array}}

Qui possède la valeur de départ secrète signe en un quart de seconde. Qui ne la possède pas devrait inverser SHA-256 ou SHA-512 — même avec un ordinateur quantique.

Pour le lecteur d'un avis, cela signifie : même si toutes les méthodes fondées sur les réseaux devaient tomber un jour, cette signature resterait vérifiable. Il n'a pas besoin de nous croire. Il peut recalculer.