X-Git-Url: https://git.draconx.ca/gitweb/homepage.git/blobdiff_plain/e1908f247e262b45949dd6d4a0d62ca54664cf15..fe387389cf437d11a969d51b5625cd6bc4ddb79f:/lib/gpg-wkd.rb 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