Използване на MLDSA и MLKEM в Go език
Общ преглед
Предистория
От доста време бързите изчисления на квантовите компютри се възприемат като заплаха за съществуващите криптографски системи. Това е така, защото съществуващите системи като RSA и ECC могат да бъдат разбити поради изчислителната мощ на квантовите компютри. Въпреки това, през последните няколко години, с появата на концепцията за квантови компютри, започнаха проучвания и разработки на алтернативи, а NIST осъществи стандартизация на PQC (квантово-устойчива криптография).
MLDSA и MLKEM
В крайна сметка NIST прие MLKEM и MLDSA, базирани на CRYSTALS-Kyber и CRYSTALS-Dilithium, като стандарти през август 2024 г. Двата алгоритъма функционират въз основа на проблема MLWE (Module Learning with Errors). Ние наричаме този формат криптография, базирана на решетки.
Криптографията, базирана на решетки, както подсказва името, е криптографска система, основана на трудността на математически проблем върху решетка. Аз нямам задълбочени математически познания по този въпрос, но накратко може да се обобщи като „проблем с решаването на линейни уравнения с шум в модулна решетка“. Не е ясно колко е трудно, но се казва, че тези проблеми са толкова трудни, че дори квантовите компютри не могат да ги решат.
MLDSA
Нека първо разгледаме MLDSA.
Конфигурация
MLDSA, както подсказва името, е асиметричен алгоритъм за подписване, който преминава през следните 2 етапа:
- Генериране на подпис: Използване на частен ключ за генериране на подпис за съобщение.
- Проверка на подписа: Използване на публичен ключ за проверка на валидността на генерирания подпис.
Освен това MLDSA има следните 3 характеристики:
- strong existential unforgeability: Не може да се генерира друг валиден подпис с един подпис и публичен ключ.
- chosen message attack: Не може да се генерира нов валиден подпис с публичен ключ, дори и с подпис за дадено съобщение.
- side-channel attack: Висока сигурност чрез постоянно използване на нови случайни стойности и псевдослучайни стойности, получени от съобщението по време на подписване.
- domain separation: Предотвратява повтарящи се проблеми със сигурността, като използва различни seed-ове за различни параметри.
Код
Сега ще ви покажа прост пример за код на Go. В този пример е използван mldsa от 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 // Генерира ключове със спецификация 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 // Генерира подпис.
22 // Едно нещо, което трябва да се отбележи, е, че въз основа на текущата версия от 22 декември 2024 г., ще възникне грешка, ако не е 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 // Извиква схемата на публичния ключ за проверка.
32 ok := pub.Scheme().Verify(pub, message, signature, nil)
33 fmt.Println(ok)
34}
13228 oaSaOA-...
2true
Стойността на подписа е твърде дълга и е съкратена. Ако искате да видите пълния текст, моля, стартирайте го в playground.
Въпреки че е кодирано в base64, 3228 байта може да са малко тежки. Малко е притеснително да си помислим, че скоро може да се наложи да обменяме този размер като подпис срещу квантови компютри...
MLKEM
Конфигурация
MLKEM е механизъм за капсулиране на ключове (Key Encapsulation Mechanism). KEM е алгоритъм, който позволява генерирането на споделен ключ между две страни, използвайки криптиране с публичен ключ. Механизмът за обмен на ключове на MLKEM преминава през следния процес:
- Капсулиране на ключ: Подателят генерира криптирано съобщение (cipher text) и споделен ключ (shared key), използвайки публичния ключ на получателя. Това криптирано съобщение първоначално се предава на получателя за използване.
- Декапсулиране на ключ: Получателят извлича споделения ключ от криптираното съобщение, използвайки своя частен ключ.
MLKEM има общо 3 параметъра. Съществуват MLKEM-512, MLKEM-768, MLKEM-1024, като по-малките числа водят до по-малки ключове и криптирани текстове, а по-големите числа водят до по-дълги ключове и криптирани текстове, както и до по-високо ниво на сигурност.
Код
MLKEM ще бъде добавен в go 1.24, така че към момента на писане е използвана go 1.24rc1.
1package main
2
3import (
4 "crypto/mlkem"
5 "encoding/base64"
6 "fmt"
7)
8
9func main() {
10 // Генерира PrivateKey на получателя.
11 receiverKey, err := mlkem.GenerateKey1024()
12 if err != nil {
13 panic(err)
14 }
15
16 // В MLKEM се използва терминът EncapsulationKey, а не PublicKey.
17 receiverPubKey := receiverKey.EncapsulationKey()
18
19 // Клонирахме го, за да покажем, че може да се извлече и използва отново ключ чрез Bytes() на EncapsulationKey и NewEncapsulationKeyX.
20 // Разбира се, ако се използва в реалния свят, този процес може да се разглежда като процес, при който подателят създава обект от EncapsulationKey на получателя, който е бил публично достъпен като текст.
21 clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22 if err != nil {
23 panic(err)
24 }
25
26 // Подателят генерира криптиран текст и споделен ключ с Encapsulate.
27 cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29 // Умишлено клонирахме PrivateKey на получателя, за да покажем как се съхранява и извлича.
30 clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31 if err != nil {
32 panic(err)
33 }
34
35 // Получателят използва PrivateKey, за да Decapsulate криптирания текст и да генерира друг споделен ключ.
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=
В резултат на това можем да потвърдим, че се генерират споделени ключове с еднакъв размер!
Този код може да бъде проверен и в playground.
Заключение
Спецификациите, нивата на сигурност, размерите на частните ключове, публичните ключове, подписите или криптираните съобщения на всеки алгоритъм могат да бъдат обобщени, както следва. Всеки от тях може да се похвали с големи размери, достойно за името PQC.
| Алгоритъм | NIST Ниво на сигурност | Размер на частен ключ | Размер на публичен ключ | Размер на подпис/криптирано съобщение |
|---|---|---|---|---|
| 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 |
Надяваме се, че благодарение на тези алгоритми ще можем да използваме интернет, който е достатъчно сигурен дори срещу квантови компютри, но изглежда неизбежно, че ще има повече изчисления поради относително по-големите размери на ключовете и подписите/криптираните съобщения.
Въпреки това, тъй като езикът Go е ефективно реализиран за всеки алгоритъм, очакваме той да бъде активно използван за защита на вашата сигурност на подходящи места!