logo

drewdevault.com

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

Gemini-TOFU.md (4114B)


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