Penggunaan MLDSA dan MLKEM dalam Bahasa Go
Ikhtisar
Latar Belakang
Sejak lama, komputasi cepat komputer kuantum telah diakui sebagai ancaman terhadap sistem enkripsi konvensional. Hal ini dikarenakan algoritma konvensional seperti RSA atau ECC berpotensi dipecahkan oleh kemampuan komputasi komputer kuantum. Namun, seiring dengan mulai terwujudnya konsep komputer kuantum beberapa tahun terakhir, alternatif-alternatif terhadap ancaman tersebut mulai diteliti dan dikembangkan, dan NIST telah melakukan standardisasi PQC (Post-Quantum Cryptography).
MLDSA dan MLKEM
Pada akhirnya, NIST mengadopsi MLKEM dan MLDSA berbasis CRYSTALS-Kyber dan CRYSTALS-Dilithium sebagai standar pada Agustus 2024. Kedua algoritma ini beroperasi berdasarkan masalah yang dikenal sebagai MLWE (Module Learning with Errors). Format ini dikenal sebagai kriptografi berbasis lattice.
Kriptografi berbasis lattice, seperti namanya, adalah sistem enkripsi yang didasarkan pada kesulitan masalah matematika pada lattice. Saya sendiri tidak memiliki pengetahuan matematika yang mendalam tentang ini, tetapi jika diringkas dalam satu baris, ini adalah masalah memecahkan persamaan linier dengan noise dalam modul lattice
. Tidak jelas seberapa sulitnya, tetapi masalah ini dikatakan sangat sulit sehingga tidak dapat dipecahkan bahkan oleh komputer kuantum.
MLDSA
Mari kita mulai dengan mempelajari MLDSA terlebih dahulu.
Konfigurasi
MLDSA, seperti namanya, adalah algoritma tanda tangan asimetris yang melalui total 2 tahap berikut:
- Pembuatan tanda tangan: Membuat tanda tangan untuk pesan menggunakan kunci pribadi.
- Verifikasi tanda tangan: Memverifikasi validitas tanda tangan yang dibuat menggunakan kunci publik.
Selain itu, MLDSA memiliki 3 karakteristik berikut:
- strong existential unforgeability: Tidak mungkin menghasilkan tanda tangan valid lain dengan satu tanda tangan dan kunci publik.
- chosen message attack: Tidak mungkin menghasilkan tanda tangan valid baru dengan kunci publik, bahkan dengan tanda tangan dari pesan apa pun.
- side-channel attack: Keamanan tinggi karena penggunaan nilai acak baru dan nilai pseudo-acak yang berasal dari pesan secara terus-menerus saat menandatangani.
- domain separation: Mencegah masalah keamanan berulang dengan menggunakan seed yang berbeda untuk parameter yang berbeda.
Kode
Berikut adalah contoh kode sederhana dalam bahasa Go.
Dalam contoh ini, kami menggunakan mldsa dari 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 // Membuat kunci dengan spesifikasi 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 // Membuat tanda tangan.
22 // Satu hal yang perlu diperhatikan adalah bahwa, per 22 Desember 2024, kesalahan akan terjadi kecuali jika crypto.Hash(0) digunakan.
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 // Melakukan verifikasi dengan memanggil skema dari kunci publik.
32 ok := pub.Scheme().Verify(pub, message, signature, nil)
33 fmt.Println(ok)
34}
13228 oaSaOA-...
2true
Nilai tanda tangan terlalu panjang sehingga dihilangkan. Jika Anda ingin melihat keseluruhannya, coba jalankan di playground.
Meskipun dienkode dengan base64, 3228 byte agak memberatkan.
Sedikit membebani pikiran bahwa kita mungkin harus bertukar ukuran ini sebagai tanda tangan yang melawan komputer kuantum dalam waktu dekat..
MLKEM
Konfigurasi
MLKEM adalah mekanisme enkapsulasi kunci (Key Encapsulation Mechanism). KEM adalah algoritma yang memungkinkan dua pihak untuk menghasilkan kunci bersama menggunakan metode enkripsi kunci publik. Mekanisme pertukaran kunci MLKEM melalui proses berikut:
- Enkapsulasi kunci: Pengirim menghasilkan pesan terenkripsi (cipher text) dan kunci bersama (shared key) menggunakan kunci publik penerima. Pesan terenkripsi ini awalnya dikirimkan ke penerima untuk digunakan.
- Dekapsulasi kunci: Penerima mengekstrak kunci bersama dari pesan terenkripsi menggunakan kunci pribadinya.
Terdapat total 3 parameter dalam MLKEM. Terdapat MLKEM-512, MLKEM-768, dan MLKEM-1024. Semakin kecil angkanya, semakin kecil ukuran kunci dan cipher text yang dihasilkan, dan semakin besar angkanya, semakin panjang kunci dan cipher text yang dihasilkan, serta semakin tinggi tingkat keamanannya.
Kode
MLKEM akan ditambahkan di go 1.24, sehingga kami menggunakan go 1.24rc1 yang tersedia saat ini.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // Membuat PrivateKey penerima.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // Dalam MLKEM, istilah EncapsulationKey digunakan, bukan PublicKey.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Untuk menunjukkan secara sederhana bahwa kunci dapat diekstraksi dan digunakan kembali dengan Bytes() dan NewEncapsulationKeyX dari EncapsulationKey, kami telah menduplikasinya.
20 // Tentu saja, dalam penggunaan nyata, proses ini adalah proses di mana pengirim mengubah kunci EncapsulationKey penerima, yang telah dipublikasikan dalam bentuk teks, menjadi objek.
21 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22 if err != nil {
23 panic(err)
24 }
25
26 // Pengirim menghasilkan cipher text dan kunci bersama dengan Encapsulate.
27 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29 // Saya sengaja menduplikasinya untuk menunjukkan cara menyimpan dan mengambil kunci pribadi penerima.
30 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31 if err != nil {
32 panic(err)
33 }
34
35 // Penerima menggunakan kunci pribadinya untuk Decapsulate cipher text dan menghasilkan kunci bersama lain.
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=
Akibatnya, dapat dilihat bahwa kunci bersama dengan ukuran yang sama dibuat!
Kode ini juga dapat diperiksa di playground.
Kesimpulan
Spesifikasi setiap algoritma, tingkat keamanan, atau ukuran kunci pribadi, kunci publik, tanda tangan, atau cipher text dapat diringkas sebagai berikut. Masing-masing, sesuai dengan namanya sebagai PQC, memiliki ukuran yang cukup besar.
Algoritma | Tingkat keamanan NIST | Ukuran kunci pribadi | Ukuran kunci publik | Ukuran tanda tangan/cipher text |
---|---|---|---|---|
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 |
Melalui algoritma ini, kami berharap dapat menggunakan internet yang cukup aman bahkan pada komputer kuantum, tetapi tampaknya tidak dapat dihindari bahwa akan ada lebih banyak komputasi karena ukuran kunci dan tanda tangan/cipher text yang relatif lebih besar.
Meskipun demikian, karena bahasa Go telah mengimplementasikan setiap algoritma secara efektif, diharapkan algoritma ini dapat secara aktif digunakan untuk melindungi keamanan Anda di tempat yang tepat!