Handtekeningmethode

SLH-DSA wiskundig uitgelegd

Hoe SLH-DSA-SHA2-256f (FIPS 205) ondertekent zonder enig rekenraadsel behalve hashfuncties — van hashketens via Merkle-bomen tot een woud van FORS-bomen.

Stand: 2026-11-09

1. Waar het hier over gaat

Bij McGesund kunnen klanten met het tarief Beoordelingen Premium en Signalen Premium het handtekeningniveau PQ-Dual-Diversified kiezen. Dan draagt elke QR-code naast de klassieke Ed25519-handtekening twee quantumresistente handtekeningen: ML-DSA-87 en SLH-DSA-SHA2-256f.

Deze bijdrage legt de tweede uit. Die is anders opgebouwd dan alles wat wij verder gebruiken.

SLH-DSA is geen versleuteling. De inhoud blijft openbaar leesbaar. SLH-DSA bewijst herkomst en integriteit — zoals elke digitale handtekening.

SLH-DSA staat voluit voor Stateless Hash-Based Digital Signature Algorithm. Het is ontwikkeld uit SPHINCS+ en in 2024 als FIPS 205 gestandaardiseerd. De naam verraadt al wat er bijzonder aan is: de methode gebruikt uitsluitend hashfuncties. Geen roosters, geen elliptische krommen, geen grote priemgetallen.


2. Wat wordt er precies ondertekend?

Bij McGesund ondertekent SLH-DSA dezelfde gegevens die ook de Ed25519-handtekening in de QR-code dekt: de payload van de QR-code. Die bevat onder meer bij welk bedrijf de code hoort (fid), van welk soort hij is (typ), wanneer hij is uitgegeven (iat) en tot wanneer hij geldig is (exp) — als compact gecodeerde bytereeks (CBOR).

Deze bytereeks is ons bericht MM. Wijzigt iemand achteraf één enkele bit, dan klopt geen van de handtekeningen meer.

De SLH-DSA-handtekening zit niet in de QR-code zelf. Met zo'n 49 KB is ze daarvoor veel te groot. Ze ligt als afzonderlijk „stempel” bij het QR-record in de McGesund-backend en wordt in de browser geladen wanneer u op „Authenticiteit controleren” klikt.


3. Waarom een methode die alleen uit hashfuncties bestaat?

Elke handtekeningmethode berust op een opgave die zonder de geheime sleutel praktisch onoplosbaar is:

MethodeVeiligheid berust op
Ed25519discrete logaritme op een elliptische kromme
FALCONkorte vectoren in NTRU-roosters
ML-DSAModule-LWE en Module-SIS (roosters)
SLH-DSAuitsluitend eigenschappen van de hashfunctie

Een grote quantumcomputer zou Ed25519 breken. FALCON en ML-DSA gelden als quantumresistent — maar beide steunen op roosterproblemen. Mocht ooit een fundamentele aanval op roosters worden gevonden, dan zouden ze allebei tegelijk getroffen zijn.

SLH-DSA heeft daarentegen alleen nodig dat de hashfunctie doet waarvoor ze gebouwd is:

Uit H(x) is x niet terug te rekenen, en een tweede x′ met H(x′)=H(x) vindt niemand.\text{Uit } H(x) \text{ is } x \text{ niet terug te rekenen, en een tweede } x' \text{ met } H(x') = H(x) \text{ vindt niemand.}

Dat is de langst en grondigst onderzochte aanname van de hele cryptografie. Ook een quantumcomputer helpt hier maar beperkt: het algoritme van Grover halveert het aantal veiligheidsbits. Daarom gebruikt de variant 256 hashwaarden van n=32n = 32 byte =256= 256 bit — na Grover blijven er nog altijd 128 bit over.

Precies daarom heet het niveau Diversified: ML-DSA (roosters) en SLH-DSA (hashes) komen uit verschillende wiskundige families. Valt de ene, dan draagt de andere.


4. Bouwsteen 1: ondertekenen met hashketens (WOTS+)

Het basisidee is verrassend eenvoudig. We beginnen met één enkel cijfer.

Neem een geheime willekeurige waarde ss en hash die steeds opnieuw:

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

Het einde van de keten, pp, wordt gepubliceerd. Dat is de openbare sleutel voor dit ene cijfer.

Om een cijfer dd tussen 0 en 15 te ondertekenen, geeft u de ketenwaarde op positie dd vrij:

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

Wie wil controleren, hasht σ\sigma nog 15−d15 - d keer verder. Komt er precies pp uit, dan is de handtekening echt:

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

