Al het onderzoek

Schakelt FIPS 140-3 post-quantum-TLS uit? Het voert stilzwijgend een downgrade uit

FIPS-140-3 TLS-waarheidstabel: Go verwijdert post-quantum, OpenSSL behoudt de niet-goedgekeurde hybride.

Het korte antwoord op "schakelt FIPS post-quantum-TLS uit" is: het levert geen compliant post-quantum-TLS op, en het kan dit stilzwijgend uitschakelen. In ons eigen lab, on the wire, deden twee grote stacks het tegenovergestelde toen we ze in FIPS-modus zetten, en geen van beide bood standaard de FIPS-goedgekeurde post-quantum-groep aan. Go 1.24, onder GODEBUG=fips140=on en =only, stripte elke post-quantum-groep uit de ClientHello en onderhandelde stilzwijgend klassieke P-256, waardoor een verbinding die "slaagde" nul kwantumresistente sleuteluitwisseling bevatte. De FIPS-provider van OpenSSL 3.5.2 deed het tegenovergestelde: deze bood nog steeds X25519MLKEM768, de hybride waarvan de X25519-helft niet FIPS-goedgekeurd is. De FIPS-provider geeft die helft door via een goedkeuringsindicator in plaats van deze te blokkeren. FIPS 140-3 post-quantum-TLS eindigt dus in een van twee foutmodi: zet FIPS aan en óf de post-quantum-bescherming gaat uit (Go), óf er blijft een niet-compliante groep actief (OpenSSL). Harvest-now-decrypt-later-bescherming staat stilzwijgend verkeerd ingesteld voor precies die gereguleerde organisaties die verplicht FIPS moeten gebruiken.

De onverbloemde versie "FIPS aan" is niet "post-quantum aan". Go 1.24 in FIPS-modus verwijdert de post-quantum-hybride volledig en valt zonder foutmelding terug op klassieke P-256. OpenSSL 3.5 in FIPS-modus behoudt X25519MLKEM768, waarvan de klassieke helft niet op de FIPS-goedkeuringslijst staat. Geen van beide biedt SecP256r1MLKEM768 aan, de hybride die daadwerkelijk FIPS-legaal is, tenzij je deze handmatig configureert.

Wat we ontdekten toen we de FIPS-schakelaar omzetten

We bouwden twee clients en twee servers, één paar op Go 1.24, één paar op OpenSSL 3.5.2, en bekeken hun TLS 1.3-handshakes on the wire met FIPS-modus uit en daarna aan. TLS 1.3 verstuurt de supported_groups en key_share extensies in de clear, dus de ClientHello is de absolute bron van waarheid: je kunt exact aflezen welke sleuteluitwisselingsgroepen een client wil gebruiken en voor welke groep hij een share heeft voorbereid. Geen gissingen op basis van de documentatie van een library, gewoon de bytes die op de socket zijn geplaatst.

Met Go 1.24 in FIPS-modus viel de ClientHello terug naar uitsluitend de NIST-curves. De supported_groups-lijst was secp256r1, secp384r1, secp521r1 en niets anders: geen losse x25519, geen X25519MLKEM768, en opvallend genoeg niet eens de FIPS-goedgekeurde SecP256r1MLKEM768. De key_share was secp256r1 en de onderhandelde groep was secp256r1, klassieke elliptic-curve Diffie-Hellman zonder enig post-quantum-component. Het gedrag was identiek voor fips140=on en het strengere fips140=only. Een Go-server in FIPS-modus waaraan een post-quantum-groep werd aangeboden door een niet-FIPS peer, antwoordde met een HelloRetryRequest en dwong de handshake af naar secp256r1. In elk geval werd de handshake succesvol voltooid. Dat is het gevaarlijke gedeelte: er faalde niets, er kwam geen waarschuwing, de verbinding verloor simpelweg stilzwijgend haar kwantumresistentie.

Met OpenSSL 3.5.2 ging de FIPS-provider de andere kant op. De ClientHello in FIPS-modus begon met X25519MLKEM768 en bevatte daarvoor een key_share, gevolgd door P-256, P-384, P-521, en de finite-field-groepen. Vergeleken met de niet-FIPS-nulmeting waren de enige groepen die de FIPS-modus verwijderde de losse curves x25519 en x448; de hybride X25519MLKEM768 bleef bovenaan de lijst staan met een actieve key_share, en onderhandelde die groep met een peer die deze ondersteunde. De OpenSSL-server accepteerde X25519MLKEM768 eveneens. OpenSSL in FIPS-modus blijft dus een groep aanbieden waarvan het de klassieke helft niet zelfstandig laat gebruiken.

De waarheidstabel, on the wire

