Een praktische blik op Daniel J. Bernstein’s ideeën over security-by-construction — van qmail tot Curve25519 — en wat ‘eenvoudige, verifieerbare crypto’ in de praktijk betekent.

Security-by-construction betekent een systeem zo bouwen dat veelvoorkomende fouten moeilijk te maken zijn — en dat de schade van onvermijdelijke fouten beperkt blijft. In plaats van te vertrouwen op een lange checklist (“denk eraan X te valideren, Y te saniteren, Z te configureren…”), ontwerp je de software zo dat het veiligste pad ook het makkelijkste pad is.
Denk eraan als kindveilige verpakkingen: het gaat er niet van uit dat iedereen altijd perfect voorzichtig is; het gaat ervan uit dat mensen moe, druk en soms onoplettend zijn. Goed ontwerp vermindert hoeveel “perfect gedrag” je van ontwikkelaars, operators en gebruikers vraagt.
Beveiligingsproblemen verstoppen zich vaak in complexiteit: te veel features, te veel opties, te veel interacties tussen componenten. Iedere extra knop kan een nieuwe faalmodus introduceren — een onverwachte manier waarop het systeem kan breken of misbruikt kan worden.
Eenvoud helpt op twee praktische manieren:
Dit gaat niet om minimalisme omwille van minimalisme. Het gaat erom het gedragsrepertoire klein genoeg te houden dat je het daadwerkelijk kunt begrijpen, testen en nadenken over wat er gebeurt als er iets misgaat.
Dit artikel gebruikt Daniel J. Bernstein’s werk als concrete voorbeelden van security-by-construction: hoe qmail faalmodi probeerde te verminderen, hoe constant-time denken onzichtbare lekken voorkomt, en hoe Curve25519/X25519 en NaCl duwen naar crypto die moeilijker verkeerd te gebruiken is.
Wat het niet doet: een volledige geschiedenis van cryptografie geven, algoritmes formeel bewijzen, of beweren dat er één “beste” bibliotheek is voor elk product. En het doet niet alsof goede primitieve alles oplossen — echte systemen falen nog steeds door sleutelbeheer, integratiefouten en operationele hiaten.
Het doel is eenvoudig: ontwerppatronen laten zien die veilige uitkomsten waarschijnlijker maken, zelfs als je geen cryptospecialist bent.
Daniel J. Bernstein (vaak “DJB”) is een wiskundige en computerwetenschapper wiens werk regelmatig opduikt in praktisch security-engineering: e-mailsystemen (qmail), cryptografische primitieve en protocollen (met name Curve25519/X25519), en bibliotheken die crypto voor de echte wereld verpakken (NaCl).
Mensen citeren DJB niet omdat hij de enige “juiste” manier van security schreef, maar omdat zijn projecten een consistent setje engineeringinstincten delen die het aantal manieren waarop dingen fout kunnen gaan verminderen.
Een terugkerend thema is kleinere, strakkere interfaces. Als een systeem minder aanmeldpunten en minder configuratiekeuzes blootlegt, is het makkelijker te reviewen, makkelijker te testen en lastiger per ongeluk verkeerd te gebruiken.
Een ander thema is expliciete aannames. Beveiligingsfouten komen vaak voort uit onuitgesproken verwachtingen — over random, timinggedrag, foutafhandeling of hoe sleutels opgeslagen worden. DJB’s teksten en implementaties maken het dreigingsmodel vaak concreet: wat beschermd is, tegen wie en onder welke condities.
Tenslotte is er een voorkeur voor veilige defaults en saai correcte oplossingen. Veel ontwerpen in deze traditie proberen scherpe randen te elimineren die tot subtiele bugs leiden: dubbelzinnige parameters, optionele modi en prestatie-snelkoppelingen die informatie lekken.
Dit artikel is geen levensverhaal of een discussie over persoonlijkheden. Het is een technische invalshoek: welke patronen je kunt observeren in qmail, constant-time denken, Curve25519/X25519 en NaCl, en hoe die patronen zich vertalen naar systemen die eenvoudiger te verifiëren zijn en minder fragiel in productie.
qmail werd gebouwd om een weinig glamoureus probleem op te lossen: e-mail betrouwbaar afleveren terwijl de mailserver als een hoogwaardig doel wordt behandeld. Mailservers staan op het internet, accepteren de hele dag vijandige input en raken gevoelige data aan (berichten, credentials, routingregels). Historisch kon één bug in een monolithische maildaemon een volledige systeemcompromittering betekenen — of stille berichtverlies dat pas opviel als het te laat was.
Een bepalend idee in qmail is het opdelen van “mailbezorging” in kleine programma’s die elk één taak doen: ontvangen, in de wachtrij zetten, lokale aflevering, externe aflevering, enz. Elk onderdeel heeft een smal interface en beperkte verantwoordelijkheden.
Die scheiding is belangrijk omdat fouten lokaal blijven:
Dit is security-by-construction in praktische vorm: ontwerp het systeem zodat “één fout” minder snel verandert in “totaal falen”.
qmail laat ook gewoonten zien die verder reiken dan e‑mail:
De les is niet “gebruik qmail”. De les is dat je vaak grote beveiligingswinst kunt behalen door rond minder faalmodi te herontwerpen — voordat je meer code schrijft of meer knoppen toevoegt.
“Attack surface” is de som van alle plekken waar je systeem gepokeerd, geprikt of misleid kan worden om het verkeerde te doen. Een handige analogie is een huis: elke deur, raam, garagedeur, reservesleutel en brievenbus is een potentiële toegangsroute. Je kunt betere sloten installeren, maar je wordt ook veiliger door minder toegangen te hebben.
Software is hetzelfde. Elke geopende poort, elk bestandsformaat dat je accepteert, elk admin-endpoint dat je blootlegt, elke configuratieknop die je toevoegt en elke pluginhook die je ondersteunt vergroot het aantal manieren waarop dingen kunnen falen.
Een “tight interface” is een API die minder doet, minder variatie accepteert en onduidelijke input weigert. Dat voelt vaak beperkend — maar het is makkelijker te beveiligen omdat er minder codepaden zijn om te auditen en minder verrassende interacties.
Overweeg twee ontwerpen:
Het tweede ontwerp beperkt wat aanvallers kunnen manipuleren. Het vermindert ook wat je team per ongeluk verkeerd kan configureren.
Opties vermenigvuldigen testen. Als je 10 toggles ondersteunt, heb je niet 10 gedragingen — je hebt combinaties. Veel beveiligingsbugs zitten in die naden: “deze flag schakelt een check uit”, “deze modus slaat validatie over”, “deze legacy-instelling omzeilt rate limits”. Strakke interfaces veranderen “kies-je-eigen-avontuur beveiliging” in één goed verlicht pad.
Gebruik dit om attack surface te vinden die ongemerkt groeit:
Als je de interface niet kunt verkleinen, maak hem dan strikt: valideer vroeg, weiger onbekende velden en plaats “power features” achter aparte, duidelijk afgebakende endpoints.
“Constant-time” gedrag betekent dat een berekening (ongeveer) evenveel tijd kost ongeacht geheime waarden zoals privésleutels, nonces of tussentijdse bits. Het doel is niet om snel te zijn; het doel is saai: als een aanvaller runtime niet kan correleren met geheimen, wordt het veel moeilijker om die geheimen door observatie te extraheren.
Timing-lekken zijn belangrijk omdat aanvallers niet altijd de wiskunde hoeven te breken. Als ze dezelfde operatie veel keer kunnen draaien (of die op gedeelde hardware kunnen observeren), kunnen kleine verschillen — microseconden, nanoseconden, zelfs cache-effecten — patronen onthullen die samen leiden tot sleutelherstel.
Zelfs “normale” code kan verschillend gedrag vertonen afhankelijk van data:
if (secret_bit) { ... } verandert de control flow en vaak de runtime.Je hoeft geen assembly te lezen om waarde te halen uit een audit:
if-verklaringen, array-indexen, lussen met geheime-terminatie en “fast path/slow path”-logica.Constant-time denken draait minder om heldendaden en meer om discipline: ontwerp code zodat geheimen de timing niet kunnen sturen.
Elliptische-curve key exchange is een manier waarop twee apparaten hetzelfde gedeelde geheim creëren terwijl ze alleen “publieke” waarden over het netwerk sturen. Elke kant genereert een privéwaarde (geheim) en een overeenkomstige publieke waarde (veilig om te sturen). Na uitwisseling combineren beide partijen hun privésleutel met de publieke waarde van de ander om bij hetzelfde gedeelde geheim uit te komen. Een afluisteraar ziet de publieke waarden maar kan praktisch onmogelijk het gedeelde geheim reconstrueren, zodat de twee partijen vervolgens encryptiesleutels kunnen afleiden en privé kunnen communiceren.
Curve25519 is de onderliggende curve; X25519 is de gestandaardiseerde, “doe precies dit” key-exchangefunctie erop gebouwd. Hun aantrekkingskracht is grotendeels security-by-construction: minder voetangels, minder parameterkeuzes en minder manieren om per ongeluk een onveilige instelling te kiezen.
Ze zijn ook snel op een breed scala aan hardware, wat belangrijk is voor servers met veel verbindingen en voor telefoons die batterij willen besparen. En het ontwerp moedigt implementaties aan die makkelijker constant-time te houden zijn (wat helpt tegen timingaanvallen), waardoor het risico dat een slimme aanvaller geheimen uit prestatiemetingen onttrekt afneemt.
X25519 geeft je key agreement: het helpt twee partijen een gedeeld geheim afleiden voor symmetrische encryptie.
Het biedt geen authenticatie op zichzelf. Als je X25519 gebruikt zonder te verifiëren met wie je praat (bijv. met certificaten, handtekeningen of een vooraf gedeelde sleutel), kun je nog steeds worden misleid om veilig met de verkeerde partij te praten. Met andere woorden: X25519 helpt afluisteren te voorkomen, maar stopt op zichzelf geen impersonatie.
NaCl (de “Networking and Cryptography library”) is gebouwd rond een eenvoudig doel: maak het moeilijk voor applicatieontwikkelaars om per ongeluk onveilige cryptografie samen te stellen. In plaats van een buffet van algoritmes, modi, paddingregels en configuratieknoppen aan te bieden, duwt NaCl je naar een klein aantal high-level operaties die al op veilige manieren aan elkaar gekoppeld zijn.
De API’s van NaCl zijn genoemd naar wat je wilt doen, niet welke primitieve je wilt samenstellen.
crypto_box ("box"): publieke-sleutel geauthenticeerde encryptie. Je geeft je privésleutel, de publieke sleutel van de ontvanger, een nonce en een bericht. Je krijgt een ciphertext die (a) het bericht verbergt en (b) aantoont dat het van iemand komt die de juiste sleutel kent.crypto_secretbox ("secretbox"): gedeelde-sleutel geauthenticeerde encryptie. Zelfde idee, maar met één gedeelde geheime sleutel.Het belangrijkste voordeel is dat je niet apart kiest "encryptiemodus" en "MAC-algoritme" en hoopt dat je ze correct combineert. NaCl’s defaults dwingen moderne, misuse-bestendige composities af (encrypt-then-authenticate), waardoor veelvoorkomende faalmodi — zoals het vergeten van integriteitschecks — minder waarschijnlijk zijn.
NaCl’s strengheid kan beperkend aanvoelen als je compatibiliteit met legacy-protocollen, gespecialiseerde formaten of door regelgeving voorgeschreven algoritmes nodig hebt. Je ruilt “ik kan elke parameter tunen” in voor “ik kan iets veiligs uitbrengen zonder cryptografie-expert te zijn.”
Voor veel producten is dat precies het punt: construeer de ontwerpvrijheid zodat er minder bugs kunnen bestaan. Als je echt aanpassing nodig hebt, kun je naar low-level primitieve afdalen — maar dan kies je er bewust voor terug te keren naar de scherpe randen.
“Secure by default” betekent dat de veiligste, meest redelijke optie is wat je krijgt als je niets doet. Als een ontwikkelaar een bibliotheek installeert, een snel voorbeeld kopieert of framework-defaults gebruikt, moet het resultaat moeilijk verkeerd te gebruiken en moeilijk per ongeluk te verzwakken zijn.
Defaults zijn belangrijk omdat de meeste echte systemen ermee draaien. Teams bewegen snel, documentatie wordt vluchtig gelezen en configuratie groeit organisch. Als de default “flexibel” is, vertaalt dat vaak naar “makkelijk verkeerd te configureren”.
Crypto-fouten worden niet altijd veroorzaakt door “slechte wiskunde”. Ze ontstaan vaak doordat een gevaarlijke instelling wordt gekozen omdat die beschikbaar, vertrouwd of gemakkelijk was.
Veelvoorkomende default-valstrikken:
Geef de voorkeur aan stacks die het veilige pad het gemakkelijkst maken: geverifieerde primitieve, conservatieve parameters en API’s die je niet vragen om fragiele beslissingen te nemen. Als een bibliotheek je dwingt te kiezen tussen tien algoritmes, vijf modi en meerdere encoderingen, word je gevraagd security-engineering te doen via configuratie.
Wanneer je kunt, kies bibliotheken en ontwerpen die:
Security-by-construction is deels weigeren om elke beslissing tot een dropdown te maken.
“Verifieerbaar” betekent in de meeste productteams niet “formeel bewezen”. Het betekent dat je snel, herhaaldelijk en met minder mogelijkheden tot misverstand vertrouwen kunt opbouwen in wat de code doet.
Een codebase wordt verifieerbaarder wanneer:
Elke tak, modus en optionele feature vermenigvuldigt wat reviewers moeten overdenken. Simpele interfaces versmallen de mogelijke toestanden, wat de reviewkwaliteit op twee manieren verbetert:
Houd het saai en herhaalbaar:
Deze combinatie vervangt geen expertreview, maar verhoogt de ondergrens: minder verrassingen, snellere detectie en code waar je echt over kunt redeneren.
Zelfs als je goed beoordeelde primitieve kiest zoals X25519 of een minimalistische API in NaCl-stijl, gaan systemen nog steeds stuk in de rommelige delen: integratie, encodering en operatie. De meeste incidenten in de praktijk zijn niet “de wiskunde zat fout”, maar “de wiskunde werd verkeerd gebruikt”.
Fouten in sleutelbeheer komen veel voor: langdurige sleutels hergebruiken waar een ephemeral sleutel verwacht wordt, sleutels in source control opslaan, of publiek en geheim door elkaar halen omdat het allebei gewoon arrays bytes zijn.
Nonce-misbruik is een veelvoorkomende boosdoener. Veel authenticated-encryption-schema’s vereisen een unieke nonce per sleutel. Dupliceer je een nonce (door counter resets, multi-process races of “random genoeg”-aannames), dan verlies je vertrouwelijkheid of integriteit.
Encodering en parsing creëren stille fouten: base64 vs hex verwarring, weggelaten voorloopnullen, inconsistent endianness of het accepteren van meerdere encoderingen die verschillend vergelijken. Deze bugs kunnen van “geverifieerde handtekening” een “geverifieerde iets anders” maken.
Foutafhandeling kan gevaarlijk zijn in beide richtingen: gedetailleerde fouten teruggeven die aanvallers helpen, of verificatiefouten negeren en toch doorgaan.
Geheimen lekken via logs, crashreports, analytics en debug-endpoints. Sleutels belanden ook in backups, VM-images en omgevingvariabelen die te breed gedeeld worden. Ondertussen kunnen dependency-updates (of het ontbreken daarvan) je op een kwetsbare implementatie achterlaten, zelfs als het ontwerp goed was.
Security-by-construction betekent software zo ontwerpen dat het veiligste pad ook het gemakkelijkste pad is. In plaats van te vertrouwen op lange checklists, beperk je het systeem zodat veelvoorkomende fouten moeilijk te maken zijn en onvermijdelijke fouten een beperkte impact hebben (een kleinere “blast radius”).
Complexiteit veroorzaakt verborgen interacties en randgevallen die moeilijk te testen en gemakkelijk verkeerd te configureren zijn.
Praktische voordelen van eenvoud zijn onder andere:
Een tight interface doet minder en accepteert minder variatie. Ze vermijdt vage inputs en vermindert optionele modi die leiden tot “security by configuration”.
Een praktische aanpak is om:
qmail splitst mailverwerking in kleine programma’s (ontvangen, queueen, afleveren, etc.) met beperkte verantwoordelijkheden. Dit vermindert faalmodi omdat:
Constant-time gedrag streeft ernaar runtime (en vaak geheugentoegangen) onafhankelijk te maken van geheime waarden. Dat is belangrijk omdat aanvallers soms geheimen kunnen afleiden door timing, cache-effecten of verschillen tussen “fast path” en “slow path” te meten over veel pogingen.
Het gaat om het voorkomen van “onzichtbare lekken”, niet alleen om het kiezen van sterke algoritmes.
Begin met te identificeren wat geheim is (privésleutels, gedeelde geheimen, MAC-sleutels, authenticatietags) en kijk dan waar geheimen de controleflow of geheugentoegang beïnvloeden.
Rode vlaggen:
if-takken op geheime dataControleer ook expliciet of je crypto-dependency constant-time gedrag claimt voor de relevante operaties.
X25519 is een gestandaardiseerde key-agreementfunctie gebouwd op Curve25519. Het is populair omdat het voetangels vermindert: minder parameters om te kiezen, goede prestaties, en een ontwerp dat constant-time implementaties bevordert.
Beschouw het als een veiligere “default lane” voor key exchange — mits je nog steeds authenticatie en sleutelbeheer goed regelt.
Nee. X25519 levert sleutelovereenstemming (een gedeeld geheim), maar bewijst op zichzelf niet met wie je praat.
Om impersonatie te voorkomen, combineer je het met authenticatie zoals:
Zonder authenticatie kun je nog steeds “veilig” praten met de verkeerde partij.
NaCl vermindert fouten door hoog-niveau operaties aan te bieden die al veilig gecomponeerd zijn, in plaats van een buffet van algoritmes en modi.
Twee veelgebruikte bouwstenen:
crypto_box: publieke-sleutel geauthenticeerde encryptie (jouw privésleutel + ontvanger publiek + nonce → ciphertext)crypto_secretbox: gedeelde-sleutel geauthenticeerde encryptieHet praktische voordeel is dat je veelvoorkomende samenstellingsfouten (zoals versleutelen zonder integriteitscontrole) voorkomt.
Goede primitieve falen nog steeds wanneer integratie en operatie slordig zijn. Veelvoorkomende valkuilen:
Mitigaties: