GoSuda

Gebruikservaring met MLDSA en MLKEM in de Go-taal

By snowmerak
views ...

Overzicht

Achtergrond

Al geruime tijd wordt de snelle rekenkracht van kwantumcomputers beschouwd als een bedreiging voor bestaande cryptografische systemen. Dit komt doordat conventionele methoden zoals RSA en ECC potentieel ontcijferd kunnen worden door de rekenkracht van kwantumcomputers. Echter, sinds het concept van kwantumcomputers enkele jaren geleden tastbaar werd, zijn alternatieven onderzocht en ontwikkeld, en heeft NIST gewerkt aan de standaardisatie van PQC (Post-Quantum Cryptography).

MLDSA en MLKEM

Uiteindelijk heeft NIST in augustus 2024 MLKEM en MLDSA, gebaseerd op CRYSTALS-Kyber en CRYSTALS-Dilithium, als standaarden aangenomen. Beide algoritmen functioneren op basis van het MLWE (Module Learning with Errors) probleem. Wij noemen dit type cryptografie roostergebaseerde cryptografie.

Roostergebaseerde cryptografie is, zoals de naam al aangeeft, een cryptografisch systeem dat gebaseerd is op de moeilijkheid van wiskundige problemen op een rooster. Hoewel ik geen uitgebreide wiskundige kennis hierover bezit, kan het in één zin worden samengevat als het probleem van het oplossen van lineaire vergelijkingen met ruis in een modulair rooster. Het is moeilijk in te schatten hoe moeilijk dit is, maar deze problemen zouden zelfs voor kwantumcomputers onoplosbaar zijn.

MLDSA

Laten we eerst MLDSA bespreken.

Structuur

MLDSA is, zoals de naam al aangeeft, een asymmetrisch handtekeningalgoritme dat de volgende twee stappen omvat:

  1. Handtekening genereren: Een handtekening voor een bericht wordt gegenereerd met behulp van een privésleutel.
  2. Handtekening verifiëren: De geldigheid van de gegenereerde handtekening wordt bevestigd met behulp van de publieke sleutel.

MLDSA heeft de volgende drie eigenschappen:

  1. strong existential unforgeability: Met één handtekening en een publieke sleutel kan geen andere geldige handtekening worden gegenereerd.
  2. chosen message attack: Zelfs met een handtekening voor een willekeurig bericht en de publieke sleutel kan geen nieuwe geldige handtekening worden gegenereerd.
  3. side-channel attack: De beveiliging is hoog doordat bij het ondertekenen voortdurend nieuwe willekeurige waarden en pseudo-willekeurige waarden afgeleid van het bericht worden gebruikt.
  4. domain separation: Voorkomt herhaalde beveiligingsproblemen door verschillende seeds te gebruiken voor verschillende parameters.

Code

Hier is een eenvoudig Go-taalvoorbeeld.
In dit voorbeeld is mldsa van cloudflare/circl gebruikt.

 1package main
 2
 3import (
 4	"crypto"
 5	"crypto/rand"
 6	"encoding/base64"
 7	"fmt"
 8
 9	"github.com/cloudflare/circl/sign/mldsa/mldsa44"
10)
11
12func main() {
13    // Genereer een sleutel volgens de mldsa44-specificatie.
14	pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15	if err != nil {
16		panic(err)
17	}
18
19	message := []byte("Hello, World!")
20
21    // Genereer een handtekening.
22    // Eén opmerking: met de huidige versie van 22 december 2024 treedt een fout op als crypto.Hash(0) niet wordt gebruikt.
23	signature, err := priv.Sign(rand.Reader, message, crypto.Hash(0))
24	if err != nil {
25		panic(err)
26	}
27
28	encodedSignature := base64.URLEncoding.EncodeToString(signature)
29	fmt.Println(len(encodedSignature), encodedSignature)
30
31    // Roep het schema van de publieke sleutel aan om de verificatie uit te voeren.
32	ok := pub.Scheme().Verify(pub, message, signature, nil)
33	fmt.Println(ok)
34}
13228 oaSaOA-...
2true

De handtekeningwaarde is weggelaten omdat deze te lang is. Als u de volledige tekst wilt zien, kunt u deze uitvoeren in de playground.

Zelfs als het base64-gecodeerd is, kan 3228 bytes een beetje omslachtig zijn.
Het is enigszins zorgwekkend om te bedenken dat we binnenkort deze grootte moeten uitwisselen voor handtekeningen die bestand zijn tegen kwantumcomputers.

