Az MLDSA és az MLKEM használata a Go programozási nyelvben
Áttekintés
Háttér
Már jó ideje felismerték, hogy a kvantumszámítógépek gyors számítási képességei veszélyt jelentenek a meglévő titkosítási rendszerekre. A meglévő RSA-hoz vagy ECC-hez hasonló algoritmusok a kvantumszámítógépek ilyen számítási képességei miatt dekódolhatóvá válhatnak. Azonban, ahogy a kvantumszámítógép fogalma évekkel ezelőtt elkezdett megvalósulni, alternatívák kutatása és fejlesztése is elkezdődött, és a NIST elindította a PQC (kvantumrezisztens titkosítás) szabványosítását.
MLDSA és MLKEM
Végül a NIST 2024 augusztusában szabványként fogadta el az MLKEM-et és az MLDSA-t, amelyek a CRYSTALS-Kyber és a CRYSTALS-Dilithium algoritmusokon alapulnak. Mindkét algoritmus az MLWE (Module Learning with Errors) problémán alapul. Ezt a formátumot rács alapú titkosításnak nevezzük.
A rács alapú titkosítás, ahogy a neve is mutatja, egy olyan titkosítási rendszer, amely a rácsokon lévő matematikai problémák nehézségén alapul. Nekem sincs mély matematikai tudásom erről, de egy sorban összefoglalva azt mondhatnám, hogy ez a zajos lineáris egyenletek megoldásának problémája egy moduláris rácson
. Nem tudom, mennyire nehéz, de azt mondják, hogy ez a probléma olyan nehéz, hogy még egy kvantumszámítógéppel sem lehet megoldani.
MLDSA
Először is nézzük meg az MLDSA-t.
Felépítés
Az MLDSA, ahogy a neve is mutatja, egy aszimmetrikus aláíró algoritmus, amely összesen 2 lépésből áll.
- Aláírás generálása: Privát kulcs segítségével aláírást generálunk az üzenethez
- Aláírás ellenőrzése: A publikus kulcs segítségével ellenőrizzük a generált aláírás érvényességét
Az MLDSA-nak 3 jellemzője van:
- strong existential unforgeability: Egy aláírással és publikus kulccsal nem lehet másik érvényes aláírást létrehozni.
- chosen message attack: Egyetlen üzenethez tartozó aláírással sem lehet a publikus kulcs segítségével új érvényes aláírást generálni.
- side-channel attack: Az aláírás során folyamatosan új véletlenszerű értéket és az üzenetből származtatott pszeudo-véletlenszerű értéket használnak, ami magas szintű biztonságot eredményez.
- domain separation: Biztosítva, hogy a különböző paraméterekhez különböző seed-eket használjanak, megelőzve az ismétlődő biztonsági problémákat.
Kód
Íme egy egyszerű Go nyelvi példakód.
Ebben a példában a cloudflare/circl mldsa-ját használtam.
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 // Kulcsokat generál a mldsa44 specifikációval.
14 pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15 if err != nil {
16 panic(err)
17 }
18
19 message := []byte("Hello, World!")
20
21 // Aláírás generálása.
22 // Egy dologra figyelni kell, hogy a jelenlegi, 2024. december 22-i verzióban hiba lép fel, ha nem 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 // A publikus kulcs sémájának meghívásával ellenőrizzük.
32 ok := pub.Scheme().Verify(pub, message, signature, nil)
33 fmt.Println(ok)
34}
13228 oaSaOA-...
2true
Az aláírás értéke túl hosszú volt, ezért kihagytam. Ha látni szeretné a teljeset, futtassa a playground oldalon.
Bár base64-gyel van kódolva, a 3228 bájt egy kicsit terhes lehet.
Egy kicsit terhes belegondolni, hogy hamarosan ekkora méretű aláírásokat kell majd küldenünk a kvantumszámítógépek ellenében...
MLKEM
Felépítés
Az MLKEM egy kulcsbeágyazási mechanizmus (Key Encapsulation Mechanism). A KEM egy olyan algoritmus, amely lehetővé teszi két fél számára egy közös kulcs generálását nyilvános kulcsú titkosítási módszerrel. Az MLKEM kulcscsere mechanizmusa a következő lépéseken megy keresztül:
- Kulcsbeágyazás: A küldő a fogadó nyilvános kulcsával titkosított üzenetet (cipher text) és egy közös kulcsot hoz létre. Ezt a titkosított üzenetet kezdetben elküldjük a fogadónak, hogy azt felhasználhassa.
- Kulcskibontás: A fogadó a saját privát kulcsával kibontja a közös kulcsot a titkosított üzenetből.
Az MLKEM-nek összesen 3 paramétere van. Létezik MLKEM-512, MLKEM-768 és MLKEM-1024. Minél kisebb a szám, annál kisebb a kulcs és a titkosított szöveg, minél nagyobb, annál hosszabb a kulcs és a titkosított szöveg, és annál magasabb a biztonsági szint.
Kód
Mivel az MLKEM a go 1.24-ben kerül bevezetésre, a jelenlegi időpontban a go 1.24rc1-et használtam, ami elérhető.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // A fogadó PrivateKey-jét generálja.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // Az MLKEM-ben nem PublicKey-t, hanem EncapsulationKey-t használunk.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Egyszerűen lemásoltam, hogy bemutassam, hogy az EncapsulationKey Bytes()-ával és a NewEncapsulationKeyX-szel ki lehet nyerni és újra felhasználni a kulcsot.
20 // Természetesen a valóságban ez a folyamat az a folyamat lenne, amikor a feladó objektummá alakítja a fogadó EncapsulationKey-jét, amely szövegként publikus.
21 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22 if err != nil {
23 panic(err)
24 }
25
26 // Az Encapsulate segítségével a feladó létrehozza a titkosított szöveget és a közös kulcsot.
27 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29 // Szándékosan másolatot készítettem, hogy bemutassam, hogyan kell a fogadó privát kulcsát tárolni és lekérni.
30 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31 if err != nil {
32 panic(err)
33 }
34
35 // A fogadó a privát kulcsával kibontja a titkosított szöveget a Decapsulate segítségével, így egy másik közös kulcsot hoz létre.
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=
Láthatjuk, hogy a végeredményben azonos méretű közös kulcsok jönnek létre!
Ez a kód a playground oldalon is megtekinthető.
Következtetés
Az egyes algoritmusok specifikációi, biztonsági szintjei és a privát kulcsok, publikus kulcsok, aláírások vagy titkosított szövegek méretei az alábbiak szerint foglalhatók össze. Mindegyik, a PQC nevéhez méltóan, tekintélyes méreteket mutat.
Algoritmus | NIST biztonsági szint | Privát kulcs mérete | Publikus kulcs mérete | Aláírás/Titkosított szöveg mérete |
---|---|---|---|---|
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 |
Reméljük, hogy ezekkel az algoritmusokkal egy kvantumszámítógépekkel szemben is biztonságos internetet használhatunk, de a megnövekedett kulcsok és aláírás/titkosított szöveg mérete miatt több számításra lesz szükség, ami elkerülhetetlennek tűnik.
Ennek ellenére, mivel a Go nyelvben az algoritmusok hatékonyan vannak implementálva, remélhetőleg aktívan felhasználhatók lesznek az Önök biztonságának megóvásában a megfelelő helyeken!