-- Leo's gemini proxy

-- Connecting to gem.twunk.uk:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

Even here I find yaks to shave


I was going to add a little thing to my server to receive messages from people. Not that anyone is likely to send me messages here, but it would have been fun to work on anyway, right?


I haven't done it yet. That's because I figured I should probably add support for client certificates, since that's what Gemini uses for authentication and identity (very neat). Adding support for client certificates seemed like it would be easy. rustls supports it, so all I need to do is change ClientCertVerifier in the configuration object to be an instance of AllowAnyAnonymousOrAuthenticatedClient instead of an instance of NoClientAuth. Easy.


Except when I tried it with Lagrange it didn't work. Connection closed during TLS handshake, because rustls rejected the certificate with an error about an unsupported version. rustls uses rustls-webpki to do certification validation, and it turns out that rustls-webpki does not support X.509 V1 certificates, only X.509 V3 [1]. And Lagrange... Lagrange generates self-signed V1 certificates [2].


Anyway, this cause was not immediately apparent to me, so the first thing I had to do was insert extra logging (yes, I'm a print-debug kind of person, sorry) to find out what the problem was. Then having found that, searching the github repositories lead me to the various historical issues and idle pull requests about it. Luckily it turns out that most of the necessary code changes actually already exist in one form or another. They just haven't been integrated (and are not up to date).


So the next thing was to apply my own patches to webpki (not pushed upstream, sorry) to add support for accepting X.509 V1 certificates. Then that revealed the next problem, which is that webpki is... well it does verification based on the Certificate Authority based PKI system that's used for the web. So it expects to get a certificate *chain*, and a set of valid root certificates, and it will use those to verify that the presented end certificate has a proper chain back to a trusted root. But the client certs used for Gemini are usually not that. Gemini uses self-signed certificates and Trust-On-First-Use (TOFU). Identity certs in Lagrange (unless you import one from another source) are self-signed V1 certs.


So the next patch in my local version of webpki adds a greatly cut-down verification code path that just verifies basic information about the certificate and checks that it is self-signed.


Finally, a custom rustls ClientCertVerifier can check the presented client certificate using these new code-paths, so that the server will accept a self-signed (V1 or V3) certificate.


And now my server can accept certificates from Lagrange.


You can see it by presenting a certificate on here:

Server info and debug page


One yak shaved.


Edit 2023-05-16: I'm an idiot and I didn't test it properly, leading to always getting the same fingerprint for any cert (even though the rest of the cert information was parsed ok). Will fix it in the next deploy.


[1] There's an issue on the webpki repository about adding support for V1 certificates, but it hasn't seen any motion since the end of 2021.

[2] There was an issue about adding support in Lagrange for generating V3 certificates, but it was closed wontfix because none of the V3 functionality is needed.

-- Response ended

-- Page fetched on Sun May 12 12:17:21 2024