logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

Gemini-TOFU.md (4089B)


  1. ---
  2. title: TOFU recommendations for Gemini
  3. date: 2020-09-21
  4. ---
  5. I will have more to say about [Gemini][0] in the future, but for now, I wanted to
  6. write up some details about one thing in particular: the trust-on-first-use
  7. algorithm I implemented for my client, [gmni][1]. I think you should implement
  8. this algorithm, too!
  9. [0]: https://gemini.circumlunar.space/
  10. [1]: https://sr.ht/~sircmpwn/gmni
  11. First of all, it's important to note that the Gemini specification explicitly
  12. mentions TOFU and the role of self-signed certificates: they are the norm in
  13. Geminiland, and if your client does not support them then you're going to be
  14. unable to browse many sites. However, the exact details are left up to the
  15. implementation. Here's what mine does:
  16. First, on startup, it finds the known_hosts file. For my client, this is
  17. `~/.local/share/gmni/known_hosts` (the exact path is adjusted as necessary per
  18. the XDG basedirs specification). Each line of this file represents a known host,
  19. and each host has four fields separated by spaces, in this order:
  20. - Hostname (e.g. gemini.circumlunar.space)
  21. - Fingerprint algorithm (e.g. SHA-512)
  22. - Fingerprint, in hexadecimal, with ':' between each octet (e.g. 55:01:D8...)
  23. - Unix timestamp of the certificate's notAfter date
  24. If a known_hosts entry is encountered with a hashing algorithm you don't
  25. understand, it is disregarded.
  26. Then, when processing a request and deciding whether or not to trust its
  27. certificate, take the following steps:
  28. 1. Verify that the certificate makes sense. Check the notBefore and notAfter
  29. dates against the current time, and check that the hostname is correct
  30. (including wildcards). Apply any other scrutiny you want, like enforcing a
  31. good hash algorithm or an upper limit on the expiration date. If these checks
  32. do not pass, the trust state is INVALID, GOTO 5.
  33. 2. Compute the certificate's fingerprint. Use the entire certificate (in OpenSSL
  34. terms, `X509_digest` will do this), not just the public key.[^1]
  35. 3. Look up the known_hosts record for this hostname. If one is found, but the
  36. record is expired, disregard it. If one is found, and the fingerprint does
  37. not match, the trust state is UNTRUSTED, GOTO 5. Otherwise, the trust state
  38. is TRUSTED. GOTO 7.
  39. 4. The trust state is UNKNOWN. GOTO 5.
  40. 5. Display information about the certficate and its trust state to the user, and
  41. prompt them to choose an action, from the following options:
  42. - If INVALID, the user's choices are ABORT or TRUST_TEMPORARY.
  43. - If UNKNOWN, the user's choices are ABORT, TRUST_TEMPORARY, or TRUST_ALWAYS.
  44. - If UNTRUSTED, abort the request and display a diagnostic message. The user
  45. must manually edit the known_hosts file to correct the issue.
  46. 6. Complete the requested action:
  47. - If ABORT, terminate the request.
  48. - If TRUST_TEMPORARY, update the session's list of known hosts.
  49. - If TRUST_ALWAYS, append a record to the known_hosts file and update the
  50. session's list of known hosts.
  51. 7. Allow the request to proceed.
  52. If the trust state is UNKNOWN, instead of requring user input to proceed, the
  53. implementation MAY proceed with the request IF the UI displays that a new
  54. certificate was trusted and provides a means to review the certificate and
  55. revoke that trust.
  56. Note that being signed by a certificate authority in the system trust store is
  57. not considered meaningful to this algorithm. Such a cert is TOFU'd all the same.
  58. [^1]: Rationale: this fingerprint matches the output of `openssl x509 -sha512 -fingerprint`.
  59. That's it! If you have feedback on this approach, please [send me an
  60. email](mailto:sir@cmpwn.com).
  61. My implementation doesn't *entirely* match this behavior, but it's close and
  62. I'll finish it up before 1.0. If you want to read the code, [here it is][2].
  63. [2]: https://git.sr.ht/~sircmpwn/gmni/tree/master/src/tofu.c
  64. Bonus recommendation for servers: you **should** use a self-signed certificate,
  65. and you **should not** use a certificate signed by one of the mainstream
  66. certificate authorities. We don't need to carry along the legacy CA cabal into
  67. our brave new Gemini future.