GoSuda

Utilizzo di MLDSA e MLKEM nel linguaggio Go

By snowmerak
views ...

Panoramica

Contesto

Per molto tempo, la rapida capacità di calcolo dei computer quantistici è stata percepita come una minaccia per i sistemi crittografici esistenti. Questo perché sistemi come RSA o ECC potrebbero potenzialmente essere decifrati a causa di tale capacità di calcolo dei computer quantistici. Tuttavia, da diversi anni, con l'emergere del concetto di computer quantistico, sono state avviate ricerche e sviluppi di alternative, e il NIST ha proceduto con la standardizzazione del PQC (Post-Quantum Cryptography).

MLDSA e MLKEM

Infine, nell'agosto 2024, il NIST ha adottato MLKEM e MLDSA, basati su CRYSTALS-Kyber e CRYSTALS-Dilithium, come standard. Entrambi gli algoritmi operano basandosi sul problema MLWE (Module Learning with Errors). Questo formato è ciò che chiamiamo crittografia basata su reticoli.

La crittografia basata su reticoli, come suggerisce il nome, è un sistema crittografico basato sulla difficoltà di problemi matematici sui reticoli. Sebbene io non abbia una profonda conoscenza matematica in merito, in una frase, si dice che sia "un problema di risoluzione di equazioni lineari con rumore su un reticolo modulare". Non ho idea di quanto sia difficile, ma si dice che tali problemi siano così difficili da non poter essere risolti nemmeno dai computer quantistici.

MLDSA

Esaminiamo ora MLDSA.

Composizione

MLDSA, come suggerisce il nome, è un algoritmo di firma asimmetrica che attraversa i seguenti due passaggi:

  1. Generazione della firma: creazione di una firma per un messaggio utilizzando la chiave privata.
  2. Verifica della firma: verifica della 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 da una firma e una chiave pubblica.
  2. chosen message attack: non è possibile generare una nuova firma valida con la chiave pubblica da una firma per un qualsiasi messaggio.
  3. side-channel attack: la sicurezza è elevata grazie all'uso continuo di nuovi valori casuali e valori pseudo-casuali derivati dal messaggio durante la firma.
  4. domain separation: previene problemi di sicurezza ripetitivi assicurando che vengano utilizzati seed diversi per parametri diversi.

Codice

Ora vi mostrerò un semplice esempio di codice Go. In questo esempio, è stato utilizzato l'mldsa di 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    // Genera una chiave con la specifica 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    // Genera una firma.
22    // Un punto da notare è che, alla data del 22 dicembre 2024, se non è crypto.Hash(0), si verifica un errore.
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    // Chiama lo schema della chiave pubblica per la verifica.
32	ok := pub.Scheme().Verify(pub, message, signature, nil)
33	fmt.Println(ok)
34}
13228 oaSaOA-...
2true

Il valore della firma è stato omesso perché troppo lungo. Se desiderate visualizzare l'intero contenuto, provate a eseguirlo nel playground.

Anche se codificato in base64, un risultato di 3228 byte potrebbe essere un po' scoraggiante. Pensare che in futuro potremmo dover scambiare firme di questa dimensione per contrastare i computer quantistici è un po' preoccupante.

MLKEM

Composizione

MLKEM è un meccanismo di incapsulamento della chiave (Key Encapsulation Mechanism). KEM è un algoritmo che consente la generazione di una chiave condivisa tra due parti utilizzando la crittografia a chiave pubblica. Il meccanismo di scambio di chiavi di MLKEM segue il seguente processo:

  1. Incapsulamento della chiave: il mittente utilizza la chiave pubblica del destinatario per generare un messaggio crittografato (cipher text) e una chiave condivisa (shared key). Questo messaggio crittografato viene inizialmente inviato al destinatario per essere utilizzato.
  2. Decapsulamento della chiave: il destinatario utilizza la propria chiave privata per estrarre la chiave condivisa dal messaggio crittografato.

Esistono tre parametri totali in MLKEM. Sono MLKEM-512, MLKEM-768 e MLKEM-1024. Un numero inferiore produce chiavi e testo crittografato più piccoli, mentre un numero maggiore produce chiavi e testo crittografato più lunghi, offrendo un livello di sicurezza più elevato.

Codice

MLKEM dovrebbe essere aggiunto in Go 1.24, quindi è stata utilizzata la versione Go 1.24rc1, che è utilizzabile al momento.

 1package main
 2
 3import (
 4	"crypto/mlkem"
 5	"encoding/base64"
 6	"fmt"
 7)
 8
 9func main() {
10    // Genera la PrivateKey del ricevitore.
11	receiverKey, err := mlkem.GenerateKey1024()
12	if err != nil {
13		panic(err)
14	}
15
16    // In MLKEM, si usa il termine EncapsulationKey anziché PublicKey.
17	receiverPubKey := receiverKey.EncapsulationKey()
18
19    // È stato clonato per mostrare che è possibile estrarre e riutilizzare la chiave semplicemente con Bytes() di EncapsulationKey e NewEncapsulationKeyX.
20    // Naturalmente, nell'uso reale, questo processo può essere visto come il mittente che crea un oggetto dalla chiave EncapsulationKey del ricevitore che era pubblica come testo.
21	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22	if err != nil {
23		panic(err)
24	}
25
26    // Il mittente genera il testo cifrato e la chiave condivisa con Encapsulate.
27	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29    // È stato clonato appositamente per mostrare come salvare e recuperare la chiave privata del destinatario.
30	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31	if err != nil {
32		panic(err)
33	}
34
35    // Il destinatario decapsula il testo cifrato usando la chiave privata per generare un'altra chiave condivisa.
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=

Di conseguenza, si può verificare che viene generata una chiave condivisa della stessa dimensione!

Questo codice può essere verificato anche nel playground.

Conclusione

Le specifiche di ciascun algoritmo, il livello di sicurezza, la dimensione della chiave privata, della chiave pubblica, della firma o del testo cifrato possono essere riassunte come segue. Ognuno vanta dimensioni considerevoli, degne del nome PQC.

AlgoritmoLivello di sicurezza NISTDimensione chiave privataDimensione chiave pubblicaDimensione firma/testo cifrato
ML-DSA-4422.5601.3122.420
ML-DSA-6534.0321.9523.309
ML-DSA-8754.8962.5924.627
ML-KEM-51211.632800768
ML-KEM-76832.4001.1841.088
ML-KEM-102453.1681.5681.568

Grazie a questi algoritmi, ci aspettiamo di poter utilizzare un Internet sufficientemente sicuro anche sui computer quantistici, ma sembra inevitabile che ci saranno più operazioni a causa delle dimensioni relativamente maggiori delle chiavi e delle firme/testi cifrati.

Tuttavia, poiché ogni algoritmo è efficacemente implementato nel linguaggio Go, ci auguriamo che venga attivamente utilizzato per proteggere la vostra sicurezza nei contesti appropriati!