1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# OpenPGP Web Key Directory implementation
# https://www.ietf.org/id/draft-koch-openpgp-webkey-service-06.txt
require 'base32'
require 'digest'
module Gentoo
class WKDGenerator < Jekyll::Generator
DEV_KEYRING = '_data/active-devs.gpg'.freeze
SERVICE_KEYRING = '_data/service-keys.gpg'.freeze
WKD_DIR = '.well-known/openpgpkey/'.freeze
GPG_BASE_COMMAND = ['gpg', '--no-default-keyring', '--with-colon'].freeze
def generate_each_nick(site, keyring, nick, fps)
# Do not run if we have no fingerprints to do
# otherwise GPG will print 'gpg: WARNING: nothing exported'
return if fps.empty?
gpg = GPG_BASE_COMMAND + ['--keyring', keyring]
IO.popen(gpg + ['--export', *fps], 'rb') do |p|
keydata = p.read
next if keydata.empty?
site.pages << WKDFile.new(site, nick, keydata)
end
end
def get_fingerprints_from_keyring(keyring)
gpg = GPG_BASE_COMMAND + ['--keyring', keyring]
# build a quick list of all fingerprints in this keyring
# IO.popen in a non-block context returns a list of lines
IO.popen(gpg + ['--list-keys'], 'rt',
&:readlines).grep(/^fpr:/).map(&:strip).map do |line|
line.split(':')[9]
end.compact.map(&:upcase)
end
def generate(site)
return if site.data['userinfo'].nil?
# WKD uses z-Base32; replace the alphabet since the standard
# Base32 module supports that and the zBase32 modules are hard to get
old_base32_table = Base32.table
Base32.table = 'ybndrfg8ejkmcpqxot1uwisza345h769'.freeze
[['current', DEV_KEYRING], ['system', SERVICE_KEYRING]].each do |group, keyring|
keyring_fps = get_fingerprints_from_keyring(keyring)
# Now loop over users
site.data['userinfo'][group].each do |nick, details|
begin
fps = details['gpgfp'].map { |fp| fp.gsub(/\s+/, '').upcase }
# Run only on the intersection of fingerprints we want and fingerprints we have
generate_each_nick(site, keyring, nick, (keyring_fps & fps))
rescue
# fail them silently
end
end
end
# policy file is required
site.pages << WKDPolicyFile.new(site)
Base32.table = old_base32_table
end
end
class WKDFile < Jekyll::Page
def initialize(site, nick, keydata)
@site = site
@base = @site.source
@dir = WKDGenerator::WKD_DIR + 'hu/'
@name = Base32.encode(Digest::SHA1.digest(nick.downcase))
process(@name)
read_yaml(File.join(@base, '_layouts'), 'passthrough.html')
@content = keydata
end
def render_with_liquid?
false
end
end
class WKDPolicyFile < Jekyll::Page
def initialize(site)
@site = site
@base = @site.source
@dir = WKDGenerator::WKD_DIR
@name = 'policy'
process(@name)
read_yaml(File.join(@base, '_layouts'), 'passthrough.html')
@content = ''
end
end
end
# vim:et ts=2 sts=2:
|