From fe387389cf437d11a969d51b5625cd6bc4ddb79f Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 25 Mar 2022 21:34:12 -0400 Subject: [PATCH 1/1] Update GPG instructions for packages. It seems the keys.gnupg.net keyserver has been dead for some time, so it is not a viable option for public key distribution. The new-ish Web Key Directory gives similar functionality but unlike third party keyservers is self-hosted. --- Rules | 17 +++++- content/pubring.gpg | Bin 0 -> 643 bytes lib/gpg-wkd.rb | 131 ++++++++++++++++++++++++++++++++++++++++++ lib/project-readme.rb | 12 ++-- 4 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 content/pubring.gpg create mode 100644 lib/gpg-wkd.rb diff --git a/Rules b/Rules index 26bcb40..7acf71d 100644 --- a/Rules +++ b/Rules @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # -# Copyright © 2018-2020 Nick Bowler +# Copyright © 2018-2022 Nick Bowler # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -68,6 +68,8 @@ preprocess do @items.create("", attrs, "#{dir}/index.lst") end + + create_wkd_items(@items["/pubring.gpg"]) end postprocess do @@ -216,6 +218,19 @@ compile '/**/*.scss' do write @item.identifier.without_ext + '.css' end +compile '/gpg/*' do + filter :wkd_export, armor: true + write "/pubring/" + @item.identifier.components.last + ".asc" +end + +compile '/gpg/*', rep: :hu do + filter :wkd_export + write "/pubring/wkd/" + @item[:wkd_hash] +end + +compile '/*.gpg' do +end + compile '/**/*.svg' do filter :scour, comment_stripping: true write @item.identifier.to_s diff --git a/content/pubring.gpg b/content/pubring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..456deefdb559df95cda9ad9e5ac82486dd48abae GIT binary patch literal 643 zcmV-}0(||M0SyFTD1zew2mqXq!Ta4)rm4w_fPNIOa0MDaJHPZ=2Cr;_;-zg8wU3JF z&~AV}PENLT0E(2hyK4EqgAuQ-&F=dEK_q<)7F91G%k%^mJ1fG1`>jC(h8|{k3`;$l zeHiYV08)TB`g#)t1NYw$&HkN$-%wE6dnxH)5)4oWy-p%Z`iyOwS$0DaqN;qEk&izx za_O2ZxQv#O0)aL1G%CU|__AIKvs;{!6hcbC!?J?41(_PZCy)UC(g+l9_x5G;p7;$k zhS#3jl))`gzfI2acty6DL2e)7E?Bqar3rsBM7C<6XmnpHT@0BP+HyJWY`Tpz^IJ^# zh(>EZu9`9>PuFUF;bQ<10RRECAWmsxYal{zcWh;HAUtkjZ+C2EazJEqVPkJ@crIgM zK8XQN1QP)W05}#Q1SO~(g1f=AA6>M|gIh(@(S^9-_5}i9D1zex8v_Lk2?z%Q1{Dek z2nzxP76JnS0v-VZ7k~f?2@qRF)6s>v;r0U}2mBA*$<5Q@NW%JO*n5vxMpN4u{?Y?k4wHV z7Wsf^Gl=yVH(B(M3!wM0Z*MaG)~S8Lc&Gi)n&YqAWJs{~bDr+5aNEvTtR)ch7S-}` zR744}d-yF|SEQ~56Zmf}s5n1OYwyq}pXI5Aq0|b{qfLa+?KP147E}rD8>aIV3Xwck dd~|O2-=7T9|1(6e0BRL#l4nw8VsgK#7@=ZrAK3r^ literal 0 HcmV?d00001 diff --git a/lib/gpg-wkd.rb b/lib/gpg-wkd.rb new file mode 100644 index 0000000..c6e5fcc --- /dev/null +++ b/lib/gpg-wkd.rb @@ -0,0 +1,131 @@ +# Nick's web site: Export public keys for the Web Key Directory +# +# Copyright © 2022 Nick Bowler +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +module WKD + require 'open3' + + @@gpg2 = "/usr/bin/gpg" + @@wksclient = "/usr/libexec/gpg-wks-client" + + def WKD.gpg2; @@gpg2 end + def WKD.gpg2=(x); @@gpg2 = x end + def WKD.wksclient; @@wksclient end + def WKD.wksclient=(x); @@wksclient = x end + + # Return a list list of all UIDs known from the given GPG keyrings. + def WKD.uids_from_keyrings(*args) + uids = {} + + Open3.popen2(@@gpg2, + "--no-default-keyring", "--with-colons", "--list-keys", + *args.map { |x| "--keyring=" + (x['/'] ? x : "./" + x) } + ) do |stdin, stdout, result| + stdin.close + stdout.each do |line| + fields = line.split(":") + next if fields[0] != "uid" + fields[9].gsub!(/\\x../) { |x| x[2..].hex.chr } + uids[fields[9]] = true + end + stdout.close + + raise "gpg failed" unless result.value.success? + end + + return uids.keys + end + + # Given a list of UIDs, return a dictionary where the keys are UIDs + # and the values are the WKS hash. + def WKD.hashes_from_uids(*args) + wkd_hash = {} + + consume_output = Proc.new do |s| + while l = s.slice!(/([^\n]*)\n/) do + hash, uid = l.chomp.split(nil, 2) + wkd_hash[uid] = hash + end + end + + Open3.popen2(@@wksclient, + "--print-wkd-hash" + ) do |stdin, stdout, result| + buf = "" + args.flatten.each do |uid| + stdin.puts(uid) + stdin.flush + + loop do + buf += stdout.read_nonblock(100) + consume_output.call(buf) + rescue EOFError, IO::WaitReadable + break + end + end + stdin.close + + loop do + buf += stdout.readpartial(100) + consume_output.call(buf) + rescue EOFError + break + end + stdout.close + + raise "gpg-wks-client failed" unless result.value.success? + end + + return wkd_hash + end + +end + +# Call during preprocessing to create items for each unique UID found in the +# given keyring items. The items have the identifier /gpg/UID and the content +# is the same UID. The items are created with the :keyrings attribute set to +# the list of keyring files and :wkd_hash is for the Web Key Directory. +def create_wkd_items(keyring_items) + keyring_files = {} + [*keyring_items].each { |item| keyring_files[item.raw_filename] = true } + + wkd = WKD.hashes_from_uids(WKD.uids_from_keyrings(*keyring_files.keys)) + wkd.each do |uid, hash| + attrs = { + keyrings: keyring_files.keys, + wkd_hash: hash + } + @items.create(uid, attrs, "/gpg/" + uid) + end +end + +# Convert items created by create_wkd_items into real GPG keyrings. +class WKDExport < Nanoc::Filter + identifier :wkd_export + type :text => :binary + + def run(content, params = {}) + args = @item[:keyrings].map { |x| + "--keyring=" + (x['/'] ? x : "./" + x) + }; + args.append("--armor") if params[:armor] + + dummy, result = Open3.capture2(WKD.gpg2, "--export", + "--no-default-keyring", "--output=" + output_filename, + *args, content.chomp) + raise "gpg failed" unless result.success? + end +end diff --git a/lib/project-readme.rb b/lib/project-readme.rb index 3338e8a..ffe3e3f 100644 --- a/lib/project-readme.rb +++ b/lib/project-readme.rb @@ -1,6 +1,6 @@ # Nick's web site: Generate a project page from a submodule README.md # -# Copyright © 2021 Nick Bowler +# Copyright © 2021-2022 Nick Bowler # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -82,21 +82,25 @@ def project_readme(item = @item) obtaining += line.lstrip end + gpg_uid = "nbowler@draconx.ca" obtaining += "\n\n" + <<~EOF [gpg]: https://gnupg.org/ + [keyring]: /pubring/#{gpg_uid}.asc Use the signature file to verify that the corresponding source bundle is intact. After downloading both files, if [GnuPG][gpg] is installed, the signature can be verified with a command like: - gpg --verify #{targz}.sig + gpg --verify #{targz}.sig #{targz} If the verification fails because you don't have the required public key, that key can be imported with a command such as: - gpg --keyserver keys.gnupg.net --recv-keys 5B45D3D185B8E1F6 + gpg --locate-external-keys #{gpg_uid} - Then run the verify command again. + Then run the verify command again. Alternatively, you can + [download the public keyring][keyring] manually and import it + using `gpg --import`. EOF end -- 2.43.0