---
id: df2ea4cfe0d5ec72a92e1b3cfd4156f2
author: snowmerak
title: Retour d'expérience sur l'utilisation de MLDSA et MLKEM en Go
description: Découvrez comment utiliser les algorithmes de cryptographie post-quantique MLDSA et MLKEM en Go, avec une présentation des caractéristiques de chaque algorithme et des exemples de code.
language: fr
date: 2024-12-22T08:17:58.797764292Z
path: /blog/posts/usage-of-mldsa-and-mlkem-in-the-go-language-zb0442110
category: security
---

## Aperçu

### Contexte

Depuis assez longtemps, la capacité de calcul rapide des ordinateurs quantiques est perçue comme une menace pour les systèmes de chiffrement existants. En effet, des algorithmes tels que RSA ou ECC pourraient être déchiffrés grâce à cette puissance de calcul. Cependant, depuis quelques années, le concept d'ordinateur quantique commence à se concrétiser, et des alternatives à ces systèmes ont commencé à être étudiées et développées ; le NIST mène ainsi la normalisation de la PQC (cryptographie post-quantique).

### MLDSA et MLKEM

Enfin, en août 2024, le NIST a adopté comme normes MLKEM et MLDSA, fondés sur CRYSTALS-Kyber et CRYSTALS-Dilithium. Ces deux algorithmes reposent sur le problème MLWE (Module Learning with Errors). Ce type de construction est appelé cryptographie fondée sur les réseaux (lattice-based cryptography).

La cryptographie fondée sur les réseaux est, comme son nom l'indique, un système cryptographique reposant sur la difficulté de problèmes mathématiques sur des réseaux. Je ne dispose pas moi-même de connaissances mathématiques approfondies à ce sujet, mais en une phrase, on la décrit comme « le problème consistant à résoudre des équations linéaires bruitées dans un réseau modulaire ». Il est difficile de se faire une idée de son niveau de difficulté, mais on dit que ce type de problème est si ardu qu'il ne peut pas être résolu même par un ordinateur quantique.

## MLDSA

Commençons donc par examiner MLDSA.

### Composition

Comme son nom l'indique, MLDSA est un algorithme de signature asymétrique qui comporte les deux étapes suivantes :

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

De plus, MLDSA présente les trois caractéristiques suivantes :

1. Strong existential unforgeability : à partir d'une signature et d'une clé publique, il est impossible de générer une autre signature valide.
2. Chosen message attack : même à partir de la signature d'un message quelconque, il est impossible de générer, à l'aide de la clé publique, une nouvelle signature valide.
3. Side-channel attack : lors de la signature, des valeurs aléatoires nouvelles et des valeurs pseudo-aléatoires dérivées du message sont utilisées en permanence, ce qui renforce le niveau de sécurité.
4. Domain separation : en utilisant des seeds différents pour des paramètres différents, on prévient les problèmes de sécurité liés à la répétition.

### Code

Voici maintenant un exemple de code simple en langage Go.
Dans cet exemple, le module mldsa de [cloudflare/circl](github.com/cloudflare/circl) a été utilisé.

```go
package main

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

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

func main() {
    // Génère une clé selon la spécification mldsa44.
	pub, priv, err := mldsa44.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}

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

    // Génère la signature.
    // Point d'attention : à la version actuelle au 22 décembre 2024, une erreur se produit si crypto.Hash(0) n'est pas utilisé.
	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)

    // Effectue la vérification en appelant le scheme de la clé publique.
	ok := pub.Scheme().Verify(pub, message, signature, nil)
	fmt.Println(ok)
}
```

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

> La valeur de la signature étant trop longue, elle a été omise. Pour voir le texte complet, exécutez-le sur le [playground](https://go.dev/play/p/nqbUnKpFW3j).

Même encodée en base64, une taille de 3228 octets peut paraître un peu lourde.
En songeant que nous devrons peut-être bientôt échanger des signatures de cette taille pour résister aux ordinateurs quantiques, cela me paraît un peu préoccupant...

## MLKEM

### Composition

MLKEM est un mécanisme d'encapsulation de clés (Key Encapsulation Mechanism). Un KEM est un algorithme qui permet de générer une clé partagée entre deux parties en utilisant un système de chiffrement à clé publique. Le mécanisme d'échange de clés de MLKEM comporte 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 d'abord transmis au destinataire afin qu'il puisse l'utiliser.
2. Décapsulation de clé : le destinataire utilise sa propre clé privée pour extraire la clé partagée à partir du message chiffré.

MLKEM comporte au total trois paramètres : MLKEM-512, MLKEM-768 et MLKEM-1024. Plus le nombre est petit, plus les clés et les textes chiffrés sont petits ; plus il est grand, plus les clés et les textes chiffrés sont longs, et plus le niveau de sécurité est élevé.

### Code

MLKEM devant être ajouté dans go 1.24, nous avons utilisé go 1.24rc1, disponible au moment de la rédaction.

```go
package main

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

func main() {
    // Génère la PrivateKey du destinataire.
	receiverKey, err := mlkem.GenerateKey1024()
	if err != nil {
		panic(err)
	}

    // Dans MLKEM, on utilise le terme EncapsulationKey plutôt que PublicKey.
	receiverPubKey := receiverKey.EncapsulationKey()

    // Pour montrer simplement qu'on peut extraire la clé avec Bytes() de EncapsulationKey et la réutiliser avec NewEncapsulationKeyX, l'objet a été dupliqué.
    // Bien entendu, en pratique, ce processus correspond à la création, par l'expéditeur, d'un objet à partir de la clé EncapsulationKey du destinataire, publiée sous forme de texte.
	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Encapsulate permet à l'expéditeur de générer le texte chiffré et la clé partagée.
	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()

    // L'objet a été dupliqué délibérément pour montrer comment stocker et restaurer la clé privée du destinataire.
	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
	if err != nil {
		panic(err)
	}

    // Le destinataire utilise sa clé privée pour Decapsulate le texte chiffré et générer une autre clé partagée.
	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=
```

On constate ainsi que des clés partagées de même taille sont générées !

Ce code est également disponible sur le [playground](https://go.dev/play/p/n_cxNp435Qn?v=gotip).

## Conclusion

Les spécifications de chaque algorithme, leur niveau de sécurité, ainsi que la taille des clés privées, des clés publiques, des signatures et des textes chiffrés, peuvent être résumés comme suit. Chacun affiche des dimensions imposantes, à la hauteur de son nom PQC.

| Algorithme  | Niveau de sécurité NIST | Taille de la clé privée | Taille de la clé publique | Taille signature/texte chiffré |
| ----------- | ----------------------- | ----------------------- | ------------------------- | ------------------------------ |
| 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                          |

Grâce à ces algorithmes, nous espérons pouvoir utiliser Internet en toute sécurité, même face aux ordinateurs quantiques ; toutefois, la taille accrue des clés, des signatures et des textes chiffrés rend inévitable un volume de calcul plus important.

Néanmoins, le langage Go implémente efficacement chacun de ces algorithmes ; nous espérons qu'il sera activement utilisé, à bon escient, pour protéger votre sécurité !
