---
id: df2ea4cfe0d5ec72a92e1b3cfd4156f2
author: snowmerak
title: Experiență cu utilizarea MLDSA și MLKEM în limbajul Go
description: În acest material vom examina modul de utilizare a algoritmilor de criptografie rezistentă la computerele cuantice, MLDSA și MLKEM, în limbajul Go, prezentând caracteristicile fiecărui algoritm și exemple de cod.
language: ro
date: 2024-12-22T08:17:58.797764292Z
path: /blog/posts/usage-of-mldsa-and-mlkem-in-the-go-language-zb0442110
category: security
---

## Prezentare generală

### Context

De ceva vreme, rapiditatea calculului cuantic a fost percepută ca o amenințare pentru sistemele criptografice existente. Motivul este că RSA și ECC, de exemplu, ar putea fi sparte prin această capacitate de calcul a computerelor cuantice. Totuși, din câțiva ani, conceptul de computer cuantic a început să devină tangibil, iar alternativele la acestea au început să fie cercetate și dezvoltate, NIST desfășurând un proces de standardizare pentru PQC (criptografie post-cuantică).

### MLDSA și MLKEM

În cele din urmă, în august 2024, NIST a adoptat ca standarde MLKEM și MLDSA, bazate pe CRYSTALS-Kyber și CRYSTALS-Dilithium. Cei doi algoritmi funcționează pe baza problemei MLWE (Module Learning with Errors). Acest tip de criptografie este denumit de noi criptografie bazată pe rețele (lattice-based cryptography).

Criptografia bazată pe rețele este, după cum sugerează și numele, un sistem criptografic bazat pe dificultatea problemelor matematice din rețele. Nu dețin cunoștințe matematice profunde în acest domeniu, însă, rezumat într-o singură propoziție, aceasta constă în `problema rezolvării unui sistem de ecuații liniare cu zgomot într-o rețea modulară`. Cât de dificilă este această problemă nu îmi este clar, dar se spune că ea este atât de grea încât nu poate fi rezolvată nici de computerele cuantice.

## MLDSA

Să începem, deci, prin a afla despre MLDSA.

### Structură

După cum sugerează numele, MLDSA este un algoritm de semnătură asimetrică, care parcurge în total următoarele două etape:

1. Generarea semnăturii: generarea unei semnături pentru un mesaj folosind cheia privată
2. Verificarea semnăturii: confirmarea validității semnăturii generate folosind cheia publică

De asemenea, MLDSA are următoarele 3 caracteristici:

1. Strong existential unforgeability: pe baza unei semnături și a cheii publice nu se poate genera o altă semnătură validă.
2. Chosen message attack: nici cu semnătura unui mesaj oarecare nu se poate genera, folosind cheia publică, o nouă semnătură validă.
3. Side-channel attack: la semnare se utilizează continuu valori aleatoare noi și valori pseudo-aleatoare derivate din mesaj, ceea ce ridică nivelul de securitate.
4. Domain separation: pentru parametri diferiți se utilizează seed-uri diferite, prevenind astfel probleme de securitate repetitive.

### Cod

Acum vă voi arăta un exemplu simplu de cod în limbajul Go.
În acest exemplu am utilizat mldsa din [cloudflare/circl](github.com/cloudflare/circl).

```go
package main

import (
	"crypto"
	"crypto/rand"
	"encoding/base64"
	"fmt"

	"github.com/cloudflare/circl/sign/mldsa/mldsa44"
)

func main() {
    // Generăm cheia cu specificația mldsa44.
	pub, priv, err := mldsa44.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}

	message := []byte("Hello, World!")

    // Generăm semnătura.
    // De menționat: la versiunea actuală din 22 decembrie 2024, dacă crypto.Hash(0) nu este utilizat, apare o eroare.
	signature, err := priv.Sign(rand.Reader, message, crypto.Hash(0))
	if err != nil {
		panic(err)
	}

	encodedSignature := base64.URLEncoding.EncodeToString(signature)
	fmt.Println(len(encodedSignature), encodedSignature)

    // Apelăm scheme-ul cheii publice pentru a efectua verificarea.
	ok := pub.Scheme().Verify(pub, message, signature, nil)
	fmt.Println(ok)
}
```

```
3228 oaSaOA-...
true
```

