* Import keyanalyze into signing-party. Thanks to Matthew Wilcox for the
[pgp-tools.git] / gpg-key2ps / gpg-key2ps
1 #!/usr/bin/perl -w
2 #
3 # gpg-key2ps: convert a PGP/GnuPG key into paper slips.
4 # Copyright (C) 2001-2005 Simon Richter
5 # Copyright (C) 2005-2007 Thijs Kinkhorst
6 # Copyright (C) 2005-2008 Christoph Berg <cb@df7cb.de>
7 # Licenced under the GNU General Public License,
8 # version 2 or later.
9 #
10 # $Id$
11
12 use strict;
13 use Getopt::Long;
14
15 my $version = '$Rev$';
16 $version =~ s/\$Rev:\s*(\d+)\s*\$/$1/;
17 my $revokestyle = "hide";
18 my $columns = 2;
19 my $creationdate = scalar(localtime);
20
21 sub version($) {
22 my $fd = shift;
23 print $fd "gpg-key2ps $version - (c) 2001-2008 Simon Richter, Thijs Kinkhorst, Christoph Berg\n";
24 }
25
26 sub usage($$) {
27 my ($fd, $exitcode) = @_;
28 version ($fd);
29 print $fd <<EOF;
30 Usage: $0 [-p papersize] [-r revoked-style] [-1] keyid-or-name ...
31 Options:
32 -p --paper-size
33 -r --revoked-style
34 hide - Don't show revoked uids and subkeys (default)
35 grey - Print text in grey
36 note - Add "[revoked]"
37 show - List revoked uids normally
38 strike - Strike through lines
39 -1 Only print one column, for extra wide keys
40 -h --help
41 -v --version
42 EOF
43 exit $exitcode;
44 }
45
46 # fetch command line parameters
47 my $opts;
48 Getopt::Long::config('bundling');
49 if (!GetOptions (
50 '-h' => \$opts->{help},
51 '--help' => \$opts->{help},
52 '-v' => \$opts->{version},
53 '--version' => \$opts->{version},
54 '-p=s' => \$opts->{papersize},
55 '--paper-size=s' => \$opts->{papersize},
56 '-r=s' => \$opts->{revokestyle},
57 '--revoked-style=s' => \$opts->{revokestyle},
58 '-1' => \$opts->{1},
59 )) {
60 usage(\*STDERR, 1);
61 }
62
63 if ($opts->{help}) {
64 usage (\*STDOUT, 0);
65 }
66
67 if ($opts->{version}) {
68 version (\*STDOUT);
69 exit 0;
70 }
71
72 if ( $opts->{revokestyle} ) { $revokestyle = $opts->{revokestyle}; }
73 if ( $opts->{papersize} ) { $ENV{'PAPERSIZE'} = $opts->{papersize}; }
74
75 if ( $revokestyle !~ /^(grey|hide|note|show|strike)$/ ) {
76 print STDERR "Unknown revoked-style \"$revokestyle\".\n";
77 usage (\*STDERR, 1);
78 }
79
80 if ( $opts->{1} ) { $columns = 1; }
81
82 usage(\*STDERR, 1) unless scalar @ARGV >= 1;
83
84 # determine the paper size through the paperconf tool
85 my $w; my $h;
86 if ( `which paperconf` && $? == 0 ) {
87 $w=`paperconf -w`;
88 $h=`paperconf -h`;
89 chomp($w);
90 chomp($h);
91 } else {
92 # Default to A4.
93 print STDERR "Warning: libpaper-utils is not installed, defaulting to A4.\n";
94 $w=596;
95 $h=842;
96 }
97
98 # open a gpg process we'll be reading from below
99 map { s/'/'\\''/g; } @ARGV; # quote single quotes
100 # --list-key due to #382794
101 open(GPG, "gpg --list-key --with-fingerprint --with-colons '". (join "' '", @ARGV) ."' |");
102
103 sub start_postscript {
104 # start the PostScript output
105 print <<EOF;
106 %!PS-Adobe-3.0
107 %%BoundingBox: 0 0 $w $h
108 %%Title:
109 %%Creator: gpg-key2ps $version
110 %%CreationDate: $creationdate
111 %%Pages: 1
112 %%EndComments
113
114 %%Page: 1 1
115
116 /w $w def
117 /h $h def
118
119 /Times-Roman findfont 9 scalefont setfont
120
121 /newline {
122 /y y 10 sub def
123 } def
124
125 /hline {
126 30 y 3 add moveto
127 w $columns div 30 sub y 3 add lineto stroke
128 newline
129 } def
130
131 /needhline {
132 /condhline { hline } def
133 } def
134
135 /noneedhline {
136 /condhline { } def
137 } def
138
139 /showAlgorithm {
140 << 1 (R) 2 (r) 3 (s) 16 (g) 20 (G) 17 (D) >> exch get
141 show
142 } def
143
144 /pub {
145 condhline
146 50 y moveto (pub) show
147 70 y moveto show showAlgorithm (/) show show
148 150 y moveto show
149 200 y moveto show
150 newline
151 needhline
152 } def
153
154 /fpr {
155 70 y moveto (Key fingerprint = ) show show
156 newline
157 } def
158
159 /uid {
160 50 y moveto (uid) show
161 200 y moveto show
162 newline
163 } def
164
165 /sbk {
166 50 y moveto (sub) show
167 70 y moveto show showAlgorithm (/) show show
168 150 y moveto show
169 newline
170 } def
171
172 EOF
173
174 # output the desired display for revoked uids
175 if ( $revokestyle eq "grey" ) {
176 print <<EOF;
177 /revuid {
178 .5 setgray
179 uid
180 0 setgray
181 } def
182 /revsbk {
183 .5 setgray
184 sbk
185 0 setgray
186 } def
187 EOF
188 } elsif ( $revokestyle eq "note" ) {
189 print <<EOF;
190 /revuid {
191 50 y moveto (uid) show
192 200 y moveto show ( [revoked]) show
193 newline
194 } def
195 /revsbk {
196 sbk
197 ( [revoked]) show
198 } def
199 EOF
200 } elsif ( $revokestyle eq "show" ) {
201 print <<EOF;
202 /revuid { uid } def
203 /revsbk { sbk } def
204 EOF
205 } elsif ( $revokestyle eq "strike" ) {
206 print <<EOF;
207 /revuid {
208 uid
209 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
210 } def
211 /revsbk {
212 sbk
213 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
214 } def
215 EOF
216 }
217
218 print <<EOF;
219
220 /key {
221 noneedhline
222 EOF
223 } # sub start_postscript
224
225 # walk the output of gpg line by line
226 # $numlines has the total number of lines so we'll know how many to put on page
227 my $numlines = 0;
228 my $started = 0;
229 while(<GPG>) {
230 # we don't use these
231 if ( /^(tru|uat):/ ) { next; }
232 # every primary uid causes an extra line because of the separator
233 if ( /^pub:/ ) {
234 start_postscript() unless $started;
235 $started = 1;
236 $numlines++;
237 }
238 # primary uid
239 s/^pub:[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:.*/ ($5) ($4) ($3) $2 ($1) pub/;
240 # fingerprint, format it nicely with spaces
241 if ( /^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ) {
242 my $fpr = $1;
243 # v4 key
244 $fpr =~ s/(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})/$1 $2 $3 $4 $5 $6 $7 $8 $9 $10/;
245 # v3 key
246 $fpr =~ s/(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})/$1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 $16/g;
247 $_ = " ($fpr) fpr\n";
248 }
249 # user ids
250 s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
251 $_ = `echo "$_" | iconv -ct latin1`;
252 s/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) uid/;
253 # revoked user id
254 if (s/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) revuid/) {
255 next if $revokestyle eq "hide";
256 }
257 # subkey
258 s/^sub:[^r:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) sbk/;
259 if (s/^sub:r[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) revsbk/) {
260 next if $revokestyle eq "hide";
261 }
262 $numlines++;
263 # print this line
264 print;
265 }
266 close(GPG);
267
268 unless ($started) {
269 print STDERR "No public key found.\n";
270 exit 1;
271 }
272
273 # output the remaining postscript
274 print <<EOF;
275 } def
276
277 /numlines $numlines def
278 /num w 16 sub 10 div numlines div def
279
280 /column {
281 /y w 20 sub def
282 1 1 num {
283 gsave
284 0 0 h $columns div w rectclip
285 /upper y 11 add def
286 key
287 newline
288 /lower y 11 add def
289 0 upper h $columns div upper h $columns div lower 0 lower 0 upper moveto lineto lineto lineto lineto stroke
290 grestore
291 } for
292 } def
293
294 w 0 translate
295 90 rotate
296 column
297 EOF
298
299 if ( $columns == 2 ) {
300 print <<EOF;
301 h $columns div 0 translate
302 column
303
304 EOF
305 }
306
307 print <<EOF;
308 showpage
309
310 %%Trailer
311 %%EOF
312 EOF
313
314 # done!
315 exit 0;
316
317
318 __END__
319
320 =head1 NAME
321
322 B<gpg-key2ps> - generates a PS file from a GnuPG keyring
323
324 =head1 SYNOPSIS
325
326 B<gpg-key2ps> [B<-r> I<revoked-style>] [B<-p> I<papersize>] I<keyid-or-name> [ I<...> ]
327
328 =head1 DESCRIPTION
329
330 gpg-key2ps generates a PostScript file with your OpenPGP key fingerprint (repeated
331 as often as it fits) useful for keysigning parties. The only argument is the same
332 as you would pass to GPG's list-keys command, either a key-id or a (partial) name.
333 The PS data is written to stdout.
334
335 =head1 OPTIONS
336
337 =over
338
339 =item B<-p> B<--paper-size> I<paper-size>
340
341 Select the output paper size. Default is to look into /etc/papersize or A4 if
342 libpaper isn't installed.
343
344 =item B<-r> B<--revoked-style> I<revoked-style>
345
346 Select how to mark revoked UIDs and subkeys. Five styles are available:
347 B<hide> don't show at all (default),
348 B<show> show normally,
349 B<grey> display in 50% grey,
350 B<note> add "[revoked]", and
351 B<strike> strike through.
352
353 =item I<keyid>
354
355 Keyids to print. Multiple can be separated by spaces.
356
357 =item B<-h> B<--help>
358
359 Print usage and exit.
360
361 =item B<-v> B<--version>
362
363 Print version and exit.
364
365 =back
366
367 =head1 SEE ALSO
368
369 =over
370
371 =item gpg(1)
372
373 GNU Privacy Guard.
374
375 =item http://pgp-tools.alioth.debian.org/
376
377 The homepage of B<gpg-key2ps> and the other tools bundled in B<signing-party>.
378
379 =item http://www.debian.org/events/materials/business-cards/
380
381 B<gpg-key2ps> prints plain fingerprint slips. If you are looking for something
382 more stylish, look at these latex templates for business cards that also
383 include fingerprints.
384
385 =back
386
387 =head1 AUTHORS AND COPYRIGHT
388
389 =over
390
391 =item (c) 2001-2005 Simon Richter <sjr@debian.org>
392
393 =item (c) 2005-2007 Thijs Kinkhorst <thijs@kinkhorst.com>
394
395 =item (c) 2005-2008 Christoph Berg <cb@df7cb.de>
396
397 =back