MLKEM

Structuur

MLKEM is een Key Encapsulation Mechanism. KEM is een algoritme dat het genereren van een gedeelde sleutel tussen twee partijen mogelijk maakt met behulp van een publieke sleutel cryptografische methode. Het sleuteluitwisselingsmechanisme van MLKEM doorloopt de volgende stappen:

  1. Sleutel inkapseling: De zender gebruikt de publieke sleutel van de ontvanger om een versleuteld bericht (cipher text) en een gedeelde sleutel (shared key) te genereren. Dit versleutelde bericht wordt aanvankelijk aan de ontvanger geleverd voor gebruik.
  2. Sleutel decapsulatie: De ontvanger gebruikt zijn privésleutel om de gedeelde sleutel uit het versleutelde bericht te extraheren.

MLKEM heeft in totaal drie parameters. MLKEM-512, MLKEM-768 en MLKEM-1024 bestaan, waarbij kleinere waarden resulteren in kleinere sleutels en cipherteksten, en grotere waarden in langere sleutels en cipherteksten, met een hoger beveiligingsniveau.

Code

Omdat MLKEM naar verwachting in Go 1.24 zal worden toegevoegd, is Go 1.24rc1, dat op dit moment beschikbaar is, gebruikt.

 1package main
 2
 3import (
 4	"crypto/mlkem"
 5	"encoding/base64"
 6	"fmt"
 7)
 8
 9func main() {
10    // Genereer de PrivateKey van de ontvanger.
11	receiverKey, err := mlkem.GenerateKey1024()
12	if err != nil {
13		panic(err)
14	}
15
16    // MLKEM gebruikt de term EncapsulationKey in plaats van PublicKey.
17	receiverPubKey := receiverKey.EncapsulationKey()
18
19    // Gekloond om aan te tonen dat de sleutel kan worden geëxtraheerd en hergebruikt met Bytes() en NewEncapsulationKeyX van EncapsulationKey.
20    // In de praktijk zou dit proces worden gezien als de verzender die een object maakt van de EncapsulationKey van de ontvanger die als tekst is gepubliceerd.
21	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22	if err != nil {
23		panic(err)
24	}
25
26    // De zender genereert de ciphertekst en de gedeelde sleutel met Encapsulate.
27	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29    // Met opzet gekloond om te laten zien hoe de privésleutel van de ontvanger wordt opgeslagen en opgehaald.
30	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31	if err != nil {
32		panic(err)
33	}
34
35    // De ontvanger gebruikt de privésleutel om de ciphertekst te Decapsulate en een andere gedeelde sleutel te genereren.
36	sharedKeyReceiver, err := clonedReceiverKey.Decapsulate(cipherText)
37	if err != nil {
38		panic(err)
39	}
40
41	fmt.Println(base64.StdEncoding.EncodeToString(SenderSharedKey))
42	fmt.Println(base64.StdEncoding.EncodeToString(sharedKeyReceiver))
43}
1Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=
2Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=

Als resultaat kunnen we zien dat gedeelde sleutels van dezelfde grootte worden gegenereerd!

Deze code kan ook worden bekeken in de playground.

Conclusie

De specificaties van elk algoritme, het beveiligingsniveau en de grootte van de privésleutel, publieke sleutel, handtekening of ciphertekst kunnen als volgt worden samengevat. Elk van deze algoritmen heeft, zoals passend is voor PQC, aanzienlijke afmetingen.

AlgoritmeNIST-beveiligingsniveauGrootte privésleutelGrootte publieke sleutelGrootte handtekening/ciphertekst
ML-DSA-4422,5601,3122,420
ML-DSA-6534,0321,9523,309
ML-DSA-8754,8962,5924,627
ML-KEM-51211,632800768
ML-KEM-76832,4001,1841,088
ML-KEM-102453,1681,5681,568

Wij verwachten dat deze algoritmen ons in staat zullen stellen om een voldoende veilig internet te gebruiken, zelfs op kwantumcomputers, maar het lijkt onvermijdelijk dat de relatief grotere sleutel- en handtekening-/ciphertekstgroottes zullen leiden tot meer computationele inspanning.

Desondanks zijn de Go-taalimplementaties van elk algoritme effectief, en wij hopen dat zij actief zullen worden gebruikt om uw beveiliging op de juiste plaatsen te waarborgen!