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