implementing client certificate support

mbays at sdf.org mbays at sdf.org
Sun Jun 7 19:54:39 BST 2020


[This post is also a glog entry at
gemini://gemini.thegonz.net/glog/200607-clientCertificatePostMortem.gmi]

I just finished adding client certificate support to my line-based 
textual gemini client diohsc. This involved making some non-trivial 
choices, and I didn't copy av98 in all cases, so I hope discussing the 
decisions I made and the reasoning behind them could be of use to other 
client developers and in refining the spec.

## User interface
* The technical terminology "client certificate", which describes an 
implementation detail rather than the underlying concept, is mostly 
hidden from the user. Instead, the client talks about "identities". An 
identity is just a client certificate along with an optional name.
* In diohsc, there are two kinds of identity -- temporary anonymous 
identities and long-term named identities. The latter are saved to disc, 
the former are held only in memory.
* There is no way to name and save an anonymous identity. Allowing it 
would complicate the UI and the conceptual distinction between the two 
kinds of identity.

### Creating identities
* When 61 is received, the user is asked if they want to create an 
anonymous identity.
* On any other 6* error code, the user may select an existing named 
identity or create a new named or anonymous identity, and then retry the 
request.
* When creating a named identity, the user is prompted to set a Common 
Name (which can be empty). This is the only choice required of the user. 
It would make for a neater UI to automatically set the Common Name to be 
the name of the identity, and in some ways it would make sense. But the 
user may well want to use the same Common Name (e.g. "anon" or "") on 
multiple servers without a common identity.

### Using identities
* An identity can be set to be used on a given server at and below 
a given URI path. So you can have one identity in use for 
gemini://foo.bar/~quux/..., and another for gemini://foo.bar/~xuuq/...
* Whenever an URI is shown to the user, it is annotated with the 
identity, if any, which would be used if it were requested; e.g. 
"gemini://foo.bar/~xuuq/hello.gmi[clarkKent]". This includes relative 
URIs in links. So the user knows when they're going to use what 
identity.
* Colouring is used to visually separate the annotation from the URI 
(but '[' and ']' are reserved characters in URIs, so this isn't actually 
necessary).
* Anonymous identities are denoted as "[]".
* If the user requests an URI which would use an identity which hasn't 
been used in 30m, they are asked to confirm whether they want to still 
use it. If they don't want to, the identity is disabled for this path.

### Controlling identities
* There is a single new command added to deal with identities: "identify 
[URI]". If URI is missing, it defaults to the current URI (if any).
* If there is an identity in use at the URI, after confirmation it is 
removed.
* Otherwise the user is prompted to select an existing identity or 
create a new one, as if 60 had been received.

## Certificates
* To minimise information leakage to the server, all certificates, for 
both anonymous and named identities, are created the same way apart from 
the Common Name.
* The expiration date is set to 2 years in the future. I'm not sure if 
this is the best compromise.
* The "valid from" date is set to 1 year in the past. Why not set it to 
the current time? It's a known problem that users "in the wild" often 
don't have correctly set clocks -- they don't set the right time zone, 
then set the clock to make the time "look right". Since gemini servers 
are meant to be widely deployed, we shouldn't expect either client or 
server to have the right time. So we should backdate certificates by at 
least a couple of days, and then a year seems a nice round safe choice.
* The serial number is set to 0, no extensions are set, and the 
distinguished name consists only of the Common Name.
* The key is 2048 bit RSA with public exponent e=65537, and with hash 
algorithm SHA256. I would have liked to use Ed25519, which is more 
efficient in size and processing, but not all servers accept it.

### Fingerprinting
Currently, these choices identify diohsc uniquely. Hopefully a future 
version of the spec will suggest defaults to prevent such 
fingerprinting, and I think these would be reasonable choices. But it 
could be worth considering mandating Ed25519, even if this means only 
TLS1.3 servers can use client certificates. This can be considered an 
"advanced" feature for a server, so it isn't so much of a problem if 
some servers aren't capable of it.

## Libraries
I used Vincent Hanquez's excellent pure-haskell cryptography libraries 
to create and use client certificates. Neither openssl nor gnu-tls are 
involved at all.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://lists.orbitalfox.eu/archives/gemini/attachments/20200607/b0e961b6/attachment.sig>


More information about the Gemini mailing list