Hier is elke situatie die we hebben gemeten, met de groepen die elke stack aanbood, de onderhandelde groep, en wat dat voor jou betekent. De Go-rijen en de OpenSSL-rijen vertellen het verhaal: dezelfde intentie, "maak TLS FIPS-compliant", tegenovergesteld resultaat.

FIPS-140-3 TLS-gedrag gemeten on the wire, lab-test 2026-08-04
StackRolFIPS-modusAangeboden groepenOnderhandelde groepResultaat
Go 1.24Clientfips140=on / =onlysecp256r1, secp384r1, secp521r1secp256r1Alleen klassiek, geen post-quantum
Go 1.24Serverfips onNIST-curves; stuurt HelloRetryRequestsecp256r1Dwingt peer af naar klassiek
OpenSSL 3.5.2Clientfips onX25519MLKEM768, secp256r1/384/521, ffdhe2048/3072X25519MLKEM768Post-quantum, maar niet-goedgekeurde hybride
OpenSSL 3.5.2Serverfips onAccepteert X25519MLKEM768X25519MLKEM768Post-quantum, maar niet-goedgekeurde hybride
OpenSSL 3.5.2Beide, geforceerdfips onX25519MLKEM768 (geforceerd)X25519MLKEM768Handshake slaagt onder FIPS
OpenSSL 3.5.2Beide, geforceerdfips onSecP256r1MLKEM768 (geforceerd)SecP256r1MLKEM768Goedgekeurde hybride werkt, maar is nooit de standaard

Lees de laatste twee rijen in de context van alles wat erboven staat. Beide post-quantum-hybrides voeren een schone handshake uit met beide peers in FIPS-modus, inclusief de volledig goedgekeurde SecP256r1MLKEM768. De stacks zijn in staat om het juiste te doen. Ze kiezen het alleen niet voor je: Go verwijdert ze allemaal, OpenSSL valt terug op de niet-goedgekeurde hybride.

Hoe we dit hebben gemeten, zodat een scepticus de cijfers kan vertrouwen

De dataset is van ons eigen lab, opgebouwd op onze eigen systemen, uitsluitend geaggregeerd. We voerden alles uit in zelfgebouwde Docker-containers, op een tijdelijke cloud-VM, pratend met elkaar via loopback. Er werd geen server van derden aangeraakt, geen extern eindpunt gecontacteerd, en er werd niets over individuen verzameld. Dit is een lab, geen scan van de infrastructuur van anderen.

We hebben drie onafhankelijke controles gebruikt, zodat geen enkele tool op goed geloof vertrouwd hoeft te worden:

Waarom FIPS dit doet: X25519 is niet goedgekeurd, ML-KEM wel

Het mechanisme is een mismatch tussen wat de standaarden goedkeuren en wat het ecosysteem standaard gebruikt. Onder NIST's regels voor key-agreement in SP 800-56A is X25519 geen FIPS-goedgekeurd schema, wat verklaart waarom een strikte module het op zichzelf weigert. ML-KEM, gestandaardiseerd als FIPS 203, is goedgekeurd. De enige breed gespecificeerde hybride die op beide helften FIPS-legaal is, is SecP256r1MLKEM768: NIST P-256, een goedgekeurde curve, gekoppeld aan ML-KEM-768. De concurrerende hybride X25519MLKEM768 combineert het goedgekeurde ML-KEM-768 met de niet-goedgekeurde X25519, waardoor de helft niet op de lijst staat.

Het probleem is dat browsers, Go en OpenSSL allemaal standaard X25519MLKEM768 gebruiken, en niet de goedgekeurde SecP256r1MLKEM768, omdat X25519 snel en alomtegenwoordig is buiten de FIPS-wereld. Beide hybrides zijn gespecificeerd in dezelfde IETF-draft voor ECDHE-MLKEM sleuteluitwisseling. Een FIPS-schakelaar die X25519 verwijdert, verwijdert dus ook de post-quantum-hybride die daarop gebouwd is, tenzij de stack een uitzondering maakt voor de hybride. Dat is precies de splitsing die we gemeten hebben. Go beperkt te streng: het verwijdert elke post-quantum-groep en valt terug op klassiek, wat veilig is voor compliance maar kwantumresistentie weggooit. OpenSSL beperkt onvoldoende: het houdt de niet-goedgekeurde hybride actief en geeft deze door via een FIPS-goedkeuringsindicator in plaats van deze te blokkeren, een ontwerp dat wordt bijgehouden in openssl/openssl #27061. Geen van beide gedragingen is een bug in de traditionele zin; het zijn beide verdedigbare keuzes binnen de library die een niet-triviaal resultaat opleveren zodra je daadwerkelijk naar de bytes on the wire kijkt. Het Go-gedrag wordt besproken in golang/go #78178 en #78298, en de post-quantum-ondersteuning van OpenSSL 3.5 verscheen in haar release van april 2025.

Wat te doen als je FIPS draait en geeft om harvest-now-decrypt-later

