$RodHat_
MOTD

ML-KEM has been the default SSH key exchange for two years. Enterprise IT just noticed.

Published by

ML-KEM has been the default SSH key exchange for two years. Enterprise IT just noticed.
Photo: AI-generated — no human photographer / RodHat AI Cover

The helpdesk tickets have been trickling in for months. Now they are a flood. SSH connections from corporate jump boxes to anything running a current distro are timing out on the key exchange, falling back slowly, sometimes failing outright with no useful error. The root cause is OpenSSH 9.9, which shipped in October 2024 and made mlkem768x25519-sha256 the default key exchange algorithm.

Two years ago. The change happened two years ago.

What actually changed

The SSH protocol negotiates the key exchange algorithm during the initial handshake. Client and server each advertise what they support, pick the highest preference they share, and proceed. OpenSSH 9.9 put ML-KEM hybrid first on both sides. A client running 9.9 or later connecting to a server running 9.9 or later negotiates mlkem768x25519-sha256 and gets on with it.

The problem is one side being old. If your jump box is still on OpenSSH 8.x from a corporate Ubuntu 20.04 image that nobody has touched since the LTS rollout, it does not know what ML-KEM is. Negotiation falls back to the next mutually supported algorithm, usually curve25519-sha256 or an ECDH variant. That is fine. Fallback negotiation is working exactly as designed.

Except some sites hardcode their KexAlgorithms line to a specific list written circa 2019. Some old versions of PuTTY, some corporate SSH gateway appliances, some embedded SSH stacks in network hardware do not negotiate gracefully. They send their preference list, the remote end offers mlkem768x25519-sha256 first, and something in the parsing logic on the old end breaks in a way that produces no useful error. Connection drops. Helpdesk ticket opens.

Why ML-KEM is the correct default

The threat is called “harvest now, decrypt later.” Nation-state actors with sufficient storage capture encrypted sessions today and decrypt them once a capable enough quantum computer exists. RSA and ECDH are quantum-vulnerable. The ciphertext is safe for now, but “now” has an expiration date that is not zero. If you are operating SSH infrastructure in a context where adversaries are plausibly recording your traffic, that matters.

ML-KEM is NIST FIPS 203, standardized in August 2024: the Module-Lattice-based Key Encapsulation Mechanism. OpenSSH uses a hybrid: mlkem768x25519-sha256 combines ML-KEM-768 with classical X25519. Both algorithms have to be broken simultaneously to break the session key. If ML-KEM’s lattice assumptions turn out to be wrong, X25519 holds. If a quantum computer eventually breaks X25519, ML-KEM holds. You get post-quantum security without abandoning the classical security proof.

NIST published FIPS 203. OpenSSH waited for the final standard before flipping the default. This is the correct sequence. The timeline was not rushed.

Finding the breakage

On a server, check your effective running configuration:

sshd -T | grep kex

If kexalgorithms does not include mlkem768x25519-sha256 near the front, your sshd_config has an explicit KexAlgorithms line that predates 9.9. Drop the line entirely and let the OpenSSH default take over. Removing an overly specific KexAlgorithms line is almost always the correct fix.

On a client, check what your installed version actually supports:

ssh -Q kex

If mlkem768x25519-sha256 is absent, your SSH client is the compatibility problem. Update it.

For diagnosing a specific failing connection:

ssh -vvv user@host 2>&1 | grep -E 'kex|KEX|algo'

The verbose output shows exactly what each side offered and where negotiation went sideways.

For situations where you cannot update the remote end immediately, force a specific algorithm on the client:

ssh -o KexAlgorithms=curve25519-sha256 user@host

This is a workaround, not a fix. Update the other end.

Who owns this

The OpenSSH project documented the change in the 9.9 release notes, ran a multi-version transition with support added well before it became the default, and updated on a schedule that is not aggressive by any reasonable measure. Major distros packaged it promptly.

Corporate environments running three-year-old SSH infrastructure without a patching process own this one entirely. The tickets hitting helpdesk right now are not evidence that OpenSSH got the transition wrong. They are a measurement of how far behind corporate patching culture has drifted relative to the actual threat model.

Update your SSH clients and servers. Drop hardcoded KexAlgorithms lines from configs that predate 9.9. Run sshd -T | grep kex and confirm mlkem768x25519-sha256 is in the list. This is not complicated. The complicated part was the last twenty years of pretending harvest-now-decrypt-later was somebody else’s problem.

See also: OpenSSL 3.0 EOL and what it means for your crypto stack for the adjacent conversation about keeping cryptographic libraries current, and seccomp-bpf syscall filtering for hardening the process boundary around the services your SSH sessions reach.

Sources

  1. OpenSSH 9.9 release notes (OpenSSH)
  2. FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard (National Institute of Standards and Technology)