RSSAmplifier

Signs of Triviality · Jul 20, 2025

Post-Quantum Cryptography Proof of Concept Implementations

0
Sign in to vote or save

netmeister.org

July 20th, 2025

Construction
workers in front of concrete road construction
vehicles in what appears to be a rural setting.More and more organizations are moving towards adopting post-quantum cryptography (PQC). In order to verify whether your systems, clients, libraries, and toolkits are able to speak PQC, it's useful to have sample implementations that provide the bare minimum functionality to let you validate compatibility. For the most part, the current focus for many is primarily on hybrid key exchange in TLS 1.3 using X25519MLKEM768, and so I had a need to determine just what that looks like in practice.

To this end, I stood up a few HTTP endpoints that speak PQC using different implementations:

The names should be reasonably self-explanatory. Where possible, I tried to extract the negotiated group1, HTTP protocol spoken, and whether or not a HelloRetryRequest (HRR) was performed. However, not all implementations or servers provide that level of detail.

A few notes on each of the endpoints:

boringssl-nginx

For this service, I built BoringSSL following these instructions using -DBUILD_SHARED_LIBS=1, then built nginx as described here.

To reflect the negotiated group and other TLS parameters, I use a simple PHP script with variables exposed as FastCGI parameters. The nginx config file can be found here.

golang

For Go, I wrote a simple TLS server myself, code here.

The one thing of interest here is that as of Go 1.24, the negotiated group is not properly exposed, and I'm extracting it from the stringified object representation. Likewise for the HRR determination. It's ugly.

The good news is that Go 1.25 will get ConnectionState.CurveID exposed, and there's no opposition to further exposing HRR in the future.

java-bc

This is another simple proof-of-concept server, this time written in Java. In order to get hybrid TLS key exchange with PQC, this requires the use of BouncyCastle version >= 1.81.

Writing this was a pain, because I normally don't write Java. I was unable to find a way to get the negotiated group from the connection without writing my own TLS stack here. After fighting for a few hours with AI that kept trying to convince me to use non-existent functions, I eventually gave up.

The endpoint at https://java-bc.pqc.dotwtf.wtf currently only offers PQC, so if you're trying to hit that with, e.g., Safari or any other client not linked against a PQC enabled TLS library, you should get an error. (By only supporting a single key group, I can also fake HRR detection, but that is, of course, a bit silly.) It also only speaks HTTP/1.x. And by "speak", I mean: "fake".

Anyway, the code is here; I included a client PoC there as well.

openssl-nginx

This endpoint uses OpenSSL 3.5 with nginx, pretty much following this write-up.

openssl-oqs-apache

This endpoint is running on the same server as this website, using 's OpenSSL oqs-provider.

For server side detection of the negotiated group, I'm cheating a bit: since I didn't see a good way to expose the TLS details from Apache, I'm using the same approach that Cloudflare uses for their https://pq.cloudflareresearch.com/ endpoint: the client capabilities are determined by fetching a resource from another endpoint via JavaScript and then parsing the response. (In my case, I'm simply hitting my own Golang endpoint; Cloudflare fetches https://pq.cloudflareresearch.com/cdn-cgi/trace.)

This isn't quite accurate: I can't determine what HTTP protocol the client used for this server, nor whether it required an HRR, and of course it's possible that the client didn't use PQC to this server but did use it for the async fetch, but ¯\_(ツ)_/¯.

wolfssl-nginx

This endpoint uses WolfSSL. I downloaded the wolfssl-5.8.2 tarball from their website (note: you do not have to provide your personal information), and then used the "wolfssl-nginx" patcheѕ found here to patch nginx.

Unfortunately, the patches there are only for very old versions of nginx, so I tried to apply them to nginx-1.28.0. That worked for the most part, but needed a few additional tweaks and still failed to compile due to some issues relating to Quic and CRLs; since I don't need those for this PoC, I just patched the code to drop the functionality in question altogether.

The other things that is worth noting here is that with WolfSSL, nginx doesn't let me select the key groups / curves to configure, for which I opened an issue. As a result, nginx can't dynamically reflect the negotiated group, either. Other than that, though, it appears to work.

Client verification

In addition to the server examples and the Java client noted above, I also have a trivial OpenSSL client to illustrate the use of the SSL_CTX_set1_groups_list library function. Other options include BoringSSL's bssl command, or curl(1), if linked against a PQC enabled TLS library:

$ java -cp "./bc/*:." PQTlsClient golang.pqc.dotwtf.wtf | grep "<br>"
      HTTP Version: HTTP/1.1<br>
      Protocol: TLS 1.3<br>
      TLS HRR: false<br>
      Cipher: TLS_AES_128_GCM_SHA256<br>
      Named Group: X25519MLKEM768<br>
$ </dev/null bssl client -curves X25519MLKEM768 -connect golang.pqc.dotwtf.wtf:443
  Version: TLSv1.3
  Resumed session: no
  Cipher: TLS_AES_128_GCM_SHA256
  ECDHE group: X25519MLKEM768
[...]
$ curl --curves X25519MLKEM768 https://golang.pqc.dotwtf.wtf/ | grep "<br>"
      HTTP Version: HTTP/2.0<br>
      Protocol: TLS 1.3<br>
      TLS HRR: true<br>
      Cipher: TLS_AES_128_GCM_SHA256<br>
      Named Group: X25519MLKEM768<br>
$ 

Anyway, I hope that these example are useful for others. Obligatory "PRs welcome!" here: https://github.com/jschauma/pqcpoc/

July 20th, 2025

Footnotes:

[1] This is often times referred to as the negotiated curve (for, well, Elliptic Curve cryptography). In TLS 1.3, this was renamed to the support (and then: negotiated) group to also refer to a potential finite field group. With hybrid PQC key exchange, this is the only place where we can identify the X25519MLKEM768 key group.


Links:

Read the original on netmeister.org

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.