Utilizarea MLDSA și MLKEM în limbajul Go
Prezentare generală
Context
De mult timp, capacitatea rapidă de calcul a computerelor cuantice a fost percepută ca o amenințare la adresa schemelor de criptare existente. Aceasta deoarece algoritmi precum RSA sau ECC ar putea fi decriptați din cauza acestei capacități de calcul a computerelor cuantice. Cu toate acestea, de câțiva ani, conceptul de computer cuantic a devenit vizibil, iar alternativele au început să fie cercetate și dezvoltate, iar NIST a progresat cu standardizarea PQC (Post-Quantum Cryptography).
MLDSA și MLKEM
În cele din urmă, în august 2024, NIST a adoptat MLKEM și MLDSA, bazate pe CRYSTALS-Kyber și CRYSTALS-Dilithium, ca standarde. Ambii algoritmi funcționează pe baza problemei MLWE (Module Learning with Errors). Acest format este cunoscut sub denumirea de criptografie bazată pe rețele.
Criptografia bazată pe rețele este, așa cum sugerează și numele, un sistem de criptare bazat pe dificultatea problemelor matematice pe o rețea. Deși nu am o cunoaștere matematică aprofundată a acestui subiect, într-o singură frază, se spune că este „problema rezolvării ecuațiilor liniare cu zgomot într-o rețea modulară”. Nu este clar cât de dificilă este, dar se spune că o astfel de problemă este suficient de dificilă încât nici măcar computerele cuantice nu o pot rezolva.
MLDSA
Să investigăm mai întâi MLDSA.
Compoziție
MLDSA este, așa cum sugerează și numele, un algoritm de semnare asimetrică care implică următoarele două etape:
- Generarea semnăturii: Se generează o semnătură pentru un mesaj utilizând cheia privată.
- Verificarea semnăturii: Se verifică validitatea semnăturii generate utilizând cheia publică.
Și MLDSA are următoarele trei caracteristici:
- strong existential unforgeability: O altă semnătură validă nu poate fi generată dintr-o semnătură și o cheie publică.
- chosen message attack: Nu se poate genera o nouă semnătură validă cu o cheie publică, chiar și cu o semnătură pentru un mesaj ales.
- side-channel attack: Securitatea este ridicată prin utilizarea continuă a unor valori aleatorii noi și a unor valori pseudo-aleatorii derivate din mesaj în timpul semnării.
- domain separation: Previne problemele de securitate repetitive prin utilizarea unor seed-uri diferite pentru parametri diferiți.
Cod
Vă voi prezenta un exemplu simplu de cod Go. Acest exemplu utilizează mldsa din cloudflare/circl.
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 // Generează chei conform specificației mldsa44.
14 pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15 if err != nil {
16 panic(err)
17 }
18
19 message := []byte("Hello, World!")
20
21 // Generează o semnătură.
22 // Un aspect de reținut este că, la versiunea actuală din 22 decembrie 2024, se va produce o eroare dacă nu este crypto.Hash(0).
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 // Verifică apelând schema cheii publice.
32 ok := pub.Scheme().Verify(pub, message, signature, nil)
33 fmt.Println(ok)
34}
13228 oaSaOA-...
2true
Valoarea semnăturii a fost omisă deoarece este prea lungă. Dacă doriți să vedeți conținutul complet, rulați-l în playground.
Chiar și după codificarea în base64, obținerea a 3228 de octeți ar putea fi puțin copleșitoare. Este puțin copleșitor să ne gândim că s-ar putea să trebuiască să schimbăm semnături de această dimensiune pentru a contracara computerele cuantice în viitorul apropiat.
MLKEM
Compoziție
MLKEM este un mecanism de încapsulare a cheilor (Key Encapsulation Mechanism). KEM este un algoritm care permite generarea unei chei partajate între două părți utilizând o metodă de criptare cu cheie publică. Mecanismul de schimb de chei al MLKEM parcurge următorul proces:
- Încapsularea cheii: Expeditorul generează un mesaj criptat (cipher text) și o cheie partajată (shared key) utilizând cheia publică a destinatarului. Acest mesaj criptat este inițial transmis destinatarului pentru a fi utilizat.
- Decapsularea cheii: Destinatarul extrage cheia partajată din mesajul criptat utilizând cheia sa privată.
MLKEM are un total de 3 parametri. Există MLKEM-512, MLKEM-768 și MLKEM-1024; cu cât numărul este mai mic, cu atât cheia și textul criptat sunt mai mici, iar cu cât numărul este mai mare, cu atât cheia și textul criptat sunt mai lungi, și nivelul de securitate este mai ridicat.
Cod
MLKEM este planificat să fie adăugat în Go 1.24, așa că am folosit Go 1.24rc1, care este disponibil în prezent.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // Generează PrivateKey-ul destinatarului.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // În MLKEM, se folosește termenul EncapsulationKey, nu PublicKey.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Este clonat pentru a demonstra că cheia poate fi extrasă și reutilizată cu Bytes() și NewEncapsulationKeyX de la EncapsulationKey.
20 // Desigur, în practică, acest proces ar fi cel în care expeditorul transformă EncapsulationKey-ul destinatarului, care a fost publicat ca text, într-un obiect.
21 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22 if err != nil {
23 panic(err)
24 }
25
26 // Expeditorul generează textul criptat și cheia partajată cu Encapsulate.
27 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29 // L-am clonat intenționat pentru a arăta cum se salvează și se extrage cheia privată a destinatarului.
30 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31 if err != nil {
32 panic(err)
33 }
34
35 // Destinatarul utilizează cheia privată pentru a Decapsulate textul criptat și a genera o altă cheie partajată.
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=
În cele din urmă, putem confirma că se generează chei partajate de aceeași dimensiune!
Acest cod poate fi verificat și în playground.
Concluzie
Specificațiile, nivelurile de securitate și dimensiunile cheilor private, cheilor publice, semnăturilor sau textelor cifrate ale fiecărui algoritm pot fi rezumate după cum urmează. Fiecare se mândrește cu dimensiuni considerabile, fiind demne de numele PQC.
| Algoritm | Nivel de securitate NIST | Dimensiune cheie privată | Dimensiune cheie publică | Dimensiune semnătură/text cifrat |
|---|---|---|---|---|
| ML-DSA-44 | 2 | 2,560 | 1,312 | 2,420 |
| ML-DSA-65 | 3 | 4,032 | 1,952 | 3,309 |
| ML-DSA-87 | 5 | 4,896 | 2,592 | 4,627 |
| ML-KEM-512 | 1 | 1,632 | 800 | 768 |
| ML-KEM-768 | 3 | 2,400 | 1,184 | 1,088 |
| ML-KEM-1024 | 5 | 3,168 | 1,568 | 1,568 |
Sperăm că prin acești algoritmi vom putea utiliza un internet suficient de sigur chiar și pe computere cuantice, dar se pare că nu putem evita un număr mai mare de operațiuni din cauza dimensiunilor relativ mai mari ale cheilor și semnăturilor/textelor cifrate.
Cu toate acestea, deoarece limbajul Go implementează eficient fiecare algoritm, sperăm că va fi utilizat activ pentru a vă proteja securitatea în locurile potrivite!