GoSuda

Az MLDSA és az MLKEM használata a Go programozási nyelvben

By snowmerak
views ...

Á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.

  1. Aláírás generálása: Privát kulcs segítségével aláírást generálunk az üzenethez
  2. 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:

  1. strong existential unforgeability: Egy aláírással és publikus kulccsal nem lehet másik érvényes aláírást létrehozni.
  2. 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.
  3. 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.
  4. 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:

  1. 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.
  2. 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.

AlgoritmusNIST biztonsági szintPrivát kulcs méretePublikus kulcs méreteAláírás/Titkosított szöveg mérete
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

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!