GoSuda

Utilisation de MLDSA et MLKEM dans le langage Go

By snowmerak
views ...

Aperçu

Contexte

Depuis un certain temps déjà, la rapidité de calcul des ordinateurs quantiques est perçue comme une menace pour les systèmes cryptographiques existants. En effet, des systèmes tels que RSA ou ECC risquent d'être déchiffrés en raison de cette capacité de calcul des ordinateurs quantiques. Cependant, depuis plusieurs années, le concept d'ordinateur quantique a commencé à se concrétiser, ce qui a entraîné la recherche et le développement d'alternatives, et le NIST a entrepris la standardisation de la PQC (cryptographie post-quantique).

MLDSA et MLKEM

Finalement, le NIST a adopté MLKEM et MLDSA, basés sur CRYSTALS-Kyber et CRYSTALS-Dilithium, comme standards en août 2024. Les deux algorithmes fonctionnent sur la base d'un problème appelé MLWE (Module Learning with Errors). Nous appelons ce format la cryptographie basée sur les réseaux.

La cryptographie basée sur les réseaux est, comme son nom l'indique, un système cryptographique basé sur la difficulté d'un problème mathématique sur un réseau. Je n'ai pas de connaissances mathématiques approfondies à ce sujet, mais en une phrase, il s'agit de « résoudre une équation linéaire avec du bruit dans un réseau modulaire ». Bien que je n'aie aucune idée de sa difficulté, on dit que de tels problèmes sont si difficiles qu'ils ne peuvent pas être résolus même avec un ordinateur quantique.

MLDSA

Examinons d'abord MLDSA.

Composition

MLDSA, comme son nom l'indique, est un algorithme de signature asymétrique qui passe par les 2 étapes suivantes :

  1. Génération de signature : création d'une signature pour un message à l'aide d'une clé privée.
  2. Vérification de signature : vérification de la validité de la signature générée à l'aide d'une clé publique.

Et MLDSA possède les 3 caractéristiques suivantes :

  1. strong existential unforgeability : il est impossible de générer une autre signature valide avec une seule signature et une clé publique.
  2. chosen message attack : il est impossible de générer une nouvelle signature valide avec une clé publique à partir d'une signature pour un message donné.
  3. side-channel attack : la sécurité est élevée car de nouvelles valeurs aléatoires et des valeurs pseudo-aléatoires dérivées du message sont constamment utilisées lors de la signature.
  4. domain separation : en utilisant des seed différentes pour des paramètres différents, cela prévient les problèmes de sécurité récurrents.

Code

Voici un exemple de code simple en Go. Cet exemple utilise mldsa de 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    // Génère une clé selon la spécification 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    // Génère une signature.
22    // Une chose à noter est qu'à partir de la version actuelle du 22 décembre 2024, une erreur se produit si ce n'est pas 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    // Appelle le scheme de la clé publique pour effectuer la vérification.
32	ok := pub.Scheme().Verify(pub, message, signature, nil)
33	fmt.Println(ok)
34}
13228 oaSaOA-...
2true

La valeur de la signature est trop longue et a été omise. Si vous souhaitez voir le texte complet, exécutez-le dans le playground.

Même si elle est encodée en base64, une taille de 3228 octets pourrait être un peu lourde. Il est un peu inquiétant de penser que nous devrons peut-être échanger cette taille de signature pour contrer les ordinateurs quantiques dans un avenir proche.

MLKEM

Composition

MLKEM est un mécanisme d'encapsulation de clé (Key Encapsulation Mechanism). Un KEM est un algorithme qui permet de générer une clé partagée entre deux parties en utilisant une méthode de chiffrement à clé publique. Le mécanisme d'échange de clés de MLKEM passe par les étapes suivantes :

  1. Encapsulation de clé : l'expéditeur utilise la clé publique du destinataire pour générer un message chiffré (cipher text) et une clé partagée (shared key). Ce message chiffré est initialement transmis au destinataire pour être utilisé.
  2. Décapsulation de clé : le destinataire utilise sa clé privée pour extraire la clé partagée du message chiffré.

MLKEM possède 3 paramètres au total. MLKEM-512, MLKEM-768, MLKEM-1024 existent, et plus le nombre est petit, plus la clé et le texte chiffré sont petits, et plus le nombre est grand, plus la clé et le texte chiffré sont longs, et plus le niveau de sécurité est élevé.

Code

MLKEM devrait être ajouté dans go 1.24, j'ai donc utilisé go 1.24rc1, qui est disponible actuellement.

 1package main
 2
 3import (
 4	"crypto/mlkem"
 5	"encoding/base64"
 6	"fmt"
 7)
 8
 9func main() {
10    // Génère la PrivateKey du destinataire.
11	receiverKey, err := mlkem.GenerateKey1024()
12	if err != nil {
13		panic(err)
14	}
15
16    // Dans MLKEM, le terme EncapsulationKey est utilisé au lieu de PublicKey.
17	receiverPubKey := receiverKey.EncapsulationKey()
18
19    // Pour montrer que la clé peut être extraite et réutilisée avec Bytes() de EncapsulationKey et NewEncapsulationKeyX, elle a été clonée.
20    // Bien sûr, en réalité, ce processus peut être considéré comme le processus par lequel l'expéditeur crée un objet à partir de la clé EncapsulationKey du destinataire qui était exposée sous forme de texte.
21	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22	if err != nil {
23		panic(err)
24	}
25
26    // Avec Encapsulate, l'expéditeur génère le texte chiffré et la clé partagée.
27	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29    // J'ai délibérément cloné la clé privée du destinataire pour montrer comment la stocker et la récupérer.
30	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31	if err != nil {
32		panic(err)
33	}
34
35    // Le destinataire utilise sa clé privée pour Décapsulate le texte chiffré et génère une autre clé partagée.
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=

En conséquence, nous pouvons confirmer que des clés partagées de la même taille sont générées !

Ce code est également disponible sur le playground.

Conclusion

Les spécifications de chaque algorithme, les niveaux de sécurité, ainsi que la taille des clés privées, des clés publiques, des signatures ou des textes chiffrés peuvent être résumés comme suit. Chacun d'eux présente des tailles considérables, dignes du nom de PQC.

AlgorithmeNiveau de sécurité NISTTaille de la clé privéeTaille de la clé publiqueTaille de la signature/du texte chiffré
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

Nous espérons que ces algorithmes permettront d'utiliser un Internet suffisamment sécurisé même face aux ordinateurs quantiques. Cependant, il semble inévitable que l'augmentation relative de la taille des clés et des signatures/textes chiffrés entraînera davantage d'opérations.

Néanmoins, les algorithmes étant efficacement implémentés dans le langage Go, nous espérons qu'ils seront activement utilisés pour assurer la sécurité là où cela est approprié !