> Valoarea semnăturii a fost omisă deoarece este prea lungă. Dacă doriți versiunea integrală, puteți rula codul pe [playground](https://go.dev/play/p/nqbUnKpFW3j).

Deși este codificată în base64, dimensiunea de 3228 de octeți este probabil destul de incomodă.
Gândindu-mă că în curând, pentru a ne apăra de computerele cuantice, ar putea fi nevoie să schimbăm semnături de această dimensiune, această perspectivă mă face să mă simt cam îngrijorat...

## MLKEM

### Structură

MLKEM este un mecanism de încapsulare a cheii (Key Encapsulation Mechanism). KEM este un algoritm care, folosind criptografia cu cheie publică, permite generarea unei chei partajate între două părți. Mecanismul de schimb de chei MLKEM parcurge următoarele etape:

1. Încapsularea cheii: expeditorul utilizează cheia publică a destinatarului pentru a genera un mesaj criptat (cipher text) și o cheie partajată (shared key). Acest mesaj criptat este transmis inițial destinatarului, pentru a fi utilizat de acesta.
2. Decapsularea cheii: destinatarul utilizează cheia sa privată pentru a extrage cheia partajată din mesajul criptat.

MLKEM are în total 3 parametri: MLKEM-512, MLKEM-768 și MLKEM-1024. Cu cât valoarea este mai mică, cu atât cheile și textul criptat sunt mai mici; cu cât valoarea este mai mare, cu atât cheile și textul criptat sunt mai lungi, iar nivelul de securitate este mai ridicat.

### Cod

MLKEM urmează să fie adăugat în Go 1.24, prin urmare, în momentul de față, am utilizat go 1.24rc1, care este disponibilă.

```go
package main

import (
	"crypto/mlkem"
	"encoding/base64"
	"fmt"
)

func main() {
    // Generăm PrivateKey-ul destinatarului.
	receiverKey, err := mlkem.GenerateKey1024()
	if err != nil {
		panic(err)
	}

    // În MLKEM se utilizează termenul EncapsulationKey, nu PublicKey.
	receiverPubKey := receiverKey.EncapsulationKey()

    // Pentru simplitate, am duplicat EncapsulationKey pentru a demonstra că poate fi extras prin Bytes() și reutilizat prin NewEncapsulationKeyX.
    // Desigur, în practică, acest pas înseamnă că expeditorul transformă în obiect cheia EncapsulationKey a destinatarului, publicată anterior sub formă de text.
	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Cu Encapsulate, expeditorul generează textul criptat și cheia partajată.
	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()

    // Am duplicat intenționat cheia privată a destinatarului pentru a demonstra salvarea și recuperarea acesteia.
	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Destinatarul utilizează cheia privată pentru a efectua Decapsulate asupra textului criptat și generează o altă cheie partajată.
	sharedKeyReceiver, err := clonedReceiverKey.Decapsulate(cipherText)
	if err != nil {
		panic(err)
	}

	fmt.Println(base64.StdEncoding.EncodeToString(SenderSharedKey))
	fmt.Println(base64.StdEncoding.EncodeToString(sharedKeyReceiver))
}
```

```sh
Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=
Q1ciS818WFHTK7D4MTvsQvciMTGF+dSGqMllOxW80ew=
```

Ca rezultat, se poate constata că se generează chei partajate de aceeași dimensiune!

Acest cod poate fi verificat și pe [playground](https://go.dev/play/p/n_cxNp435Qn?v=gotip).

## Concluzie

Specificațiile fiecărui algoritm, nivelul de securitate, precum și dimensiunile cheii private, cheii publice, semnăturii sau textului criptat pot fi rezumate astfel. Fiecare dintre ele se remarcă, fără a fi nevoie de scuze pentru numele PQC, printr-o dimensiune considerabilă.

| Algoritm    | Nivel de securitate NIST | Dimensiune cheie privată | Dimensiune cheie publică | Dimensiune semnătură/text criptat |
| ----------- | ------------------------ | ------------------------ | ------------------------ | --------------------------------- |
| 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                             |

Datorită acestor algoritmi, ne așteptăm să putem utiliza un internet suficient de sigur și în prezența computerelor cuantice, însă, din cauza dimensiunilor relativ mai mari ale cheilor și ale semnăturilor/textelor criptate, un volum mai mare de calcule pare inevitabil.

Totuși, Go implementează eficient fiecare dintre acești algoritmi, așa că sperăm că ei vor fi utilizați activ, în locurile potrivite, pentru a vă proteja securitatea!
