GoSuda

Uso de MLDSA e MLKEM na linguagem Go

By snowmerak
views ...

Visão Geral

Contexto

Há já algum tempo, a capacidade de computação rápida dos computadores quânticos tem sido reconhecida como uma ameaça aos sistemas de criptografia existentes. Isso ocorre porque algoritmos como RSA e ECC podem ser decifrados pela capacidade computacional dos computadores quânticos. No entanto, à medida que o conceito de computadores quânticos começou a se materializar há alguns anos, alternativas começaram a ser pesquisadas e desenvolvidas, e o NIST tem trabalhado na padronização de PQC (Criptografia Resistente a Quântica).

MLDSA e MLKEM

Finalmente, em agosto de 2024, o NIST adotou MLKEM e MLDSA, baseados em CRYSTALS-Kyber e CRYSTALS-Dilithium, como padrões. Ambos os algoritmos operam com base no problema MLWE (Module Learning with Errors). Essa forma é chamada de criptografia baseada em reticulados.

A criptografia baseada em reticulados, como o nome sugere, é um sistema de criptografia baseado na dificuldade de problemas matemáticos em reticulados. Eu mesmo não tenho um conhecimento matemático profundo sobre o assunto, mas, resumidamente, é o problema de resolver equações lineares com ruído em um reticulado de módulos. Não tenho ideia do quão difícil isso é, mas esse problema é considerado difícil o suficiente para não ser resolvido nem mesmo por computadores quânticos.

MLDSA

Então, vamos primeiro aprender sobre o MLDSA.

Composição

O MLDSA, como o nome sugere, é um algoritmo de assinatura assimétrica que passa por um total de duas etapas:

  1. Geração de assinatura: Geração de uma assinatura para uma mensagem usando uma chave privada.
  2. Verificação de assinatura: Verificação da validade da assinatura gerada usando uma chave pública.

Além disso, o MLDSA tem três características:

  1. forte não falsificabilidade existencial: Não é possível gerar outra assinatura válida com uma assinatura e uma chave pública.
  2. ataque de mensagem escolhida: Não é possível gerar uma nova assinatura válida com uma chave pública a partir de uma assinatura para qualquer mensagem.
  3. ataque de canal lateral: A segurança é alta porque novos valores aleatórios e valores pseudoaleatórios derivados da mensagem são usados ​​continuamente ao assinar.
  4. separação de domínio: Evita problemas de segurança repetitivos, permitindo que diferentes seeds sejam usadas para diferentes parâmetros.

Código

Agora, vou mostrar um exemplo de código simples na linguagem Go. Este exemplo usou o 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	// Cria uma chave com a especificação mldsa44.
14	pub, priv, err := mldsa44.GenerateKey(rand.Reader)
15	if err != nil {
16		panic(err)
17	}
18
19	message := []byte("Olá, Mundo!")
20
21	// Gera uma assinatura.
22	// Um ponto a notar é que, de acordo com a versão atual em 22 de dezembro de 2024, um erro ocorre se não for 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	// Chama o esquema da chave pública para realizar a verificação.
32	ok := pub.Scheme().Verify(pub, message, signature, nil)
33	fmt.Println(ok)
34}
13228 oaSaOA-...
2true

O valor da assinatura foi omitido porque era muito longo. Se você quiser ver o texto completo, execute-o no playground.

Embora tenha sido codificado em base64, é um pouco oneroso ter 3228 bytes. É um pouco oneroso pensar que em breve talvez tenhamos que trocar este tamanho como assinatura contra computadores quânticos.

MLKEM

Composição

MLKEM é um Mecanismo de Encapsulamento de Chaves (Key Encapsulation Mechanism). KEM é um algoritmo que permite que duas partes gerem uma chave compartilhada usando criptografia de chave pública. O mecanismo de troca de chaves do MLKEM passa pelo seguinte processo:

  1. Encapsulamento de chave: O remetente usa a chave pública do destinatário para gerar uma mensagem criptografada (cipher text) e uma chave compartilhada. Essa mensagem criptografada é inicialmente entregue ao destinatário para uso.
  2. Desencapsulamento de chave: O destinatário usa sua chave privada para extrair a chave compartilhada da mensagem criptografada.

Existem um total de três parâmetros no MLKEM. Existem MLKEM-512, MLKEM-768 e MLKEM-1024. Quanto menor, menor a chave e o texto criptografado. Quanto maior, maior a chave e o texto criptografado e maior o nível de segurança.

Código

O MLKEM será adicionado ao Go 1.24, portanto, usei o Go 1.24rc1, que está disponível no momento.

 1package main
 2
 3import (
 4	"crypto/mlkem"
 5	"encoding/base64"
 6	"fmt"
 7)
 8
 9func main() {
10	// Gera a PrivateKey do destinatário.
11	receiverKey, err := mlkem.GenerateKey1024()
12	if err != nil {
13		panic(err)
14	}
15
16	// No MLKEM, o termo EncapsulationKey é usado em vez de PublicKey.
17	receiverPubKey := receiverKey.EncapsulationKey()
18
19	// Foi clonado para mostrar que a chave pode ser extraída e reutilizada simplesmente com Bytes() e NewEncapsulationKeyX de EncapsulationKey.
20	// Claro, na realidade, se você o usar, esse processo será o processo em que o remetente cria a chave EncapsulationKey do destinatário que foi divulgada como texto como um objeto.
21	clonedReceiverPubKey, err := mlkem.NewEncapsulationKey1024(receiverPubKey.Bytes())
22	if err != nil {
23		panic(err)
24	}
25
26	// O remetente gera texto criptografado e uma chave compartilhada com Encapsulate.
27	cipherText, SenderSharedKey := clonedReceiverPubKey.Encapsulate()
28
29	// Eu deliberadamente o clonei para mostrar o armazenamento e a recuperação da chave privada do destinatário.
30	clonedReceiverKey, err := mlkem.NewDecapsulationKey1024(receiverKey.Bytes())
31	if err != nil {
32		panic(err)
33	}
34
35	// O destinatário usa a chave privada para Decapsulate o texto criptografado e gerar outra chave compartilhada.
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=

Como resultado, você pode verificar que uma chave compartilhada do mesmo tamanho é gerada!

Este código também pode ser verificado no playground.

Conclusão

As especificações, os níveis de segurança e os tamanhos de chave privada, chave pública, assinatura ou texto cifrado de cada algoritmo podem ser resumidos da seguinte forma. Cada um tem um tamanho grande, o que não é nada vergonhoso para ser chamado de PQC.

AlgoritmoNível de segurança NISTTamanho da chave privadaTamanho da chave públicaTamanho da assinatura/texto cifrado
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

Espera-se que esses algoritmos nos permitam usar a Internet de forma segura, mesmo em computadores quânticos, mas parece inevitável que haverá mais cálculos devido ao aumento relativo do tamanho das chaves e assinaturas/textos cifrados.

Ainda assim, como cada algoritmo está implementado de forma eficaz na linguagem Go, espero que seja ativamente utilizado para proteger a sua segurança em locais adequados!