Fully post-quantum OpenVPN w/OpenSSH tunneling

Fully post-quantum OpenVPN w/OpenSSH tunneling

Table of contents

  1. Fully post-quantum OpenVPN w/OpenSSH tunneling
  2. DNS ad/tracker blocking vulnerability fixed
  3. What's next?

Fully post-quantum OpenVPN w/OpenSSH tunneling

We tweeted this a few weeks ago, but most people probably don't know what this means:

We added support for post-quantum key exchanges to our SSH tunneling/obfuscation.
OpenVPN can't do PQ KEX yet, but with this you can add a PQ layer on top of your OpenVPN (TCP) traffic.
This requires a recent OpenSSH/OpenSSL version.

So we thought we'd do a quick blog post explaining it.

In https://cryptostorm.is/blog/pq-openvpn we explain how OpenSSL 3.5.0 allows us to use post-quantum algorithms with OpenVPN, but like it says on that page:

In these new ML-DSA-87 OpenVPN instances, only authentication is post-quantum. The key exchange and encryption still rely on classical algorithms

That means the new ML-DSA-87 instances only protect against quantum impersonation attacks (spoofing/MitM). To be fully post-quantum, OpenVPN needs to officially add support for post-quantum key exchanges. The key exchange is where the server (and client) agree on which symmetric keys to use to actually encrypt your traffic (AES-256-GCM in our newer ML-DSA-87 instances). Those keys are rotated every 20 minutes for perfect forward secrecy, but they use classical algorithms, so they're still susceptible to quantum attacks. 

OpenVPN might not support post-quantum key exchanges yet, but OpenSSH already does (since 2024).
In our SSH tunneling server-side config we have this line which tells OpenSSH which key exchange algorithms the server allows:

KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256

Those two at the beginning are the new post-quantum algorithms. You can find more information about sntrup761 or ML-KEM-768 on any search engine.

What all this means is that if you use our SSH tunneling feature described on https://cryptostorm.is/blog/ssh-tunnels then you can have post-quantum key exchanges in that outer SSH layer that encapsulates/obfuscates your OpenVPN traffic, as long as your OpenSSH or PuTTY version is recent. Even with OpenVPN still partially susceptible to post-quantum attacks, that won't matter because all your traffic will appear to be SSH (because it is), and that encrypted traffic is using post-quantum algorithms for both key exchange and authentication. 

Support for post-quantum key exchanges was added to OpenSSH in 9.9, which https://www.openssh.com/txt/release-9.9 says was released on 2024-09-19. That means if your OpenSSH version is 9.9 or newer, then you can make use of these post-quantum features in our SSH tunneling. Most Windows users are probably going to use PuTTY instead of OpenSSH to start an SSH tunnel (because it's easier), and according to https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ml-kem.html, they added post-quantum key exchange support in version 0.83, so if you have that or newer, then you can do a fully post-quantum tunnel.

And just a reminder: SSH tunneling on our network only works with the OpenVPN TCP configs. It doesn't work with OpenVPN UDP or WireGuard (yet). Also, with OpenSSH, you can verify that the SSH session is using these new post-quantum algorithms by running the command(s):

[df@b ~]$ ssh -v -D 127.0.0.1:1080 sshtunnel@paris.cstorm.is 2>&1 | grep "kex: algorithm"
debug1: kex: algorithm: mlkem768x25519-sha256
sshtunnel@paris.cstorm.is's password:

If you see mlkem768x25519-sha256 or sntrup761x25519-sha512 in that debug1: kex: algorithm: line, then you know it's using a post-quantum algorithm for the key exchange.

DNS ad/tracker blocking vulnerability fixed

I ran into this a few weeks ago when I was using an Android phone with the VPN turned on and the ad/tracker blocking DNS service enabled (see https://cryptostorm.is/ts). I was using an app that had some of those embedded Google ads, which normally gets blocked by our ad/tracker blocking DNS, but for some reason, a few ads were still getting through occasionally.

The problem ended up being a small window (~5 seconds) where ad/tracker hosts weren't being blocked correctly because the PowerDNS Recursor the ad/tracker blocking uses was in the process of restarting, because it has to restart whenever it updates to the latest ad/tracker host lists from https://github.com/hagezi/dns-blocklists/?tab=readme-ov-file#proplus

To close that small window, I changed our current DNS setup of 2 PowerDNS Recursors (one ad/tracker blocking, one unfiltered) so that for the ad/tracker blocking one dnsdist is used instead to balance between two separate ad/tracker blocking PowerDNS Recursor instances. The purpose of dnsdist here is that it has the ability to drain all connections from one of those recursors so that they go to the 2nd one, then with no clients using the 1st one, it can safely be restarted. Once it's done, dnsdist does the opposite, it drains all connections to the 2nd one and sends everyone to the 1st one so that the 2nd one can be gracefully restarted. This completely closes that ~5 second window.

What's next?

We started working on adding Xray support to the network a few months ago. That will allow our clients to obfuscate their VPN traffic so that it looks like HTTPS traffic, but unlike stunnel in our current HTTPS obfuscation setup, Xray better mimics the HTTPS that a real browser would generate (same JA3 hashes and all), and it works with WireGuard (and OpenVPN UDP, and technically OpenVPN TCP too but that would be slow).

It's actually implemented on the servers now and tests with v2rayN on Windows/Linux/Mac have succeeded, but we haven't announced it yet because we're still testing on mobile devices and something might change later in the server-side setup. 

We couldn't find an existing Android Xray app that exposes the specific features of https://github.com/XTLS/Xray-core that we needed (dokodemo-door inbound, reality outbound), so we started working on our own Android app. All the existing apps did a local SOCKS proxy or a full VpnService style VPN, which won't work in our Xray setup. The idea is that the user would have WireGuard already installed and loaded with cryptostorm configs like normal, then they start our app, which starts an xray instance listening on 127.0.0.1:31337 and connects to whichever server they want to connect to. Then once Xray is running, they switch back to the WireGuard app and edit the config for the server they want to connect to (has to be the same as the one they selected in our app), then they change the Endpoint to 127.0.0.1:31337, and they use WireGuard's built-in app exclusion feature to exclude our app from WireGuard's VPN. That app exclusion is required, otherwise a recursive routing loop would occur when WireGuard tries to send our Xray traffic through the VPN.

At the moment, it looks something like this (in Android Studio):

The core code is pretty much done now, we've got it routing everything correctly, Wireshark/tcpdump show the device is sending out TLSv1.3 "HTTPS" traffic. Now we just need to tweak the UI a bit and clean up some other things that aren't related to Xray (like something that auto-updates the server list, but only after you connect to the VPN). It uses hard-coded IPs since our entry IPs don't change often, and that avoids having to do DNS resolution. There's also a "Custom IP" option in the server list dropdown menu that might be useful to people who want to do their own WireGuard+Xray setup using their own servers. 

We're probably still a month or two away from releasing this though. Of course, we plan on releasing the source code once it's finished.

Posted on