Go Dilinde MLDSA ve MLKEM Kullanımı Üzerine Bir Deneyim

Genel Bakış
Arka Plan
Kuantum bilgisayarların hızlı hesaplama yetenekleri, uzun süredir mevcut kriptografik sistemler için bir tehdit olarak görülmektedir. Mevcut RSA ve ECC gibi algoritmalar, kuantum bilgisayarların bu hesaplama gücü sayesinde çözülebilme olasılığına sahiptir. Ancak birkaç yıl öncesinden itibaren kuantum bilgisayar kavramı somutlaşmaya başlayınca, bunlara yönelik alternatifler araştırılmaya ve geliştirilmeye başlanmış; NIST, PQC (Kuantum Sonrası Kriptografi) standardizasyonunu yürütmüştür.
MLDSA ve MLKEM
Sonunda NIST, Ağustos 2024'te CRYSTALS-Kyber ve CRYSTALS-Dilithium temelli MLKEM ve MLDSA'yı standart olarak kabul etmiştir. Her iki algoritma da MLWE (Module Learning with Errors) adı verilen bir probleme dayanarak çalışmaktadır. Bu tür yapılara biz kafes tabanlı kriptografi diyoruz.
Kafes tabanlı kriptografi, adından da anlaşılacağı üzere kafes üzerindeki matematik problemlerinin zorluğuna dayanan bir kriptografi sistemidir. Ben de bu konuda derin bir matematik bilgisine sahip değilim; ancak tek satırda özetlemek gerekirse, modül kafes üzerinde gürültülü doğrusal denklemleri çözme problemi olarak tanımlanmaktadır. Ne kadar zor olduğuna dair net bir fikrim yok, ancak bu tür problemlerin kuantum bilgisayarlarla bile çözülemeyecek kadar zor olduğu söylenmektedir.
MLDSA
Öncelikle MLDSA'yı inceleyelim.
Yapı
MLDSA, adından da görüldüğü üzere asimetrik bir imza algoritmasıdır ve toplam olarak şu 2 aşamadan geçer:
- İmza üretimi: Özel anahtar kullanılarak mesaj için bir imza üretilir
- İmza doğrulama: Genel anahtar kullanılarak üretilen imzanın geçerliliği kontrol edilir
Ayrıca MLDSA'nın şu 3 özelliği vardır:
- Güçlü varoluşsal sahtecilik direnci (strong existential unforgeability): Bir imza ve genel anahtar ile başka bir geçerli imza üretilemez.
- Seçilmiş mesaj saldırısı (chosen message attack): Herhangi bir mesajın imzasıyla bile genel anahtarı kullanarak yeni bir geçerli imza üretilemez.
- Yan kanal saldırısı (side-channel attack): İmza sırasında sürekli yeni rastgele değerler ve mesajdan türetilmiş sözde rastgele değerler kullanıldığı için güvenlik yüksektir.
- Alan ayrımı (domain separation): Farklı parametreler için farklı seed değerleri kullanılarak tekrarlayan güvenlik sorunları önlenir.
Kod
Şimdi basit bir Go dili örnek kodunu göstereceğim.
Bu örnekte cloudflare/circl paketindeki mldsa kullanılmıştır.
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 // mldsa44 spesifikasyonu ile anahtar üretiyoruz.
14 pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15 if err != nil {
16 panic(err)
17 }
18
19 message := []byte("Hello, World!")
20
21 // İmzayı üretiyoruz.
22 // Dikkat edilmesi gereken bir nokta, 22 Aralık 2024 itibarıyla mevcut sürüme göre crypto.Hash(0) dışındaki değerlerde hata oluşmasıdır.
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 // Genel anahtarın scheme'i çağrılarak doğrulama yapılıyor.
32 ok := pub.Scheme().Verify(pub, message, signature, nil)
33 fmt.Println(ok)
34}
13228 oaSaOA-...
2true
İmza değeri çok uzun olduğu için kısaltılmıştır. Tamamını görmek isterseniz, playground üzerinde çalıştırabilirsiniz.
Base64 ile kodlanmış olmasına rağmen 3228 bayt çıkması biraz yük gibi görünebilir.
Yakın zamanda kuantum bilgisayarlara karşı bir imzayla bu boyutta verileri gönderip almamız gerekebileceğini düşününce bu durum biraz endişe verici geliyor..
MLKEM
Yapı
MLKEM, bir anahtar kapsülleme mekanizmasıdır (Key Encapsulation Mechanism). KEM, genel anahtar şifreleme yöntemini kullanarak iki taraf arasında paylaşılan bir anahtar oluşturmaya olanak tanıyan bir algoritmadır. MLKEM'in anahtar değişim mekanizması şu süreçlerden geçer:
- Anahtar kapsülleme: Gönderen, alıcının genel anahtarını kullanarak şifrelenmiş bir mesaj (cipher text) ve paylaşılan anahtar (shared key) üretir. Bu şifrelenmiş mesaj, alıcının kullanabilmesi için ilk aşamada alıcıya iletilir.
- Anahtar kapsül açma: Alıcı, kendi özel anahtarını kullanarak şifrelenmiş mesajdan paylaşılan anahtarı çıkarır.
MLKEM'de toplam 3 parametre bulunmaktadır. MLKEM-512, MLKEM-768 ve MLKEM-1024 mevcuttur; sayı küçüldükçe anahtar ve şifreli metin boyutu küçülür, sayı büyüdükçe anahtar ve şifreli metin uzar ve güvenlik seviyesi yükselir.
Kod
MLKEM'in go 1.24 sürümüne eklenmesi planlandığından, bu an itibarıyla kullanılabilir olan go 1.24rc1 sürümü kullanılmıştır.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // Alıcının PrivateKey'ini üretiyoruz.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // MLKEM'de PublicKey yerine EncapsulationKey terimi kullanılır.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Basitçe EncapsulationKey'in Bytes() ve NewEncapsulationKeyX ile anahtarı çıkarıp yeniden kullanılabileceğini göstermek için kopyaladım.
20 // Tabii ki gerçek kullanımda bu süreç, metin olarak herkese açık olan alıcının EncapsulationKey anahtarının gönderen tarafından nesneye dönüştürülmesi sürecidir diyebilirsiniz.
21 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22 if err != nil {
23 panic(err)
24 }
25
26 // Encapsulate ile gönderen şifreli metni ve paylaşılan anahtarı üretiyor.
27 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29 // Alıcının özel anahtarını saklayıp geri çağırmayı göstermek için bilerek kopyaladım.
30 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31 if err != nil {
32 panic(err)
33 }
34
35 // Alıcı, özel anahtarını kullanarak şifreli metni Decapsulate eder ve başka bir paylaşılan anahtar üretir.
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=
Sonuç olarak aynı boyutta paylaşılan anahtarların üretildiğini görebilirsiniz!
Bu kod playground üzerinde de incelenebilir.
Sonuç
Her algoritmanın spesifikasyonu, güvenlik seviyesi ile özel anahtar, genel anahtar, imza ya da şifreli metin boyutları aşağıdaki gibi özetlenebilir. Hepsi PQC adına yakışır şekilde büyük boyutlara sahiptir.
| Algoritma | NIST güvenlik seviyesi | Özel anahtar boyutu | Genel anahtar boyutu | İmza/şifreli metin boyutu |
|---|---|---|---|---|
| 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 |
Bu algoritmalar sayesinde kuantum bilgisayarlar üzerinde bile yeterince güvenli bir internet kullanabilmeyi umuyoruz; ancak göreli olarak büyüyen anahtar ve imza/şifreli metin boyutları nedeniyle daha fazla hesaplama yapılması kaçınılmaz görünmektedir.
Yine de Go dili, her algoritmayı etkin biçimde uyguladığı için, uygun yerlerde sizin güvenliğinizi sağlamak amacıyla bunların aktif olarak kullanılmasını umuyoruz!