MLDSA:n ja MLKEM:n käyttö Go-kielessä
Yleiskatsaus
Tausta
Jo melko pitkään kvanttitietokoneiden nopea laskentateho on tunnistettu uhaksi olemassa oleville salaustekniikoille. Nykyiset RSA:n ja ECC:n kaltaiset järjestelmät ovat vaarassa murtua kvanttitietokoneiden laskentakyvyn vuoksi. Kuitenkin useita vuosia sitten, kun käsite kvanttitietokoneista alkoi konkretisoitua, niihin liittyviä vaihtoehtoja alettiin tutkia ja kehittää, ja NIST on standardoinut PQC:tä (Post-Quantum Cryptography).
MLDSA ja MLKEM
Lopulta NIST hyväksyi elokuussa 2024 MLKEM:n ja MLDSA:n standardeiksi, jotka perustuvat CRYSTALS-Kyberiin ja CRYSTALS-Dilithiumiin. Molemmat algoritmit toimivat MLWE (Module Learning with Errors) -ongelman pohjalta. Tällaista muotoa kutsumme hilapohjaiseksi salaukseksi.
Hilapohjainen salaus on nimensä mukaisesti salausjärjestelmä, joka perustuu matemaattisen ongelman vaikeuteen hilaverkossa. Minullakaan ei ole tästä syvällistä matemaattista tietämystä, mutta yhdellä rivillä tiivistettynä se on "ongelma lineaaristen yhtälöiden ratkaisemisesta melun kanssa moduulihilassa". Vaikka en voi käsittää, kuinka vaikeaa se on, sanotaan, että tällaiset ongelmat ovat niin vaikeita, ettei edes kvanttitietokoneilla voi ratkaista niitä.
MLDSA
Käsitellään ensin MLDSA:ta.
Rakenne
MLDSA on, kuten nimestä voi päätellä, epäsymmetrinen allekirjoitusalgoritmi, ja se käy läpi seuraavat kaksi vaihetta:
- Allekirjoituksen luominen: Luodaan allekirjoitus viestille yksityisavaimella.
- Allekirjoituksen vahvistaminen: Vahvistetaan luodun allekirjoituksen kelpoisuus julkisella avaimella.
Ja MLDSA:lla on seuraavat 3 ominaisuutta:
- strong existential unforgeability: Yhden allekirjoituksen ja julkisen avaimen avulla ei voida luoda toista kelvollista allekirjoitusta.
- chosen message attack: Millään viestin allekirjoituksella ei voida luoda uutta kelvollista allekirjoitusta julkisen avaimen avulla.
- side-channel attack: Allekirjoitusprosessissa käytetään jatkuvasti uusia satunnaisarvoja ja viestistä johdettuja pseudorandom-arvoja, mikä lisää turvallisuutta.
- domain separation: Erilaisille parametreille käytetään erilaisia siemeniä, mikä estää toistuvat tietoturvaongelmat.
Koodi
Tässä yksinkertainen Go-kielinen esimerkkikoodi. Tässä esimerkissä käytettiin cloudflare/circl -kirjaston mldsa:ta.
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 // Luodaan avaimet mldsa44-spesifikaation mukaisesti.
14 pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15 if err != nil {
16 panic(err)
17 }
18
19 message := []byte("Hello, World!")
20
21 // Luodaan allekirjoitus.
22 // Yksi huomioitava asia on, että nykyisessä versiossa, 22. joulukuuta 2024,
23 // virhe tapahtuu, ellei crypto.Hash(0) ole käytössä.
24 signature, err := priv.Sign(rand.Reader, message, crypto.Hash(0))
25 if err != nil {
26 panic(err)
27 }
28
29 encodedSignature := base64.URLEncoding.EncodeToString(signature)
30 fmt.Println(len(encodedSignature), encodedSignature)
31
32 // Tarkistetaan julkisen avaimen skeemaa kutsumalla.
33 ok := pub.Scheme().Verify(pub, message, signature, nil)
34 fmt.Println(ok)
35}
13228 oaSaOA-...
2true
Allekirjoituksen arvo on liian pitkä, joten se on jätetty pois. Jos haluat nähdä koko tekstin, voit suorittaa sen playgroundissa.
Vaikka se onkin base64-koodattu, 3228 tavun pituus voi olla hieman rasittava. On hieman huolestuttavaa ajatella, että meidän on ehkä pian vaihdettava tämänkokoisia allekirjoituksia kvanttitietokoneita vastaan...
MLKEM
Rakenne
MLKEM on Key Encapsulation Mechanism. KEM on algoritmi, joka mahdollistaa jaetun avaimen luomisen kahden osapuolen välillä käyttäen julkisen avaimen salausta. MLKEM:n avaintenvaihtomekanismi etenee seuraavasti:
- Avaimen kapselointi: Lähettäjä luo salatun viestin (cipher text) ja jaetun avaimen (shared key) käyttämällä vastaanottajan julkista avainta. Tämä salattu viesti toimitetaan aluksi vastaanottajalle käytettäväksi.
- Avaimen kapseloinnin purkaminen: Vastaanottaja poimii jaetun avaimen salatusta viestistä käyttämällä omaa yksityistä avaintaan.
MLKEM:ssä on yhteensä 3 parametria. MLKEM-512, MLKEM-768 ja MLKEM-1024 ovat olemassa. Pienempi luku tarkoittaa lyhyempää avainta ja salaustekstiä, kun taas suurempi luku tarkoittaa pidempää avainta ja salaustekstiä sekä korkeampaa turvallisuustasoa.
Koodi
MLKEM on tarkoitus lisätä Go 1.24 -versioon, joten tässä käytettiin Go 1.24rc1 -versiota, joka on tällä hetkellä saatavilla.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // Luodaan vastaanottajan PrivateKey.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // MLKEM:ssä käytetään termiä EncapsulationKey eikä PublicKey.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Kloonattiin vain osoittaaksemme, että EncapsulationKeyn Bytes() ja NewEncapsulationKeyX-funktioilla
20 // avain voidaan poimia ja käyttää uudelleen.
21 // Todellisuudessa tämä prosessi tarkoittaisi, että lähettäjä muuntaa vastaanottajan EncapsulationKey-avaimen,
22 // joka oli julkisesti saatavilla tekstinä, objektiksi.
23 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
24 if err != nil {
25 panic(err)
26 }
27
28 // Encapsulate-funktiolla lähettäjä luo salatun tekstin ja jaetun avaimen.
29 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
30
31 // Kloonattiin tahallaan näyttääksemme vastaanottajan yksityisen avaimen tallentamisen ja hakemisen.
32 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
33 if err != nil {
34 panic(err)
35 }
36
37 // Vastaanottaja luo toisen jaetun avaimen purkamalla salatun tekstin yksityisavaimellaan.
38 sharedKeyReceiver, err := clonedReceiverKey.Decapsulate(cipherText)
39 if err != nil {
40 panic(err)
41 }
42
43 fmt.Println(base64.StdEncoding.EncodeToString(SenderSharedKey))
44 fmt.Println(base64.StdEncoding.EncodeToString(sharedKeyReceiver))
45}
1Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=
2Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=
Lopulta voidaan vahvistaa, että jaetut avaimet luodaan samankokoisina!
Tämä koodi on saatavilla myös playgroundissa.
Johtopäätös
Kunkin algoritmin spesifikaatiot, turvallisuustasot, yksityisen avaimen, julkisen avaimen, allekirjoituksen tai salakirjoituksen koot voidaan tiivistää seuraavasti. Kukin niistä on PQC-nimityksensä arvoinen ja ylpeilee huomattavan suurella koolla.
| Algoritmi | NIST-turvallisuustaso | Yksityisen avaimen koko | Julkisen avaimen koko | Allekirjoituksen/salakirjoituksen koko |
|---|---|---|---|---|
| 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 |
Näiden algoritmien ansiosta toivomme, että voimme käyttää riittävän turvallista internetiä myös kvanttitietokoneiden aikakaudella, mutta suuremman avain- ja allekirjoitus-/salakirjoituskokojen vuoksi emme voi välttää lisääntynyttä laskentatarvetta.
Siitä huolimatta Go-kieli on toteuttanut nämä algoritmit tehokkaasti, ja toivomme, että niitä hyödynnetään aktiivisesti turvallisuutenne varmistamiseksi asianmukaisissa paikoissa!