1 # Nick's web site: Export public keys for the Web Key Directory
3 # Copyright © 2022 Nick Bowler
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 @@gpg2 = "/usr/bin/gpg"
22 @@wksclient = "/usr/libexec/gpg-wks-client"
24 def WKD.gpg2; @@gpg2 end
25 def WKD.gpg2=(x); @@gpg2 = x end
26 def WKD.wksclient; @@wksclient end
27 def WKD.wksclient=(x); @@wksclient = x end
29 # Return a list list of all UIDs known from the given GPG keyrings.
30 def WKD.uids_from_keyrings(*args)
34 "--no-default-keyring", "--with-colons", "--list-keys",
35 *args.map { |x| "--keyring=" + (x['/'] ? x : "./" + x) }
36 ) do |stdin, stdout, result|
39 fields = line.split(":")
40 next if fields[0] != "uid"
41 fields[9].gsub!(/\\x../) { |x| x[2..].hex.chr }
42 uids[fields[9]] = true
46 raise "gpg failed" unless result.value.success?
52 # Given a list of UIDs, return a dictionary where the keys are UIDs
53 # and the values are the WKS hash.
54 def WKD.hashes_from_uids(*args)
57 consume_output = Proc.new do |s|
58 while l = s.slice!(/([^\n]*)\n/) do
59 hash, uid = l.chomp.split(nil, 2)
64 Open3.popen2(@@wksclient,
66 ) do |stdin, stdout, result|
68 args.flatten.each do |uid|
73 buf += stdout.read_nonblock(100)
74 consume_output.call(buf)
75 rescue EOFError, IO::WaitReadable
82 buf += stdout.readpartial(100)
83 consume_output.call(buf)
89 raise "gpg-wks-client failed" unless result.value.success?
97 # Call during preprocessing to create items for each unique UID found in the
98 # given keyring items. The items have the identifier /gpg/UID and the content
99 # is the same UID. The items are created with the :keyrings attribute set to
100 # the list of keyring files and :wkd_hash is for the Web Key Directory.
101 def create_wkd_items(keyring_items)
103 [*keyring_items].each { |item| keyring_files[item.raw_filename] = true }
105 wkd = WKD.hashes_from_uids(WKD.uids_from_keyrings(*keyring_files.keys))
106 wkd.each do |uid, hash|
108 keyrings: keyring_files.keys,
111 @items.create(uid, attrs, "/gpg/" + uid)
115 # Convert items created by create_wkd_items into real GPG keyrings.
116 class WKDExport < Nanoc::Filter
117 identifier :wkd_export
118 type :text => :binary
120 def run(content, params = {})
121 args = @item[:keyrings].map { |x|
122 "--keyring=" + (x['/'] ? x : "./" + x)
124 args.append("--armor") if params[:armor]
126 dummy, result = Open3.capture2(WKD.gpg2, "--export",
127 "--no-default-keyring", "--output=" + output_filename,
128 *args, content.chomp)
129 raise "gpg failed" unless result.success?