Het hele doel van een post-quantum-hybride is het neutraliseren van een "harvest now, decrypt later"-aanvaller: iemand die jouw TLS-verkeer vandaag opslaat en het ontsleutelt zodra er een kwantumcomputer is. Een FIPS-schakelaar die die bescherming stilzwijgend verwijdert, doet het doel teniet voor juist die organisaties die het hoogste risico lopen. Als je FIPS draait, ga er dan niet van uit dat de schakelaar dit heeft geregeld.

Wat dit niet bewijst

Een resultaat zonder haar beperkingen is marketing, dus hier zijn de grenzen van dit onderzoek.

Veelgestelde vragen

Schakelt het inschakelen van FIPS-modus post-quantum-TLS uit?

Dat kan, en in ons lab gebeurde dat ook. Met Go 1.24 onder GODEBUG=fips140=on of fips140=only, liet de ClientHello elke post-quantum-groep vallen en onderhandelde de verbinding klassieke P-256, waardoor een geslaagde handshake nul kwantumresistente sleuteluitwisseling had. OpenSSL 3.5.2 deed het tegenovergestelde: de FIPS-provider bood nog steeds X25519MLKEM768 aan en onderhandelde deze, hoewel de X25519-helft niet FIPS-goedgekeurd is. Geen van beide stacks bood standaard de FIPS-goedgekeurde post-quantum-groep aan.

Is X25519 FIPS-goedgekeurd?

Nee. X25519 is geen goedgekeurd key-agreement-schema onder NIST SP 800-56A, dus een strikte FIPS-module beschouwt het als niet-goedgekeurd. We hebben dit rechtstreeks bevestigd: onder de OpenSSL 3.5.2 FIPS-provider faalde standalone X25519-sleutelgeneratie als niet-ondersteund met een non-zero exitcode, terwijl dezelfde operatie wel slaagde onder de default-provider.

Is X25519MLKEM768 FIPS-goedgekeurd?

Niet zuiver. Het is een hybride: de ML-KEM-768-helft is FIPS 203-goedgekeurd, maar de X25519-helft is niet goedgekeurd voor key agreement. De FIPS-provider van OpenSSL blokkeert de hybride niet; hij geeft hem door via een FIPS-goedkeuringsindicator en biedt en onderhandelt hem standaard nog steeds. De groep werkt onder FIPS, maar de klassieke helft staat niet op de goedgekeurde lijst.

Wat is de FIPS-goedgekeurde post-quantum-TLS-groep?

De breed gespecificeerde FIPS-legale hybride is SecP256r1MLKEM768: NIST P-256, een SP 800-56A goedgekeurde curve, gekoppeld aan ML-KEM-768 uit FIPS 203. Beide helften zijn goedgekeurd. In ons lab leidde het forceren van SecP256r1MLKEM768 met beide peers in FIPS-modus tot een succesvolle handshake, maar noch Go 1.24 noch OpenSSL 3.5.2 bood deze standaard aan.

Waarom verandert mijn Go TLS-handshake onder GODEBUG=fips140?

Omdat de FIPS-modus van Go 1.24 de aangeboden curves beperkt tot de NIST-set. Onder fips140=on of fips140=only, adverteerde de ClientHello van de client die we opvingen alleen secp256r1, secp384r1 en secp521r1, zonder x25519 en zonder post-quantum-hybride, en onderhandelde klassieke P-256. Een Go-server in FIPS-modus stuurde een HelloRetryRequest om een peer af te dwingen naar secp256r1. De handshake slaagt nog steeds, dus de downgrade is stilzwijgend tenzij je de onderhandelde groep inspecteert.

Breekt FIPS de bescherming tegen harvest-now-decrypt-later?

In de door ons geteste standaardconfiguraties wel, in de zin die er toe doet. Go in FIPS-modus verwijderde alle post-quantum-sleuteluitwisseling en viel terug op klassieke cryptografie, precies het verkeer dat een harvest-now-decrypt-later-aanvaller wil opslaan en later wil breken. OpenSSL behield de post-quantum-sleuteluitwisseling, maar via een hybride waarvan de klassieke helft niet FIPS-goedgekeurd is, wat eerder een compliance-probleem is dan een cryptografisch probleem. Hoe dan ook: het inschakelen van FIPS leverde niet standaard compliant post-quantum-bescherming op.

Gerelateerde artikelen

FIPS draaien en ervan uitgaan dat het post-quantum heeft geregeld?

Onze check van $100 leest je daadwerkelijke cryptografische houding zoals een aanvaller die vastlegt, binnen een scope waarvan je schriftelijk hebt aangetoond dat deze van jou is en geautoriseerd is, met een senior operator bij de toelichting. Wat je services daadwerkelijk onderhandelen on the wire maakt deel uit van dat aanvalsoppervlak.

Boek een check van $100