From 220aba7ec291f6f8de8982074a3488d28b4aee1f Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 2 Apr 2022 12:12:57 -0400 Subject: [PATCH] Suggest keyserver (again) for retrievng public keys. The keyserver protocol is super simple and just consists of HTTP requests, so we can self-host this option pretty easily too. This is probably more widely supported than the very new Web Key Directory. I also prefer how the retrieval instructions specify an explicit key ID rather than just an email address. --- Rules | 11 +++++--- lib/gpg-wkd.rb | 58 ++++++++++++++++++++++++++++++++++++++++--- lib/project-readme.rb | 6 ++--- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/Rules b/Rules index c753c38..2256153 100644 --- a/Rules +++ b/Rules @@ -70,6 +70,7 @@ preprocess do end create_wkd_items(@items["/pubring.gpg"]) + create_hkp_items(@items["/pubring.gpg"]) end postprocess do @@ -220,12 +221,16 @@ end compile '/gpg/*' do filter :wkd_export_armor - write "/pubring/" + @item.identifier.components.last + ".asc" + write "/pubring/#{@item.identifier.components.last}.asc" + write "/pubring/#{@item[:id64]}.asc" if item[:id64] + write "/pubring/#{@item[:id32]}.asc" if item[:id32] end compile '/gpg/*', rep: :hu do - filter :wkd_export - write "/pubring/wkd/" + @item[:wkd_hash] + if @item[:wkd_hash] + filter :wkd_export + write "/pubring/wkd/" + @item[:wkd_hash] + end end compile '/*.gpg' do diff --git a/lib/gpg-wkd.rb b/lib/gpg-wkd.rb index 5893698..b2379f1 100644 --- a/lib/gpg-wkd.rb +++ b/lib/gpg-wkd.rb @@ -1,4 +1,5 @@ -# Nick's web site: Export public keys for the Web Key Directory +# Nick's web site: Export GPG public keys for HTTP Keyserver and the +# Web Key Directory # # Copyright © 2022 Nick Bowler # @@ -33,14 +34,35 @@ module WKD end # Helper for implementing export filters below - def WKD.export(item, uid, *args) + def WKD.export(item, id, *args) data, result = Open3.capture2(@@gpg2, "--export", *args, - *WKD.keyring_args(item[:keyrings]), uid.chomp) + *WKD.keyring_args(item[:keyrings]), id.chomp) raise "gpg failed" unless result.success? return data end - # Return a list list of all UIDs known from the given GPG keyrings. + # Return a list of all key fingerprints known from the given GPG keyrings. + def WKD.keys_from_keyrings(*args) + fps = {} + + Open3.popen2(@@gpg2, + "--with-colons", "--list-keys", *WKD.keyring_args(args) + ) do |stdin, stdout, result| + stdin.close + stdout.each do |line| + fields = line.split(":") + next if fields[0] != "fpr" + fps[fields[9]] = true + end + stdout.close + + raise "gpg failed" unless result.value.success? + end + + return fps.keys + end + + # Return a list of all UIDs known from the given GPG keyrings. def WKD.uids_from_keyrings(*args) uids = {} @@ -125,6 +147,34 @@ def create_wkd_items(keyring_items) end end +def create_hkp_items(keyring_items) + keyring_files = {} + [*keyring_items].each { |item| keyring_files[item.raw_filename] = true } + + fps = WKD.keys_from_keyrings(*keyring_files.keys) + keyids_64 = {} + keyids_32 = {} + + fps.each do |fp| + id64 = fp[-16..] + id32 = fp[-8..] + + keyids_64[id64] = keyids_64[id64].to_i + 1 + keyids_32[id32] = keyids_32[id32].to_i + 1 + end + + fps.each do |fp| + id64 = fp[-16..] + id32 = fp[-8..] + + attrs = { keyrings: keyring_files.keys } + attrs[:id64] = id64 if keyids_64[id64] == 1 + attrs[:id32] = id32 if keyids_32[id32] == 1 + + @items.create("0x"+fp, attrs, "/gpg/" + fp) + end +end + # Convert items created by create_wkd_items into real GPG keyrings. class WKDExport < Nanoc::Filter identifier :wkd_export diff --git a/lib/project-readme.rb b/lib/project-readme.rb index ffe3e3f..079640e 100644 --- a/lib/project-readme.rb +++ b/lib/project-readme.rb @@ -82,10 +82,10 @@ def project_readme(item = @item) obtaining += line.lstrip end - gpg_uid = "nbowler@draconx.ca" + gpg_keyid = "5B45D3D185B8E1F6" obtaining += "\n\n" + <<~EOF [gpg]: https://gnupg.org/ - [keyring]: /pubring/#{gpg_uid}.asc + [keyring]: /pubring/#{gpg_keyid}.asc Use the signature file to verify that the corresponding source bundle is intact. After downloading both files, if [GnuPG][gpg] @@ -96,7 +96,7 @@ def project_readme(item = @item) If the verification fails because you don't have the required public key, that key can be imported with a command such as: - gpg --locate-external-keys #{gpg_uid} + gpg --keyserver keys.draconx.ca --recv-keys #{gpg_keyid} Then run the verify command again. Alternatively, you can [download the public keyring][keyring] manually and import it -- 2.43.0