Suggest keyserver (again) for retrievng public keys.
authorNick Bowler <nbowler@draconx.ca>
Sat, 2 Apr 2022 16:12:57 +0000 (12:12 -0400)
committerNick Bowler <nbowler@draconx.ca>
Sat, 2 Apr 2022 16:19:32 +0000 (12:19 -0400)
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
lib/gpg-wkd.rb
lib/project-readme.rb

diff --git a/Rules b/Rules
index c753c3867415af50d3e5ffc6eb3049e9b7a47d5f..225615397cbbaf6eb696e07f4622179692543e10 100644 (file)
--- 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
index 589369838817c34c2d75c92ed0270c8ea5d566e1..b2379f16853d00664b479444d4197dec5065e5c5 100644 (file)
@@ -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
index ffe3e3f3679b9e1f2dbd187d057007a87ac6c5a6..079640e28d3b3d196c531781da2642dafcf16c14 100644 (file)
@@ -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:
 
-            <kbd>gpg --locate-external-keys #{gpg_uid}</kbd>
+            <kbd>gpg --keyserver keys.draconx.ca --recv-keys #{gpg_keyid}</kbd>
 
             Then run the verify command again.  Alternatively, you can
             [download the public keyring][keyring] manually and import it