Resposta curta. O X25519MLKEM768 é um grupo de troca de chaves do TLS 1.3, com ponto de código 0x11EC (4588), que executa o ML-KEM-768 e o X25519 lado a lado e leva os dois segredos compartilhados para o escalonamento de chaves (key schedule). Ele é padronizado pela RFC 10024 (agosto de 2026). O Chrome, o Firefox, as APIs de rede da Apple nas versões 26 dos sistemas, o OpenSSL 3.5, o Go 1.24+ e o JDK 27 o oferecem por padrão. Rode openssl s_client -connect example.com:443 -brief com o OpenSSL 3.5 e procure Negotiated TLS1.3 group: X25519MLKEM768. Ele protege o tráfego gravado contra um futuro computador quântico. Para a autenticação, que continua apoiada em certificados RSA ou ECDSA, ele não faz nada.
O que o nome diz
O TLS 1.3 negocia a troca de chaves por meio de named groups. O cliente lista os grupos que aceita na extensão supported_groups e envia chaves públicas para um ou mais deles em key_share. O servidor escolhe um grupo e responde com o próprio key share. Durante anos, as opções realistas foram X25519 e ECDH com P-256. Os dois caem diante do algoritmo de Shor em um computador quântico grande o bastante, e o tráfego gravado hoje pode ser descriptografado assim que essa máquina existir. Esse é o problema do coletar agora, descriptografar depois (harvest now, decrypt later).
Um grupo híbrido resolve esse problema sem apostar tudo em matemática nova. O X25519MLKEM768 executa dois acordos de chaves em uma única negociação:
- ML-KEM-768, o mecanismo de encapsulamento de chave baseado em reticulados de módulo do FIPS 203 (agosto de 2024), com os parâmetros da categoria 3. Essa parte resiste a ataques quânticos.
- X25519, a função Diffie-Hellman de curva elíptica da RFC 7748, que tem um longo histórico de criptoanálise e serve de respaldo para o componente mais novo.
A construção genérica está descrita na RFC 9954 (Hybrid Key Exchange in TLS 1.3, julho de 2026), cujo objetivo declarado é a segurança “mesmo que se encontre uma forma de quebrar a criptografia de todos os algoritmos componentes, exceto um”. Os grupos concretos estão definidos na RFC 10024 (agosto de 2026):
| Grupo | Ponto de código | Recomendado | Componentes |
|---|---|---|---|
| X25519MLKEM768 | 0x11EC (4588) | Sim | ML-KEM-768 + X25519 |
| SecP256r1MLKEM768 | 0x11EB (4587) | Não | ECDH P-256 + ML-KEM-768 |
| SecP384r1MLKEM1024 | 0x11ED (4589) | Não | ECDH P-384 + ML-KEM-1024 |
Capturas mais antigas podem mostrar X25519Kyber768Draft00, ponto de código 0x6399 (25497). Era o híbrido pré-padrão com Kyber que o Chrome lançou primeiro. O FIPS 203 mudou o algoritmo em detalhes pequenos, mas incompatíveis, e por isso o Chrome 131 o substituiu por 0x11EC e removeu o grupo do rascunho. O anúncio do Google explica por que o Chrome não ofereceu suporte aos dois: “A criptografia pós-quântica é grande demais para que seja possível oferecer duas previsões de key share pós-quânticas ao mesmo tempo”. A RFC 10024 marca os pontos de código do rascunho como obsoletos.
Por dentro do key share
O key share híbrido é uma simples concatenação dos dois valores componentes, sem prefixos de comprimento internos. Os tamanhos são fixados pelo FIPS 203 e pela RFC 7748, então cada campo pode ser verificado só pelo comprimento:
| Campo | Estrutura | Bytes |
|---|---|---|
key_exchange do cliente | Chave de encapsulamento ML-KEM-768 (1.184) ‖ chave pública X25519 (32) | 1.216 |
key_exchange do servidor | Texto cifrado ML-KEM-768 (1.088) ‖ chave pública X25519 (32) | 1.120 |
| Segredo compartilhado | Segredo compartilhado ML-KEM (32) ‖ segredo compartilhado X25519 (32) | 64 |
Repare na ordem. O nome começa com X25519, mas os bytes começam com ML-KEM. A RFC 10024 diz que a ordem “foi invertida” em relação à convenção de nomes “por razões históricas”. A seção 5 da RFC também dá um motivo de conformidade: a NIST SP 800-56C só permite um segredo combinado em uma derivação de chaves aprovada pelo FIPS quando a primeira entrada vem de um esquema aprovado, e, neste par, esse esquema é o ML-KEM. Os dois híbridos com curvas do NIST colocam o key share ECDH primeiro pelo mesmo motivo. Um parser que divide o campo no byte 32, e não no byte 1.184, falha sempre.
Este é um ClientHello real do OpenSSL 3.5.7 para cloudflare.com, capturado com -trace em 20 de outubro de 2026 (com os bytes das chaves encurtados):
Sent TLS Record
Content Type = Handshake (22)
Length = 1551
ClientHello, Length=1547
extension_type=supported_groups(10), length=18
X25519MLKEM768 (4588)
ecdh_x25519 (29)
secp256r1 (P-256) (23)
ecdh_x448 (30)
secp384r1 (P-384) (24)
secp521r1 (P-521) (25)
ffdhe2048 (256)
ffdhe3072 (257)
extension_type=key_share(51), length=1258
NamedGroup: X25519MLKEM768 (4588)
key_exchange: (len=1216): A4696B297C695DEA602FB55157BC359F...
NamedGroup: ecdh_x25519 (29)
key_exchange: (len=32): 919D957E3D7E402ECF27C3DD0B38DDA0...
O cliente envia dois key shares: o híbrido e um X25519 puro. Esse é o padrão do OpenSSL 3.5. O Go 1.27.3 envia o mesmo par, assim como o Chromium 156, que acrescenta uma entrada GREASE e chega em um registro TLS de 1.704 bytes. A JEP 527 especifica o mesmo par para o JDK 27. O key share puro custa 32 bytes e permite que um servidor que não conhece 0x11EC conclua o handshake em uma única ida e volta, em vez de pedir outro key share. As contas da extensão key_share fecham exatamente: 2 bytes de comprimento da lista, depois 2 + 2 + 1.216 para a entrada híbrida e 2 + 2 + 32 para o X25519, 1.258 no total. O servidor escolheu o híbrido:
ServerHello, Length=1206
extension_type=key_share(51), length=1124
NamedGroup: X25519MLKEM768 (4588)
key_exchange: (len=1120): C01BC77BB1F9985B0DE82CDD1D0DD14B...
Para comparação, o mesmo cliente com -groups X25519 envia um ClientHello de 313 bytes. O híbrido acrescenta cerca de 1,2 KB em cada direção, e esse tamanho é a causa da maioria dos problemas de compatibilidade (veja abaixo).
De dois segredos para um escalonamento de chaves
Cada lado faz duas operações independentes:
- O cliente gera um par de chaves ML-KEM-768 e um par de chaves X25519 e envia as duas chaves públicas.
- O servidor encapsula para a chave ML-KEM. Ele obtém um segredo de 32 bytes e um texto cifrado de 1.088 bytes. Ele também executa o X25519 com a própria chave efêmera e depois envia o texto cifrado e sua chave pública X25519.
- O cliente desencapsula o texto cifrado para recuperar o mesmo segredo ML-KEM e conclui o X25519 do seu lado.
- Os dois lados concatenam os resultados em 64 bytes e usam esse valor onde o TLS 1.3 normalmente coloca o segredo (EC)DHE: como entrada do HKDF-Extract que deriva o segredo do handshake (RFC 9846 §7.1, a revisão do TLS 1.3 de julho de 2026, que torna a RFC 8446 obsoleta).
Tudo o que vem depois é TLS 1.3 comum. As cipher suites, o certificado, o CertificateVerify e as chaves de tráfego não mudam. A RFC 10024 acrescenta passos de validação que as implementações devem executar. Elas verificam a chave de encapsulamento como o FIPS 203 exige, rejeitam um texto cifrado com o comprimento errado e abortam se o resultado do X25519 for composto só de zeros. Cada falha encerra o handshake com um alerta illegal_parameter ou internal_error.
O argumento de segurança é curto. Um atacante que grava o handshake precisa recuperar os dois segredos para calcular o segredo do handshake. Quebrar o X25519 com um futuro computador quântico não basta enquanto o ML-KEM resistir. Uma surpresa criptoanalítica contra o esquema de reticulados, ainda jovem, não basta enquanto o X25519 resistir. Se você quiser ver o encapsulamento e o desencapsulamento com chaves reais, o laboratório de ML-KEM executa os dois no seu navegador.
Quem o ativa por padrão
Em outubro de 2026, todos os principais clientes o oferecem por padrão:
| Software | Padrão desde | Fonte |
|---|---|---|
| Chrome para desktop | Chrome 131, no lugar do grupo do rascunho do Kyber | Blog de segurança do Google |
| Firefox | Com suporte desde o Firefox 132 (TLS) e o Firefox 135 (HTTP/3). A preferência security.tls.enable_kyber vem como true por padrão | Notas de versão |
| Apple: URLSession e framework Network | As versões 26: iOS, iPadOS, macOS Tahoe, visionOS (guia de rede), além de tvOS e watchOS (guia de segurança) | Suporte da Apple, Segurança das Plataformas |
| OpenSSL | 3.5.0: grupos híbridos na lista padrão, key shares padrão X25519MLKEM768 + X25519 | NEWS.md |
Go crypto/tls | Go 1.24, quando Config.CurvePreferences é nil. O Go 1.26 acrescenta os dois híbridos com curvas do NIST | Go 1.24, Go 1.26 |
| Java (JSSE) | JDK 27, JEP 527: X25519MLKEM768 em primeiro lugar na lista padrão | JEP 527 |
Qualquer software vinculado ao OpenSSL 3.5 também herda o padrão, desde que a própria configuração não substitua a lista de grupos. Isso inclui os pacotes do Debian 13 e o Node.js 24, que embute o próprio OpenSSL 3.5. No Debian 13, o módulo ssl do Python concluiu um handshake com um openssl s_server -groups X25519MLKEM768 que não aceita mais nada, e o curl e o nginx negociaram o híbrido sem configuração extra. A página Segurança do TLS da Apple aponta uma lacuna: “clientes TLS que usam as APIs SecureTransport não podem usar o TLS 1.3”, então os aplicativos que ainda usam essa API ficam sem troca de chaves híbrida.
No lado do servidor, uma grande parte do tráfego HTTPS já termina em stacks que escolhem o híbrido quando o cliente o oferece. Um teste feito com o OpenSSL 3.5.7 em 20 de outubro de 2026 negociou X25519MLKEM768 com cloudflare.com, google.com, www.amazon.com, www.microsoft.com, www.apple.com e example.com, e X25519 puro com github.com e letsencrypt.org.
Verifique um servidor, um cliente, um navegador
Na linha de comando. A saída de -brief do OpenSSL 3.5 informa o grupo diretamente:
$ openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -brief </dev/null
Connecting to 104.16.132.229
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=cloudflare.com
Hash used: SHA256
Signature type: ecdsa_secp256r1_sha256
Verification: OK
Negotiated TLS1.3 group: X25519MLKEM768
DONE
Com um servidor que escolhe um grupo clássico, a saída mostra Peer Temp Key: X25519, 253 bits no lugar dessa linha. Para testar se um servidor consegue negociar o híbrido, não ofereça mais nada. Com um servidor apenas clássico, o handshake então falha:
$ openssl s_client -groups X25519MLKEM768 -connect github.com:443 -servername github.com -brief </dev/null
...:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:...:SSL alert number 40
Use essa verificação apenas em testes. Um cliente real que oferece só o híbrido abre mão de compatibilidade sem ganhar nada. O curl compilado com o OpenSSL 3.5 mostra o grupo na linha de conexão do modo verboso:
$ curl -sv -o /dev/null https://www.google.com/ 2>&1 | grep 'SSL connection'
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey
No código. O Go 1.25 e posteriores expõem o grupo negociado como ConnectionState().CurveID, e o Node.js 24 o informa por meio de getEphemeralKeyInfo():
conn, err := tls.Dial("tcp", "cloudflare.com:443", &tls.Config{ServerName: "cloudflare.com"})
if err != nil {
log.Fatal(err)
}
fmt.Println(conn.ConnectionState().CurveID) // X25519MLKEM768 (Go 1.27.3)
import tls from "node:tls";
const socket = tls.connect({ host: "cloudflare.com", port: 443, servername: "cloudflare.com" }, () => {
console.log(socket.getEphemeralKeyInfo()); // { type: 'TLSGroup', name: 'X25519MLKEM768', size: undefined } (Node 24.22.0)
socket.end();
});
No navegador. O Chrome mostra o grupo no DevTools, nos detalhes da conexão do painel de segurança. O mesmo valor fica disponível de forma programática como securityDetails.keyExchangeGroup no evento Network.responseReceived do protocolo do DevTools. Em 20 de outubro de 2026, o Chromium 156 em modo headless informou X25519MLKEM768 para www.cloudflare.com e X25519 para github.com. No macOS 26, a Apple sugere nscurl --tls-diagnostics https://your.host. Para ver o que seu navegador envia, abra a verificação pós-quântica do navegador.
Como ativar no seu servidor
Com o OpenSSL 3.5 por baixo, o padrão já está certo. O jeito mais comum de perdê-lo é uma lista de grupos explícita copiada de um guia de hardening antigo. Este teste com o nginx 1.30.5 no Debian 13 (OpenSSL 3.5.7) altera apenas a linha ssl_ecdh_curve e verifica o resultado com openssl s_client -brief:
--- no ssl_ecdh_curve (OpenSSL default list)
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768
--- ssl_ecdh_curve X25519:prime256v1:secp384r1;
Protocol version: TLSv1.3
Peer Temp Key: X25519, 253 bits
--- ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1:secp384r1;
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768
A correção, portanto, é uma destas: remover a linha ou colocar X25519MLKEM768 no início dela. Versões mais antigas do OpenSSL não conhecem esse nome, então atualize primeiro a biblioteca TLS. O nginx 1.24.0 com o OpenSSL 1.1.1w rejeita uma linha assim (aqui, ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;) com nginx: [emerg] SSL_CTX_set1_curves_list("X25519MLKEM768:X25519:prime256v1") failed. Outros servidores seguem a mesma lógica com outros nomes: SSLOpenSSLConfCmd Groups no Apache httpd, curves no HAProxy, CurvePreferences no Go, jdk.tls.namedGroups no Java. Se você precisa de um ponto de partida para um servidor específico, o gerador de configurações TLS cria configurações com os grupos híbridos já incluídos.
Dois outros lugares são fáceis de esquecer:
- A origem atrás de uma CDN ou de um balanceador de carga. A conexão do navegador termina na borda, e a conexão da borda com sua origem é uma sessão TLS separada, com negociação própria. O tráfego gravado nesse segundo trecho pode ser coletado do mesmo jeito.
- TLS interno entre serviços. Os serviços em Go recebem o híbrido pela versão da toolchain. Os serviços em Java o recebem a partir do JDK 27, versão em que a JEP 527 adicionou os grupos híbridos. Tudo o que fixa
CurvePreferences,jdk.tls.namedGroupsou outra lista explícita de grupos fica sem nada até que alguém a edite.
Previsão de key share e HelloRetryRequest
Um cliente TLS 1.3 precisa adivinhar qual grupo o servidor vai escolher e enviar um key share para ele logo de início. Se errar, o servidor responde com um HelloRetryRequest que indica o grupo desejado, e o cliente começa de novo. Isso custa uma ida e volta completa. O palpite importa mais com key shares pós-quânticos, porque enviar um key share de 1.216 bytes “só por garantia” não sai de graça. Dá para ver a nova tentativa acontecer pedindo ao OpenSSL que liste o híbrido, mas envie um key share só para o X25519:
ClientHello, Length=315
extension_type=key_share(51), length=38
NamedGroup: ecdh_x25519 (29)
ServerHello, Length=84 <- HelloRetryRequest
extension_type=key_share(51), length=2
NamedGroup: X25519MLKEM768 (4588)
ClientHello, Length=1499
extension_type=key_share(51), length=1222
NamedGroup: X25519MLKEM768 (4588)
ServerHello, Length=1206
Negotiated TLS1.3 group: X25519MLKEM768
O servidor preferiu o híbrido mesmo com o cliente listando o X25519 primeiro, e por isso pediu uma nova tentativa. Nas strings de grupos do OpenSSL 3.5, o prefixo * marca os grupos que recebem um key share já na primeira leva de mensagens. Com -groups '*X25519:*X25519MLKEM768', o cliente envia os dois key shares, em um ClientHello de 1.535 bytes, e evita a nova tentativa. Os padrões dos principais clientes enviam os dois pelo mesmo motivo. Um rascunho do grupo de trabalho TLS, o draft-ietf-tls-key-share-prediction, permite que os servidores publiquem seus grupos preferidos em registros DNS do tipo HTTPS, para que os clientes acertem logo na primeira vez. Em outubro de 2026, ele ainda era um Internet-Draft.
O que quebra: ClientHellos grandes e middleboxes antigos
O ClientHello medido acima ocupa um registro TLS com 1.551 bytes de payload. Somados os cabeçalhos do registro, do TCP e do IP, ele já não cabe em um único pacote em um caminho com a MTU típica de 1.500 bytes. A especificação sempre permitiu isso, mas algumas implementações partiam do princípio de que a primeira chamada read() no socket retorna o ClientHello inteiro. Elas quebram quando o híbrido o divide em dois segmentos.
O projeto tldr.fail acompanha essa classe de bugs. Ele descreve servidores “não preparados para ter de chamar read() mais de uma vez para ler o ClientHello inteiro”. A lista do projeto inclui proxies que inspecionam TLS, firewalls e balanceadores de carga de vários fornecedores. Alguns já receberam correção, e outros estão marcados como desconhecidos. Os sintomas parecem problema de rede, não de criptografia: ERR_CONNECTION_RESET ou ERR_SSL_PROTOCOL_ERROR no Chrome, timeouts no handshake ou conexões derrubadas logo depois do ClientHello. Muitas vezes o bug só aparece para clientes que enviam o híbrido. A Apple alerta para a mesma falha nas notas das versões 26: os dispositivos podem não conseguir acessar alguns servidores mais antigos cujo código TLS “não consegue ler mensagens ClientHello grandes”.
Se você esbarrar nesse problema:
- Encontre o equipamento que derruba a conexão. Compare
openssl s_client -groups X25519com a lista de grupos padrão, pelo mesmo caminho. Se só o ClientHello grande falhar, o problema é o tamanho, não o certificado. - Corrija ou substitua esse equipamento. Desativar o híbrido nos clientes desliga a proteção em todas as conexões, inclusive nas que funcionavam.
- Use as opções do lado do cliente só como paliativo. O Go tem
GODEBUG=tlsmlkem=0, e as notas do Go 1.24 a mencionam para “servidores TLS com bugs que não lidam corretamente com registros grandes”. O Chrome tinha a política corporativaPostQuantumKeyAgreementEnabled, que o Google descreveu como “uma medida temporária” que “será removida em algum momento depois da versão 145 do Google Chrome”. Verifique se sua versão do Chrome ainda respeita essa política antes de contar com ela nos seus planos.
Proxies que fazem a terminação TLS merecem mais uma observação. Um proxy que descriptografa o tráfego executa dois handshakes. O que seu notebook negocia com o proxy não diz nada sobre a conexão do proxy com o mundo externo. Esse trecho só é pós-quântico se a própria stack TLS do proxy oferecer suporte ao híbrido e der preferência a ele.
O que ele não protege
O X25519MLKEM768 protege a confidencialidade contra um adversário que grava o tráfego agora e o descriptografa depois. Ele não torna pós-quântica a autenticação do handshake. O servidor continua provando sua identidade com um certificado RSA ou ECDSA e uma assinatura CertificateVerify. Alguém capaz de falsificar essas assinaturas com um computador quântico poderia se passar por um servidor em uma conexão em tempo real. Essa ameaça só passa a existir quando um computador assim existir, e é por isso que a troca de chaves migrou primeiro e as assinaturas são uma migração separada e mais lenta. Os outros artigos desta série tratam desse trabalho: chaves e assinaturas ML-DSA em ML-DSA para desenvolvedores, e o plano geral, incluindo inventário e prazos, em Migração pós-quântica em 2026.
Ele também não faz nada pelo tráfego que não é TLS 1.3. Conexões TLS 1.2, sessões SSH e túneis VPN têm as próprias trocas de chaves e os próprios caminhos de atualização. Descubra quais das suas conexões ainda negociam TLS 1.2 antes de considerá-las protegidas.
Fontes
- RFC 10024: Post-Quantum Traditional (PQ/T) Hybrid Key Agreement Mechanisms for TLS 1.3 (agosto de 2026). Definições dos grupos, ordem dos bytes, validação, pontos de código da IANA.
- RFC 9954: Hybrid Key Exchange in TLS 1.3 (julho de 2026).
- FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard (agosto de 2024).
- RFC 9846 §7.1: o escalonamento de chaves do TLS 1.3.
- Blog de segurança do Google, Um novo caminho para o Kyber na web (setembro de 2024).
- Suporte da Apple, Prepare sua rede para a criptografia com segurança quântica no TLS, e o guia Segurança das Plataformas da Apple.
- NEWS.md do OpenSSL 3.5, notas de versão do Go 1.24 e 1.26, JEP 527.
- tldr.fail: o bug do ClientHello grande e os produtos afetados.
Toda a saída de comandos desta página vem de contêineres executados em 20 de outubro de 2026: OpenSSL 3.5.7, curl 8.14.1, Node.js 24.22.0 e o Python 3 do Debian em node:24.22.0-trixie, Go 1.27.3 em golang:1.27.3-trixie, nginx 1.30.5 em nginx:1.30.5, nginx 1.24.0 com OpenSSL 1.1.1w em nginx:1.24.0 e Chromium 156 em mcr.microsoft.com/playwright:v1.64.1-noble.