5ba8c126792afc10486632b237f86bacf5d05b75
5 # See the pod documentation at the end of this file for author,
6 # copyright, and licence information.
9 # libintl-perl (Locale::Recode)
10 # OR libtext-iconv-perl (Text::Iconv),
11 # OR the "recode" binary
16 # * use the user's normal keyring to find signatures
17 # * support for multiple user keys
18 # * better charset conversion
20 # see the Debian changelog for further changes.
22 my $VERSION = qq$Rev$;
23 $ENV{PATH
} .= ":/usr/share/signing-party";
37 gpgsigs $VERSION- http://pgp-tools.alioth.debian.org/
38 (c) 2004 Uli Martens <uli\@youam.net>
39 (c) 2004, 2005 Peter Palfrader <peter\@palfrader.org>
40 (c) 2004, 2005, 2006, 2007 Christoph Berg <cb\@df7cb.de>
46 my ($fd, $error) = @_;
51 Usage: $PROGRAM_NAME [-r] [-t <charset>] <keyid> <keytxt> [<outfile>]
53 keyid is a long or short keyid (e.g. DE7AAF6E94C09C7F or 94C09C7F)
54 separate multiple keyids with ','
55 -r call gpg --recv-keys before proceeding
56 -f <charset> convert <keytxt> from charset
57 -t <charset> convert UIDs to charset in output
58 --refresh regenerate UID lists on keys
59 --latex generate LaTeX output including photo IDs
65 my ($fromcharset, $charset, $recv_keys, $refresh, $latex);
66 Getopt
::Long
::config
('bundling');
68 '-f=s' => \
$fromcharset,
73 help
=> sub { usage
(*STDOUT
, 0); },
74 version
=> sub { version
(*STDOUT
); exit 0;},
75 ) or usage
(*STDERR
, 1);
79 $fromcharset ||= "ISO-8859-1";
80 $charset ||= $ENV{LC_ALL
} || $ENV{LC_CTYPE
} || $ENV{LANG
} || "ISO-8859-1";
81 $charset = "ISO-8859-1" unless $charset =~ /[\.-]/;
87 my ($text, $from, $to) = @_;
89 if (eval "require Locale::Recode") {
90 my $rt = Locale
::Recode
->new (from
=> $from, to
=> $to);
95 } elsif (eval "require Text::Iconv") {
96 my $it = Text
::Iconv
->new($from, $to);
98 my $result = $it->convert($text);
99 warn ("Could not convert '$text'\n") unless defined $result;
100 return (defined $result) ?
$result : $text
102 my $pid = open3
(\
*WTRFH
, \
*RDRFH
, \
*ERRFH
, 'recode', "utf8..$charset");
106 my $result = <RDRFH
>;
110 warn ("'recode' failed, is it installed?\n") unless defined $result;
111 return (defined $result) ?
$result : $text
117 my @mykeys = split /,/, uc(shift @ARGV);
118 my $keytxt = (shift @ARGV) || usage
(*STDERR
, 1);
119 my $outfile = (shift @ARGV) || '-';
121 map { s/^0x//i; } @mykeys;
122 my %uids = map { $_ => [] } @mykeys;
124 if (!@mykeys || scalar @ARGV) {
127 foreach my $falsekey (grep { $_ !~ /^([0-9A-F]{16,16}|[0-9A-F]{8,8})$/ } @mykeys) {
128 print STDERR
"Invalid keyid $falsekey given\n";
132 -r
$keytxt or die ("$keytxt does not exist\n");
135 # get list of keys in file
137 open (TXT
, $keytxt) or die ("Cannot open $keytxt\n");
139 if ( m/^pub +(?:\d+)[DR]\/([0-9A
-F
]{8}) [0-9]{4}-[0-9]{2}-[0-9]{2} *(.*)/ ) {
146 # get all known signatures
148 print STDERR
"Requesting keys from keyserver\n";
149 system "gpg --recv-keys @keys";
152 print STDERR
"Running --list-sigs, this will take a while ";
153 open SIGS
, "gpg --fixed-list-mode --with-colons --list-sigs @mykeys @keys 2>/dev/null |"
154 or die "can't get gpg listing";
156 my ($key, $uid, $sigs, $photocount);
158 if ( m/^pub:(?:.*?:){3,3}([0-9A-F]{16,16}):/ ) {
164 if ( m/^uid:(.):(?:.*?:){7,7}(.*):/s ) {
167 $uid =~ s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
168 $uid = myrecode
($uid, "UTF-8", $charset);
170 my ($shortkey) = substr $key, -8;
171 # Remember non-revoked uids
172 next if $uidstatus eq "r";
173 push @
{$uids{$shortkey}}, $uid;
176 if ( m/^uat:(.)::::[^:]+::([0-9A-F]+)::\d+ (\d+)/ ) { # uat:-::::2006-08-03::27BAEAF742BD253C2F3F03B043DC1536880193C4::1 7993:
178 # $2 is hash of attribute data
179 my $size = $3 - 19; # FIXME: find a nicer way to find out picture size
180 $uid = "[jpeg image of size $size]";
181 next if $uidstatus eq "r";
182 if ($latex and not $photocount) { # call once per key
183 my ($shortkey) = substr $key, -8;
184 system "rm $shortkey.[1-9]*.eps";
185 system "gpg --photo-viewer 'gpgsigs-eps-helper $shortkey' --list-options show-photos --list-key $key > /dev/null";
188 my ($shortkey) = substr $key, -8;
189 push @
{$uids{$shortkey}}, $uid;
192 if ( m/^sig:(?:.*?:){3,3}([0-9A-F]{8})([0-9A-F]{8}):(?:.*?:){5,5}(.*?):/ ) {
194 if ($class eq '10x') {
196 } elsif ($class eq '11x') {
198 } elsif ($class eq '12x') {
200 } elsif ($class eq '13x') {
205 # Handle the case where one UID was signed multiple times
206 # with different signature classes.
207 my $before = $sigs->{$key}->{$uid}->{$1.$2};
208 if (defined $before) {
209 if ($before eq 'S' || $before eq 's') {
210 $sigs->{$key}->{$uid}->{$1.$2} = $class;
211 } elsif ($class eq 'S' || $class eq 's') {
212 # intentionally left blank
213 } elsif ($before < $class) {
214 $sigs->{$key}->{$uid}->{$1.$2} = $class;
217 $sigs->{$key}->{$uid}->{$1.$2} .= $class;
219 $sigs->{$key}->{$uid}->{$2} = $sigs->{$key}->{$uid}->{$1.$2};
222 next if ( m/^(rev|rvk|sub|tru):/ ); # revoke/revoker/subkey/trust
223 warn "unknown value: '$_', key: ".(defined $key ?
$key :'none')."\n";
228 for my $k ( keys %{$sigs} ) {
229 if ( $k =~ m/^[0-9A-F]{8}([0-9A-F]{8})$/ ) {
230 $sigs->{$1} = $sigs->{$k};
236 open MD
, "gpg --with-colons --print-md md5 $keytxt|" or warn "can't get gpg md5\n";
239 open MD
, "gpg --with-colons --print-md sha1 $keytxt|" or warn "can't get gpg sha1\n";
242 open MD
, "gpg --with-colons --print-md sha256 $keytxt|" or warn "can't get gpg sha256\n";
245 open MD
, "gpg --with-colons --print-md ripemd160 $keytxt|" or warn "can't get gpg ripemd160\n";
246 my $RIPEMD160 = <MD
>;
249 my @MD5 = split /:/, $MD5;
250 my @SHA1 = split /:/, $SHA1;
251 my @SHA256 = split /:/, $SHA256;
252 my @RIPEMD160 = split /:/, $RIPEMD160;
255 $SHA256 = $SHA256[2];
256 $RIPEMD160 = $RIPEMD160[2];
258 $MD5 =~ s/(.{16})/$1 /;
259 $SHA1 =~ s/(.{20})/$1 /;
260 $SHA256 =~ s/(.{32})/$1 /;
261 $RIPEMD160 =~ s/(.{20})/$1 /;
262 $MD5 =~ s/([0-9A-Z]{2})/$1 /ig;
263 $SHA1 =~ s/([0-9A-Z]{4})/$1 /ig;
264 $SHA256 =~ s/([0-9A-Z]{4})/$1 /ig;
265 $RIPEMD160 =~ s/([0-9A-Z]{4})/$1 /ig;
271 my $metatxt = quotemeta($keytxt);
272 $MD5 =~ s/^$metatxt:\s*//;
273 $SHA1 =~ s/^$metatxt:\s*//;
274 $SHA256 =~ s/^$metatxt:\s*//;
275 $RIPEMD160 =~ s/^$metatxt:\s*//;
281 my ($key, $uid) = @_;
282 if (! defined $sigs->{$key}->{$uid}) {
283 warn "uid '$uid' not found on key $key\n";
284 #for (keys %{ $sigs->{$key} }) {
285 # print STDERR "only have $_\n";
287 return '(' . (' ' x
@mykeys) . ')';
290 foreach my $mykey (@mykeys) {
291 $r .= defined $sigs->{$key}->{$uid}->{$mykey} ?
$sigs->{$key}->{$uid}->{$mykey} : ' ';
300 print STDERR
"Annotating $keytxt, writing into $outfile\n";
301 open (TXT
, $keytxt) or die ("Cannot open $keytxt\n");
302 open (WRITE
, '>'.$outfile) or die ("Cannot open $outfile for writing\n");
306 \documentclass{article}
307 \usepackage[margin=2cm]{geometry}
309 \usepackage{graphicx}
317 $_ = myrecode
($_, $fromcharset, $charset);
318 if (/^MD5 Checksum:/ && defined $MD5) {
319 s/[_[:xdigit:]][_ [:xdigit:]]+_/$MD5/;
321 if (/^SHA1 Checksum:/ && defined $SHA1) {
322 s/[_[:xdigit:]][_ [:xdigit:]]+_/$SHA1/;
324 if (/^SHA256 Checksum:/ && defined $SHA256) {
325 s/[_[:xdigit:]][_ [:xdigit:]]+_/$SHA256/;
327 if (/^RIPEMD160 Checksum:/ && defined $RIPEMD160) {
328 s/[_[:xdigit:]][_ [:xdigit:]]+_/$RIPEMD160/;
330 if ( m/^pub +(?:\d+)[DR]\/([0-9A
-F
]{8}) [0-9]{4}-[0-9]{2}-[0-9]{2} *(.*)/ ) {
333 #if ($uid) { # in gpg 1.2, the first uid is here
334 # print WRITE print_tag($key, $uid) . " $_";
342 if ( m/^ *Key fingerprint/ ) {
345 foreach my $mykey (@mykeys) {
346 foreach my $myuid (@
{$uids{$mykey}}) {
347 $inc .= defined $sigs->{$mykey}->{$myuid}->{$key} ?
$sigs->{$mykey}->{$myuid}->{$key} : ' ';
350 print WRITE
"[$inc] incoming signatures\n" if $inc =~ /\S/;
351 if ($refresh or $latex) {
352 foreach $uid (@
{$uids{$key}}) {
353 print WRITE print_tag
($key, $uid) . " $uid\n";
354 if ($latex and ($uid =~ /^\[jpeg image/)) {
356 print WRITE
"\\begin{flushright}\n";
357 print WRITE
"\\includegraphics[height=3cm]{$key.$photocount.eps}\n";
358 print WRITE
"\\end{flushright}\n";
365 if ( m/^uid +(.*)$/ ) {
367 next if $refresh or $latex;
368 unless (defined $key) {
369 warn "key is undefined - input text is possibly malformed near line $line\n";
372 die "bad tag from $key | $uid" unless defined (print_tag
($key, $uid));
373 print WRITE print_tag
($key, $uid) . " $_";
379 print WRITE
"Legend:\n";
381 foreach my $i (0 .. @mykeys - 1) {
382 print WRITE
'(' . ' 'x
$i . 'S' . ' 'x
(@mykeys-$i-1) . ") signed with $mykeys[$i] $uids{$mykeys[$i]}->[0]\n";
383 $num_myuids += @
{$uids{$mykeys[$i]}};
386 foreach my $mykey (@mykeys) {
387 foreach my $myuid (@
{$uids{$mykey}}) {
388 my $inc = defined $sigs->{$mykey}->{$myuid}->{$key} ?
$sigs->{$mykey}->{$myuid}->{$key} : ' ';
389 print WRITE
"[" . ' 'x
$i . 'S' . ' 'x
($num_myuids-$i-1) . "] has signed $mykey $myuid\n";
408 B<gpgsigs> - annotate list of GnuPG keys with already done signatures
412 B<gpgsigs> [I<options>] I<keyid>I<[>B<,>I<keyidI<[>B<,>I<...>I<]>>I<]> F<keytxt> [F<outfile>]
416 B<gpgsigs> was written to assist the user in signing keys during a keysigning
417 party. It takes as input a file containing keys in C<gpg --list-keys> format
418 and prepends every line with a tag indicating if the user has already signed
419 that uid. When the file contains C<ALGO Checksum:> lines and placeholders
420 (C<__ __>), the checksum is inserted. ALGO can be set to the following algorithms:
421 MD5 SHA1 SHA256 or RIPEMD160.
429 Call I<gpg --recv-keys> before creating the output.
431 =item B<-f> I<charset>
433 Convert F<keytxt> from I<charset>. The default is ISO-8859-1.
435 =item B<-t> I<charset>
437 Convert UIDs to I<charset>. The default is derived from LC_ALL, LC_CTYPE, and
438 LANG, and if all these are unset, the default is ISO-8859-1.
442 Refresh the UID lists per key from gpg. Useful when UIDs were added or revoked
443 since the input text was generated.
447 Generate LaTeX output, including photo IDs. Implies B<--refresh>.
448 B<Note:> This writes eps files to the current directory.
452 Use this keyid (8 or 16 byte) for annotation. Multiple keyids can be separated
457 Read input from F<keytxt>.
461 Write output to F<outfile>. Default is stdout.
467 The following key signing parties are using B<gpgsigs>:
469 http://www.palfrader.org/ksp-lt2k4.html
471 http://www.palfrader.org/ksp-lt2k5.html
475 B<GnuPG> is known to change its output format quite often. This version has
476 been tested with gpg 1.2.5 and gpg 1.4.1. YMMV.
482 http://pgp-tools.alioth.debian.org/
484 =head1 AUTHORS AND COPYRIGHT
486 (c) 2004 Uli Martens <uli@youam.net>
488 (c) 2004, 2005 Peter Palfrader <peter@palfrader.org>
490 (c) 2004, 2005, 2006, 2007 Christoph Berg <cb@df7cb.de>
496 Redistribution and use in source and binary forms, with or without
497 modification, are permitted provided that the following conditions
500 1. Redistributions of source code must retain the above copyright
501 notice, this list of conditions and the following disclaimer.
503 2. Redistributions in binary form must reproduce the above copyright
504 notice, this list of conditions and the following disclaimer in the
505 documentation and/or other materials provided with the distribution.
507 3. The name of the author may not be used to endorse or promote products
508 derived from this software without specific prior written permission.
510 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
511 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
512 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
513 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
514 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
515 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
516 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
517 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
518 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
519 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.