Merge pull request #1 from ldir-EDB0/master
[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://lede-project.org/docs/user-guide/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.lede-project.org/?p=keyring.git;a=history;f=%s|Last change: %s]] | " .
103 "[[https://git.lede-project.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}, 'lede-project.org') >= 0) ||
170 (index($data{email}, 'lists.infradead.org') >= 0);
171
172 push @pubkeys, \%data;
173 }
174 }
175
176 close KEYS;
177 }
178
179 my $gpg_markup = '';
180
181 foreach my $key (sort {
182 !$a->{is_system_key} <=> !$b->{is_system_key} ||
183 $a->{name} cmp $b->{name}
184 } @pubkeys) {
185
186 $gpg_markup .= sprintf "---\n\n=== %s ===\n\n",
187 format_title($key);
188
189 $gpg_markup .= sprintf "User ID: **%s** <%s>\\\\\n",
190 $key->{name}, $key->{email};
191
192 $gpg_markup .= sprintf "Public Key: 0x%s**%s** (%s)\\\\\n",
193 substr($key->{eid}, 0, 8), substr($key->{eid}, 8),
194 format_keytype($key, 0);
195
196 $gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
197 format_fingerprint($key, 0);
198
199 if (exists $key->{stype}) {
200 $gpg_markup .= sprintf "Signing Subkey: 0x%s **%s** (%s)\\\\\n",
201 substr($key->{seid}, 0, 8), substr($key->{seid}, 8),
202 format_keytype($key, 1);
203
204 $gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
205 format_fingerprint($key, 1);
206 }
207
208 $gpg_markup .= sprintf "%s\n", format_download($key);
209 }
210
211
212 my @usignkeys;
213
214 if (open KEYS, '-|', qw(find usign/ -type f -name *[0-9a-f] -print)) {
215 while (defined(my $file = readline KEYS)) {
216 chomp $file;
217
218 if (open USIGN, '<', $file) {
219 my %data;
220
221 while (defined(my $line = readline USIGN)) {
222 chomp $line;
223
224 if ($line =~ m!^untrusted comment: (.+)$!) {
225 $data{comment} = $1;
226 }
227 else {
228 $data{key} = $line;
229 }
230 }
231
232 close USIGN;
233
234 $file =~ m!/([0-9a-f]{16})$!;
235
236 $data{id} = $1;
237 $data{filename} = $file;
238
239 push @usignkeys, \%data;
240 }
241 }
242
243 close KEYS;
244 }
245
246 my $usign_markup = '';
247
248 foreach my $key (sort { $a->{comment} cmp $b->{comment} } @usignkeys) {
249 $usign_markup .= sprintf "---\n\n=== %s ===\n\n",
250 $key->{comment};
251
252 $usign_markup .= sprintf " * Key-ID: ''%%%%%s%%%%''\n",
253 $key->{id};
254
255 $usign_markup .= sprintf " * Key-Data: ''%%%%%s%%%%''\n\n",
256 $key->{key};
257
258 $usign_markup .= sprintf "%s\n",
259 format_download($key);
260 }
261
262
263 $markup_template =~ s!
264 ( /\*\sBEGIN\sGPG\sKEYS\s\*/ )
265 .+
266 ( /\*\sEND\sGPG\sKEYS\s\*/ )
267 !
268 $1 . "\n\n" . $gpg_markup . $2;
269 !esx;
270
271 $markup_template =~ s!
272 ( /\*\sBEGIN\sUSIGN\sKEYS\s\*/ )
273 .+
274 ( /\*\sEND\sUSIGN\sKEYS\s\*/ )
275 !
276 $1 . "\n\n" . $usign_markup . $2;
277 !esx;
278
279
280 print $markup_template;