Een minivoorbeeld

We ondertekenen het cijfer d=6d = 6:

  • De ondertekenaar publiceert σ=H6(s)\sigma = H^6(s).
  • De controleur berekent H9(σ)=H15(s)H^{9}(\sigma) = H^{15}(s) en vergelijkt met pp. Klopt.

Een vervalser die σ\sigma heeft gezien, kan daaruit moeiteloos H7(s)H^7(s), H8(s)H^8(s) enzovoort berekenen — en dus grotere cijfers ondertekenen. Kleinere kan hij niet, want daarvoor zou hij achteruit moeten hashen.

De controlesom tegen „ophogen”

Opdat niemand cijfers eenvoudigweg verhoogt, wordt daarnaast een controlesom ondertekend:

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

Verhoogt een vervalser een cijfer van het bericht, dan wordt CC kleiner — en daarvoor zou hij in de controlesomketen achteruit moeten rekenen. Daarmee zijn beide richtingen geblokkeerd.

Opgeschaald naar een hashwaarde

Een hashwaarde van 32 byte heeft 64 cijfers in grondtal 16. Daar komen 3 cijfers controlesom bij:

len=64+3=67 ketens\text{len} = 64 + 3 = 67 \text{ ketens}

Zo'n handtekening heet WOTS+ (Winternitz One-Time Signature). Het grote nadeel zit in de naam: one-time, eenmalig. Wie met dezelfde sleutel twee verschillende berichten ondertekent, verraadt zoveel ketenwaarden dat vervalsingen mogelijk worden.


5. Bouwsteen 2: veel eenmalige sleutels onder één dak (Merkle-boom)

Als elke sleutel maar één keer gebruikt mag worden, heeft u er heel veel nodig — maar u wilt slechts één openbare sleutel publiceren.

