---
id: df2ea4cfe0d5ec72a92e1b3cfd4156f2
author: snowmerak
title: Esperienza nell'uso di MLDSA e MLKEM nel linguaggio Go
description: Questa guida illustra l'uso di MLDSA e MLKEM, algoritmi di crittografia resistente ai computer quantistici, nel linguaggio Go, descrivendo le caratteristiche di ciascun algoritmo e fornendo esempi di codice.
language: it
date: 2024-12-22T08:17:58.797764292Z
path: /blog/posts/usage-of-mldsa-and-mlkem-in-the-go-language-zb0442110
category: security
---

## Panoramica

### Contesto

Da parecchio tempo il rapido calcolo dei computer quantistici è considerato una minaccia per i sistemi crittografici esistenti. Algoritmi come RSA ed ECC, infatti, rischiano di essere decifrati grazie a questa capacità di calcolo. Tuttavia, a partire da alcuni anni fa, il concetto di computer quantistico ha iniziato a delinearsi concretamente, e sono quindi iniziate la ricerca e lo sviluppo di alternative, mentre il NIST ha portato avanti la standardizzazione della PQC (Post-Quantum Cryptography, crittografia post-quantistica).

### MLDSA e MLKEM

Alla fine, nell'agosto 2024, il NIST ha adottato come standard MLKEM e MLDSA, basati su CRYSTALS-Kyber e CRYSTALS-Dilithium. Entrambi gli algoritmi operano sulla base del problema MLWE (Module Learning with Errors). Questa tipologia è da noi definita crittografia basata su reticoli.

La crittografia basata su reticoli, come suggerisce il nome, è un sistema crittografico fondato sulla difficoltà dei problemi matematici definiti su reticoli. Non possiedo una conoscenza matematica approfondita in materia, ma riassumendo in una riga, si tratta di `il problema di risolvere un sistema di equazioni lineari con rumore su un reticolo modulare`. Non è facile farsi un'idea precisa di quanto sia difficile, ma si dice che tali problemi siano talmente ardui da non poter essere risolti nemmeno dai computer quantistici.

## MLDSA

Vediamo innanzitutto MLDSA.

### Struttura

MLDSA, come si evince dal nome, è un algoritmo di firma asimmetrica e si articola nelle seguenti due fasi:

1. Generazione della firma: si genera una firma per il messaggio utilizzando la chiave privata
2. Verifica della firma: si verifica la validità della firma generata utilizzando la chiave pubblica

Inoltre, MLDSA presenta le seguenti tre caratteristiche:

1. Strong existential unforgeability: non è possibile generare un'altra firma valida a partire da una firma e dalla chiave pubblica.
2. Chosen message attack: non è possibile generare una nuova firma valida, utilizzando la chiave pubblica, a partire dalla firma di un messaggio qualsiasi.
3. Side-channel attack: durante la firma vengono sempre utilizzati nuovi valori casuali e valori pseudocasuali derivati dal messaggio, il che ne aumenta il livello di sicurezza.
4. Domain separation: si utilizzano seed diversi per parametri diversi, prevenendo così problemi di sicurezza dovuti alla ripetizione.

### Codice

Ora mostrerò un semplice esempio di codice in linguaggio Go.
In questo esempio è stato utilizzato il mldsa di [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() {
    // Genera la chiave con la specifica mldsa44.
	pub, priv, err := mldsa44.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}

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

    // Genera la firma.
    // Un punto da tenere presente è che, nella versione attuale al 22 dicembre 2024, si verifica un errore se il valore non è crypto.Hash(0).
	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)

    // Esegue la verifica richiamando lo scheme della chiave pubblica.
	ok := pub.Scheme().Verify(pub, message, signature, nil)
	fmt.Println(ok)
}
```

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

> Il valore della firma è stato omesso perché troppo lungo. Per visualizzarlo integralmente, eseguite il codice nel [playground](https://go.dev/play/p/nqbUnKpFW3j).

Anche se codificato in base64, che una firma occupi 3228 byte è certamente un po' gravoso.
Pensando che in futuro potremmo dover scambiare firme di questa dimensione per proteggerci dai computer quantistici, la cosa mi preoccupa un po'...

## MLKEM

### Struttura

MLKEM è un meccanismo di incapsulamento di chiavi (Key Encapsulation Mechanism). Il KEM è un algoritmo che, mediante la crittografia a chiave pubblica, consente di generare una chiave condivisa tra due parti. Lo scambio di chiavi del MLKEM segue il seguente procedimento:

1. Incapsulamento della chiave: il mittente utilizza la chiave pubblica del destinatario per generare un messaggio cifrato (cipher text) e una chiave condivisa (shared key). Tale messaggio cifrato viene inizialmente trasmesso al destinatario affinché lo utilizzi.
2. Decapsulamento della chiave: il destinatario utilizza la propria chiave privata per estrarre la chiave condivisa dal messaggio cifrato.

MLKEM prevede tre parametri: MLKEM-512, MLKEM-768 e MLKEM-1024. Al diminuire del valore, si ottengono chiavi e testi cifrati più piccoli; all'aumentare del valore, si ottengono chiavi e testi cifrati più lunghi e un livello di sicurezza più elevato.

### Codice

Poiché MLKEM sarà aggiunto in Go 1.24, è stata utilizzata la versione go 1.24rc1, attualmente disponibile.

```go
package main

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

func main() {
    // Genera la PrivateKey del destinatario.
	receiverKey, err := mlkem.GenerateKey1024()
	if err != nil {
		panic(err)
	}

    // In MLKEM si utilizza il termine EncapsulationKey invece di PublicKey.
	receiverPubKey := receiverKey.EncapsulationKey()

    // Per mostrare in modo semplice che è possibile estrarre e riutilizzare la EncapsulationKey tramite Bytes() e NewEncapsulationKeyX, la si è duplicata.
    // Naturalmente, nella pratica, questo processo corrisponde a quando il mittente crea un oggetto a partire dalla EncapsulationKey del destinatario, pubblicata in forma testuale.
	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Con Encapsulate, il mittente genera il testo cifrato e la chiave condivisa.
	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()

    // Si è deliberatamente duplicata la chiave per mostrare come salvare e recuperare la chiave privata del destinatario.
	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Il destinatario, usando la propria chiave privata, esegue Decapsulate sul testo cifrato e genera un'altra chiave condivisa.
	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=
```

Si può quindi constatare che viene generata una chiave condivisa della stessa dimensione!

Questo codice è disponibile anche nel [playground](https://go.dev/play/p/n_cxNp435Qn?v=gotip).

## Conclusione

Le specifiche di ciascun algoritmo, il livello di sicurezza e le dimensioni della chiave privata, della chiave pubblica, della firma o del testo cifrato possono essere riassunte come segue. Ciascuno di essi, come ci si aspetta da algoritmi che si fregiano del nome PQC, presenta dimensioni considerevoli.

| Algoritmo   | Livello di sicurezza NIST | Dimensione chiave privata | Dimensione chiave pubblica | Dimensione firma/testo cifrato |
| ----------- | ------------------------- | ------------------------- | -------------------------- | ------------------------------ |
| 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                          |

Grazie a questi algoritmi, speriamo di poter utilizzare un internet sufficientemente sicuro anche nell'era dei computer quantistici, ma l'aumento delle dimensioni delle chiavi e delle firme/testi cifrati comporterà inevitabilmente un maggior carico computazionale.

Ciò nonostante, Go implementa efficacemente ciascun algoritmo, e auspico che venga utilizzato attivamente, nei punti opportuni, per proteggere la sicurezza di tutti voi!
