commit: 2ea0a8069c7ee998a6ede3bd538bdccfb3984dcb
parent 7d3549cb350731990c6e23840fd6c586cc09952a
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 21 Sep 2020 18:09:44 -0400
Add Gemini TOFU
Diffstat:
1 file changed, 80 insertions(+), 0 deletions(-)
diff --git a/content/blog/Gemini-TOFU.md b/content/blog/Gemini-TOFU.md
@@ -0,0 +1,80 @@
+---
+title: TOFU recommendations for Gemini
+date: 2020-09-21
+---
+
+I will have more to say about [Gemini][0] in the future, but for now, I wanted to
+write up some details about one thing in particular: the trust-on-first-use
+algorithm I implemented for my client, [gmni][1]. I think you should implement
+this algorithm, too!
+
+[0]: https://gemini.circumlunar.space/
+[1]: https://sr.ht/~sircmpwn/gmni
+
+First of all, it's important to note that the Gemini specification explicitly
+mentions TOFU and the role of self-signed certificates: they are the norm in
+Geminiland, and if your client does not support them then you're going to be
+unable to browse many sites. However, the exact details are left up to the
+implementation. Here's what mine does:
+
+First, on startup, it finds the known_hosts file. For my client, this is
+`~/.local/share/gmni/known_hosts` (the exact path is adjusted as necessary per
+the XDG basedirs specification). Each line of this file represents a known host,
+and each host has four fields separated by spaces, in this order:
+
+- Hostname (e.g. gemini.circumlunar.space)
+- Fingerprint algorithm (e.g. SHA-512)
+- Fingerprint, in hexadecimal, with ':' between each octet (e.g. 55:01:D8...)
+- Unix timestamp of the certificate's notAfter date
+
+If a known_hosts entry is encountered with a hashing algorithm you don't
+understand, it is disregarded.
+
+Then, when processing a request and deciding whether or not to trust its
+certificate, take the following steps:
+
+1. Verify that the certificate makes sense. Check the notBefore and notAfter
+ dates against the current time, and check that the hostname is correct
+ (including wildcards). Apply any other scrutiny you want, like enforcing a
+ good hash algorithm. If these checks do not pass, the trust state is INVALID,
+ GOTO 4.
+2. Compute the certificate's fingerprint. Use the entire certificate (in OpenSSL
+ terms, `X509_digest` will do this), not just the public key.[^1]
+3. Look up the known_hosts record for this hostname. If one is found, but the
+ record is expired, disregard it. If one is found, and the fingerprint does
+ not match, the trust state is UNTRUSTED, GOTO 5. Otherwise, the trust state
+ is TRUSTED. GOTO 6.
+4. The trust state is UNKNOWN. GOTO 5.
+5. Display information about the certficate and its trust state to the user, and
+ prompt them to choose an action, from the following options:
+ - If INVALID, the user's choices are ABORT or TRUST_TEMPORARY.
+ - If UNKNOWN, the user's choices are ABORT, TRUST_TEMPORARY, or TRUST_ALWAYS.
+ - If UNTRUSTED, abort the request and display a diagnostic message. The user
+ must manually edit the known_hosts file to correct the issue.
+6. Complete the requested action:
+ - If ABORT, terminate the request.
+ - If TRUST_TEMPORARY, update the session's list of known hosts, then allow
+ the request to proceed.
+ - If TRUST_ALWAYS, append a record to the known_hosts file and update the
+ session's list of known hosts, then allow the request to proceed.
+
+If the trust state is UNKNOWN, instead of requring user input to proceed, the
+implementation MAY proceed with the request IF the UI displays that a new
+certificate was trusted and provides a means to review the certificate and
+revoke that trust.
+
+[^1]: Rationale: this fingerprint matches the output of `openssl x509 -sha512 -fingerprint`.
+
+That's it! If you have feedback on this approach, please [send me an
+email](mailto:sir@cmpwn.com).
+
+Note that my implementation doesn't *entirely* match this behavior, but it's
+close and I'll finish it up before 1.0. If you want to read the code anyway,
+[here it is][2].
+
+[2]: https://git.sr.ht/~sircmpwn/gmni/tree/master/src/tofu.c
+
+Bonus recommendation for servers: you **should** use a self-signed certificate,
+and you **should not** use a certificate signed by one of the mainstream
+certificate authorities. We don't need to carry along the legacy CA cabal into
+our brave new Gemini future.