De oplossing is een Merkle-boom: de openbare sleutels van 2h′2^{h'} WOTS+-paren vormen de bladeren. Telkens twee buren worden samen gehasht, de resultaten weer paarsgewijs, tot er bovenaan één enkele wortel staat.

knoop=H(links ∥ rechts)\text{knoop} = H(\text{links} \,\|\, \text{rechts})

Bij SLH-DSA-SHA2-256f heeft elke boom de hoogte h′=4h' = 4, dus 24=162^4 = 16 bladeren.

Om aan te tonen dat een bepaald blad bij de boom hoort, levert u het authenticatiepad: de 4 zusterknopen op de weg naar boven. Daarmee hasht de controleur zich van het blad omhoog tot aan de wortel. Komt de bekende wortel eruit, dan hoort het blad erbij.


6. Bouwsteen 3: bomen op bomen (hypertree)

Zestien eenmalige sleutels zijn natuurlijk niet genoeg. Daarom stapelt SLH-DSA bomen op elkaar: de wortel van een boom wordt ondertekend met een WOTS+-sleutel van de boom daarboven. Deze toren heet de hypertree.

Bij SLH-DSA-SHA2-256f zijn er d=17d = 17 lagen, elk met hoogte 4:

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

Helemaal onderaan staan daarmee

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

bladeren ter beschikking. De wortel van de bovenste boom heet PK.root\text{PK.root} — dat is de eigenlijke openbare sleutel.

Belangrijk: niemand berekent deze bomen vooraf. Alle geheime waarden worden naar behoefte afgeleid uit een geheime startwaarde SK.seed\text{SK.seed}. Opgeslagen worden slechts vier waarden van elk 32 byte:

DeelInhoudGrootte
SK.seedstartwaarde voor alle geheime ketenwaarden32 B
SK.prfstartwaarde voor de willekeurige component RR32 B
PK.seedopenbare startwaarde, scheidt sleutelparen van elkaar32 B
PK.rootwortel van de bovenste boom32 B

De private sleutel bestaat uit alle vier (128 byte), de openbare alleen uit PK.seed en PK.root (64 byte).


7. Bouwsteen 4: een woud tegen toevalstreffers (FORS)

Nu het eigenlijke probleem: hoe weet de ondertekenaar welk van de 2682^{68} bladeren nog ongebruikt is?

Oudere hashgebaseerde methoden (XMSS, LMS) houden daarvoor een teller bij. Die mag nooit worden teruggezet — niet door een back-up, niet door een tweede server. Gebeurt dat toch, dan wordt een eenmalige sleutel twee keer gebruikt en is de methode gebroken. Zulke methoden heten stateful (toestandsafhankelijk).

SLH-DSA is stateless (toestandsloos): het blad wordt pseudowillekeurig uit het bericht zelf gekozen. Dan kan het voorkomen dat twee berichten hetzelfde blad treffen. Daarom hangt helemaal onderaan geen eenmalige handtekening, maar een handtekening voor enkele keren: FORS (Forest of Random Subsets).

FORS bestaat uit k=35k = 35 kleine Merkle-bomen met elk 2a=29=5122^a = 2^9 = 512 bladeren. De bladeren zijn geheime willekeurige waarden.

Bij het ondertekenen wordt de berichthash opgedeeld in 35 getallen van elk 9 bit. Elk getal kiest in „zijn” boom precies één blad. De handtekening bevat voor elke boom:

  • de geheime waarde van dat blad
  • het authenticatiepad ervan (9 knopen)

Een vervalser zou voor een ander bericht in alle 35 bomen bladeren moeten treffen waarvan de geheimen al zijn verraden. Zelfs als dezelfde FORS-instantie een paar keer is gebruikt, is dat kansloos.


8. Het hele ondertekeningsproces

  1. Willekeurige component. Uit SK.prf\text{SK.prf}, een vers willekeurig getal en het bericht ontstaat een waarde RR van 32 byte.
  2. Berichthash.
    digest=Hmsg(R, PK.seed, PK.root, M)\text{digest} = H_{\text{msg}}(R,\ \text{PK.seed},\ \text{PK.root},\ M)
    De 49 byte van de digest worden opgedeeld: 40 byte kiezen de 35 FORS-bladeren, de rest bepaalt welke boom (idx_tree) en welk blad (idx_leaf) helemaal onderaan in de hypertree wordt gebruikt.
  3. FORS-handtekening over het bericht. Daaruit volgt een openbare FORS-sleutel.
  4. Hypertree-handtekening over deze FORS-sleutel: 17 WOTS+-handtekeningen met elk een authenticatiepad, laag voor laag tot aan PK.root\text{PK.root}.

De voltooide handtekening is eenvoudigweg de aaneenschakeling:

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

9. Waarom de handtekening 49.856 byte groot is

Met de parameters van SLH-DSA-SHA2-256f laat de grootte zich exact narekenen (n=32n = 32 byte per hashwaarde):

DeelBerekeningByte
RRnn32
FORSk⋅(1+a)⋅n=35⋅10⋅32k \cdot (1 + a) \cdot n = 35 \cdot 10 \cdot 3211.200
Hypertree(h+d⋅len)⋅n=(68+17⋅67)⋅32(h + d \cdot \text{len}) \cdot n = (68 + 17 \cdot 67) \cdot 3238.624
Totaal49.856

Ter vergelijking: een Ed25519-handtekening is 64 byte, ML-DSA-87 is 4.627 byte. De grootte is de prijs ervoor dat SLH-DSA met niets anders dan hashfuncties toekomt.


10. Hoe de browser controleert

Controleren betekent bij SLH-DSA: opnieuw hashen.

  1. De digest opnieuw berekenen uit RR, de openbare sleutel en het bericht.
  2. Uit de 35 vrijgegeven FORS-bladeren en hun paden de 35 boomwortels berekenen en daaruit de openbare FORS-sleutel.
  3. Met elk van de 17 WOTS+-handtekeningen de ketens tot het einde hashen, de openbare WOTS+-sleutel vormen en via het pad omhoog hashen naar de boomwortel.
  4. Aan het eind moet precies PK.root\text{PK.root} eruit komen.

Dat zijn enkele duizenden hash-aanroepen — voor een computer een kleinigheid. In de browser duurt de controle bij ons zo'n 6 tot 12 milliseconden.


11. Wat gebeurt er als iemand de gegevens wijzigt?

Verandert er ook maar één bit van het bericht MM, dan verandert de digest volledig. Daarmee wijzen de 35 getallen naar andere FORS-bladeren en de methode naar een andere plek in de hypertree.

De vrijgegeven geheimen passen niet meer: de nagerekende wortels wijken af, en aan het eind komt er niet PK.root\text{PK.root} uit. De controle mislukt.

Om toch een geldige handtekening te maken, zou een aanvaller geheimen moeten kennen die nooit zijn gepubliceerd — dus een hashfunctie achteruit moeten rekenen.


12. Waarom juist 256f?

FIPS 205 definieert twaalf parametersets: twee hashfamilies (SHA-2 en SHAKE), drie veiligheidsniveaus (128, 192, 256) en telkens een snelle („f”) en een kleine („s”) variant.

VariantHandtekeningOndertekenen (bij ons)Controleren
SHA2-256s29.792 Bca. 2,6 sca. 3 ms
SHA2-256f49.856 Bca. 0,26 sca. 6 ms

256 betekent NIST-beveiligingscategorie 5 — hetzelfde niveau als FALCON-1024 en ML-DSA-87.

Voor „f” hebben wij gekozen omdat bedrijven tot 500 QR-codes in één keer kunnen uitgeven. Met „s” zou het ondertekenen meer dan 20 minuten duren, met „f” minder dan een minuut. Daar staat een grotere handtekening tegenover — omdat die niet in de QR-code zit, is dat te overzien.

SHA-2 in plaats van SHAKE, omdat SHA-256 en SHA-512 op vrijwel alle hardware versneld worden uitgevoerd.


13. Toeval bij het ondertekenen

FIPS 205 staat twee manieren toe om RR te genereren:

  • deterministisch: zelfde bericht, zelfde handtekening
  • gerandomiseerd („hedged”): er wordt een vers willekeurig getal meegenomen

Wij gebruiken de gerandomiseerde variant. Twee handtekeningen over hetzelfde bericht zien er daardoor verschillend uit — beide zijn geldig. Dat bemoeilijkt aanvallen die metingen aan het apparaat (stroomverbruik, tijd) combineren met herhaalde handtekeningen.


14. Wat McGesund er concreet mee doet

Het niveau. PQ-Dual-Diversified is beschikbaar in Beoordelingen Premium en Signalen Premium. Een QR-code draagt dan drie handtekeningen:

HandtekeningFamiliePlaats
Ed25519elliptische krommein de QR-code
ML-DSA-87roostersstempel in de backend
SLH-DSA-SHA2-256fhashfunctiesstempel in de backend

Het ondertekenen. Een SLH-DSA-handtekening kost zo'n kwart seconde rekentijd. Opdat de server intussen voor alle anderen bereikbaar blijft, rekenen aparte achtergrondthreads.

De controle. De browser van de lezer laadt beide stempels en controleert ze met dezelfde WASM-module als alle andere PQ-handtekeningen. Wij leveren de gegevens; gerekend wordt er op het apparaat van de bezoeker.

De afstemming met NIST. Onze module doorstaat de officiële NIST-testvectoren voor SLH-DSA-SHA2-256f: uit de startwaarden van NIST ontstaan byte voor byte dezelfde sleutels, en alle opzettelijk gemanipuleerde handtekeningen worden afgewezen. Dat kunt u op de NIST-testpagina zelf in de browser nagaan.

Het tijdanker. Zoals bij alle sleutels wordt de vingerafdruk van de SLH-DSA-sleutel via OpenTimestamps in een Bitcoin-blok verankerd.


15. SLH-DSA, ML-DSA en FALCON naast elkaar

EigenschapFALCON (FN-DSA)ML-DSA-87SLH-DSA-SHA2-256f
GrondslagNTRU-roostersmodule-roostersuitsluitend hashfuncties
StandaardFIPS 206 (in ontwikkeling)FIPS 204, definitiefFIPS 205, definitief
Openbare sleutel1.793 B2.592 B64 B
Handtekening1.280 B4.627 B49.856 B
Ondertekenenmillisecondenmillisecondenca. 0,26 s
Controlerenmillisecondenmillisecondenmilliseconden
Aannameroosterprobleemroosterprobleemhashfunctie

Kort gezegd: SLH-DSA is de traagste en grootste van de drie methoden — en de meest behoudende. Het steunt op niets wat elke andere handtekening niet toch al veronderstelt: een veilige hashfunctie.


16. Eén zin om te onthouden

SLH-DSA ondertekent met hashketens, bundelt die in Merkle-bomenen kiest het blad uit het bericht zelf — veilig zolangde hashfunctie veilig is.\boxed{ \begin{array}{c} \text{SLH-DSA ondertekent met hashketens, bundelt die in Merkle-bomen}\\ \text{en kiest het blad uit het bericht zelf — veilig zolang}\\ \text{de hashfunctie veilig is.} \end{array}}

Wie de geheime startwaarde bezit, ondertekent in een kwart seconde. Wie hem niet bezit, zou SHA-256 of SHA-512 achteruit moeten rekenen — ook met een quantumcomputer.

Voor de lezer van een beoordeling betekent dat: zelfs als ooit alle roostermethoden zouden vallen, blijft deze handtekening controleerbaar. Hij hoeft ons niet te geloven. Hij kan narekenen.