github-merge-pr: fix loading .config if symbolic link is used
[maintainer-tools.git] / make-signatures.pl
1 #!/usr/bin/env perl
2
3 use strict;
4 use warnings;
5 use locale;
6
7 use POSIX qw(mktime strftime setlocale LC_COLLATE);
8
9 setlocale(LC_COLLATE, "en_US.UTF-8");
10
11 my $page = 'https://openwrt.org/docs/guide-user/security/signatures';
12
13 my @keytypes = (
14 undef,
15 'RSA',
16 'RSA, encrypt only',
17 'RSA, sign only',
18 undef,
19 undef,
20 undef,
21 undef,
22 undef,
23 undef,
24 undef,
25 undef,
26 undef,
27 undef,
28 undef,
29 undef,
30 'Elgamal, encrypt only',
31 'DSA',
32 'EC',
33 'ECDSA',
34 'Elgamal'
35 );
36
37 sub format_title {
38 my ($key) = @_;
39
40 if ($key->{is_system_key}) {
41 return $key->{comment};
42 }
43
44 return sprintf 'Public key of %s', $key->{name};
45 }
46
47 sub format_keytype {
48 my ($key, $is_subkey) = @_;
49
50 my $type = $key->{$is_subkey ? 'stype' : 'type'};
51 my $size = $key->{$is_subkey ? 'ssize' : 'size'};
52
53 my ($d, $m, $y, $s);
54
55 if (defined($size) && $size > 0) {
56 $s = sprintf '%d Bit %s', $size, $keytypes[$type];
57 }
58 else {
59 $s = $keytypes[$type];
60 }
61
62 (undef, undef, undef, $d, $m, $y) =
63 localtime $key->{$is_subkey ? 'sctime' : 'ctime'};
64
65 $s .= sprintf ', created %04d-%02d-%02d', $y + 1900, $m + 1, $d;
66
67 (undef, undef, undef, $d, $m, $y) =
68 localtime $key->{$is_subkey ? 'setime' : 'etime'};
69
70 if ($d && $m && $y) {
71 $s .= sprintf ', expires %04d-%02d-%02d', $y + 1900, $m + 1, $d;
72 }
73
74 return $s;
75 }
76
77 sub format_fingerprint {
78 my ($key, $is_subkey) = @_;
79
80 my $fprint = $key->{$is_subkey ? 'sfprint' : 'fprint'};
81 my (@fields) = $fprint =~ m!([A-F0-9]{4})!g;
82
83 return join(' ', @fields[0..4]) . ' ' . join(' ', @fields[5..9]);
84 }
85
86 sub format_download {
87 my ($key) = @_;
88
89 my $mtime = $key->{ctime};
90
91 if (open GIT, '-|', qw(git log -1 --format=%ct --), $key->{filename}) {
92 if (defined(my $line = readline GIT)) {
93 chomp $line;
94 $mtime = $line;
95 }
96 close GIT;
97 }
98
99 my $ts = strftime '%F %T %z', gmtime $mtime;
100
101 return sprintf
102 "[[https://git.openwrt.org/?p=keyring.git;a=history;f=%s|Last change: %s]] | " .
103 "[[https://git.openwrt.org/?p=keyring.git;a=blob_plain;f=%s|Download]]\n" ,
104 $key->{filename}, $ts, $key->{filename};
105 }
106
107 sub parse_timestamp {
108 my ($s) = @_;
109
110 if ($s =~ m!^(\d\d\d\d)-(\d\d)-(\d\d)$!) {
111 return mktime(0, 0, 0, $3 + 0, $2 - 1, $1 - 1900);
112 }
113
114 return int $s;
115 }
116
117
118 my $markup_template = '';
119
120 if (open RAW, '-|', 'curl', '-s', "$page?do=export_raw") {
121 local $/;
122 $markup_template = readline RAW;
123 close RAW;
124 }
125
126
127 my @pubkeys;
128
129 if (open KEYS, '-|', qw(find gpg/ -type f -name *.asc -print)) {
130 while (defined(my $file = readline KEYS)) {
131 chomp $file;
132 if (open GPG, '-|', qw(gpg --with-fingerprint --with-fingerprint --with-colons), $file) {
133 my %data;
134
135 while (defined(my $line = readline GPG)) {
136 chomp $line;
137 my @fields = split ':', $line;
138 if ($fields[0] eq 'uid' && !exists $data{name}) {
139 ($data{name}, $data{comment}, $data{email}) =
140 $fields[9] =~ m!^([^()]+)(?: \((.+?)\))? <(.+)>$!;
141 }
142 elsif ($fields[0] eq 'pub') {
143 $data{size} = int $fields[2];
144 $data{type} = int $fields[3];
145 $data{eid} = $fields[4];
146 $data{ctime} = parse_timestamp($fields[5]);
147 $data{etime} = $fields[6] ? parse_timestamp($fields[6]) : 0;
148 if ($fields[9] && !exists $data{name}) {
149 ($data{name}, $data{comment}, $data{email}) =
150 $fields[9] =~ m!^([^()]+)(?: \((.+?)\))? <(.+)>$!;
151 }
152 }
153 elsif ($fields[0] eq 'sub') {
154 $data{ssize} = int $fields[2];
155 $data{stype} = int $fields[3];
156 $data{seid} = $fields[4];
157 $data{sctime} = parse_timestamp($fields[5]);
158 $data{setime} = $fields[6] ? parse_timestamp($fields[6]) : 0;
159 }
160 elsif ($fields[0] eq 'fpr') {
161 $data{exists($data{stype}) ? 'sfprint' : 'fprint'} = $fields[9];
162 }
163 }
164
165 close GPG;
166
167 $data{filename} = $file;
168 $data{is_system_key} =
169 (index($data{email}, 'openwrt.org') >= 0) ||
170 (index($data{email}, 'lede-project.org') >= 0) ||
171 (index($data{email}, 'lists.openwrt.org') >= 0) ||
172 (index($data{email}, 'lists.infradead.org') >= 0);
173
174 push @pubkeys, \%data;
175 }
176 }
177
178 close KEYS;
179 }
180
181 my $gpg_markup = '';
182
183 foreach my $key (sort {
184 !$a->{is_system_key} <=> !$b->{is_system_key} ||
185 $a->{name} cmp $b->{name}
186 } @pubkeys) {
187
188 $gpg_markup .= sprintf "---\n\n=== %s ===\n\n",
189 format_title($key);
190
191 $gpg_markup .= sprintf "User ID: **%s** <%s>\\\\\n",
192 $key->{name}, $key->{email};
193
194 $gpg_markup .= sprintf "Public Key: 0x%s**%s** (%s)\\\\\n",
195 substr($key->{eid}, 0, 8), substr($key->{eid}, 8),
196 format_keytype($key, 0);
197
198 $gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
199 format_fingerprint($key, 0);
200
201 if (exists $key->{stype}) {
202 $gpg_markup .= sprintf "Signing Subkey: 0x%s **%s** (%s)\\\\\n",
203 substr($key->{seid}, 0, 8), substr($key->{seid}, 8),
204 format_keytype($key, 1);
205
206 $gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
207 format_fingerprint($key, 1);
208 }
209
210 $gpg_markup .= sprintf "%s\n", format_download($key);
211 }
212
213
214 my @usignkeys;
215
216 if (open KEYS, '-|', qw(find usign/ -type f -name *[0-9a-f] -print)) {
217 while (defined(my $file = readline KEYS)) {
218 chomp $file;
219
220 if (open USIGN, '<', $file) {
221 my %data;
222
223 while (defined(my $line = readline USIGN)) {
224 chomp $line;
225
226 if ($line =~ m!^untrusted comment: (.+)$!) {
227 $data{comment} = $1;
228 }
229 else {
230 $data{key} = $line;
231 }
232 }
233
234 close USIGN;
235
236 $file =~ m!/([0-9a-f]{16})$!;
237
238 $data{id} = $1;
239 $data{filename} = $file;
240
241 push @usignkeys, \%data;
242 }
243 }
244
245 close KEYS;
246 }
247
248 my $usign_markup = '';
249
250 foreach my $key (sort { $a->{comment} cmp $b->{comment} } @usignkeys) {
251 $usign_markup .= sprintf "---\n\n=== %s ===\n\n",
252 $key->{comment};
253
254 $usign_markup .= sprintf " * Key-ID: ''%%%%%s%%%%''\n",
255 $key->{id};
256
257 $usign_markup .= sprintf " * Key-Data: ''%%%%%s%%%%''\n\n",
258 $key->{key};
259
260 $usign_markup .= sprintf "%s\n",
261 format_download($key);
262 }
263
264
265 $markup_template =~ s!
266 ( /\*\sBEGIN\sGPG\sKEYS\s\*/ )
267 .+
268 ( /\*\sEND\sGPG\sKEYS\s\*/ )
269 !
270 $1 . "\n\n" . $gpg_markup . $2;
271 !esx;
272
273 $markup_template =~ s!
274 ( /\*\sBEGIN\sUSIGN\sKEYS\s\*/ )
275 .+
276 ( /\*\sEND\sUSIGN\sKEYS\s\*/ )
277 !
278 $1 . "\n\n" . $usign_markup . $2;
279 !esx;
280
281
282 print $markup_template;