OpenSSH 10.2 warns you when a connection isn’t using post-quantum key exchange. On the client side (macOS), it works out of the box. On the server side, you might need to upgrade OpenSSH, which on Ubuntu 18.04 means building from source.
The warning Link to heading
I SSH’d into my Linux box and got this:
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
The warning comes from the client. OpenSSH 10.2 shows it when the negotiated key exchange algorithm is classical, which usually means the server doesn’t support post-quantum algorithms.
Why it matters Link to heading
Classical key exchange algorithms like Curve25519 rely on the difficulty of the elliptic-curve discrete logarithm problem. A quantum computer powerful enough to run Shor’s algorithm could break that. “Harvest now, decrypt later” is the concern: an adversary records your encrypted SSH traffic today and decrypts it once quantum hardware catches up.
Post-quantum key exchange protects session data on the wire, which is the part vulnerable to future quantum attacks. Your identity key (ed25519, RSA) authenticates you in a one-time challenge-response. Cracking your public key later doesn’t let someone replay that authentication.
The client side Link to heading
My macOS client (OpenSSH 10.2) already offered PQ algorithms by default. You can verify this:
ssh -Q kex | grep -E 'mlkem|sntrup'
# sntrup761x25519-sha512
# sntrup761x25519-sha512@openssh.com
# mlkem768x25519-sha256
To control the preference order, add a KexAlgorithms line to ~/.ssh/config:
Host *
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org
SSH negotiates the first algorithm both sides support. The classical entries at the end are fallbacks for servers that don’t support PQ yet.
Both PQ options are hybrids, combining a post-quantum algorithm with classical X25519. If the PQ part turns out to have a flaw, the classical part still protects you. If quantum computers break the classical part, the PQ part protects you. Belt and braces.
mlkem768x25519-sha256 is the NIST ML-KEM standard (originally CRYSTALS-Kyber). sntrup761x25519-sha512 is Streamlined NTRU Prime, which has been in OpenSSH since version 8.5. Battle-tested but not NIST-standardised.
The server side Link to heading
My server was running OpenSSH 7.6 on Ubuntu 18.04. PQ key exchange needs at least OpenSSH 8.5 (for sntrup761) or 10.x (for ML-KEM). Ubuntu 18.04’s package repositories top out at 7.6, so building from source was the only option.
The build dependencies are minimal:
sudo apt-get install -y build-essential libssl-dev zlib1g-dev libpam0g-dev
Download and build OpenSSH 10.2:
cd ~/build
curl -LO https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-10.2p1.tar.gz
tar xzf openssh-10.2p1.tar.gz
cd openssh-10.2p1
./configure --prefix=/usr/local --sysconfdir=/etc/ssh --with-pam
make -j$(nproc)
sudo make install
Important: don’t run ./configure in /tmp if it’s mounted with noexec. The configure script compiles and runs test programs, which will fail with an unhelpful “cannot run C compiled programs” error.
The --sysconfdir=/etc/ssh flag reuses the existing config and host keys. make install won’t overwrite sshd_config, ssh_config, or moduli if they already exist.
Switching systemd to the new binary Link to heading
Create a systemd override rather than editing the system unit file. This is clean to revert.
sudo mkdir -p /etc/systemd/system/ssh.service.d
sudo tee /etc/systemd/system/ssh.service.d/override.conf > /dev/null << 'EOF'
[Service]
ExecStartPre=
ExecStartPre=/usr/local/sbin/sshd -t
ExecStart=
ExecStart=/usr/local/sbin/sshd -D $SSHD_OPTS
ExecReload=
ExecReload=/usr/local/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
EOF
The empty ExecStartPre=, ExecStart=, and ExecReload= lines clear the values from the base unit before setting new ones. Without them, systemd appends rather than replaces.
sudo systemctl daemon-reload
sudo systemctl restart ssh
Keep your current SSH session open while doing this. If something goes wrong with the new sshd, you still have a way in to fix it. Test with a second connection before closing the first.
Verify Link to heading
ssh -v yourserver 2>&1 | grep 'kex:'
# kex: algorithm: mlkem768x25519-sha256
Reverting Link to heading
If you need to go back to the system OpenSSH:
sudo rm /etc/systemd/system/ssh.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart ssh
What this doesn’t protect Link to heading
Your identity key (ed25519 or RSA) isn’t post-quantum. But identity key compromise requires a harder attack: the adversary needs a quantum computer, your public key, and an active connection, all at once. ML-KEM secures the key exchange, which protects your data in transit.
OpenSSH doesn’t support post-quantum signature algorithms for identity keys yet.
Further reading Link to heading
- OpenSSH 9.0 release notes — first release to prefer NTRU Prime hybrid key exchange by default
- NIST FIPS 203 (ML-KEM) — the ML-KEM standard
- Post-quantum cryptography on Wikipedia