File Coverage

blib/lib/Module/Signature.pm
Criterion Covered Total %
statement 53 410 12.9
branch 0 224 0.0
condition 0 129 0.0
subroutine 18 41 43.9
pod 0 2 0.0
total 71 806 8.8


line stmt bran cond sub pod time code
1 2     2   267497 use 5.005;
  2         7  
2 2     2   10 use strict;
  2         3  
  2         66  
3 2     2   8 use warnings;
  2         3  
  2         183  
4              
5             # ABSTRACT: Module signature file manipulation
6             package Module::Signature;
7             our $VERSION = '0.93'; #VERSION
8              
9 2     2   11 use vars qw($VERSION $SIGNATURE @ISA @EXPORT_OK);
  2         4  
  2         166  
10 2     2   10 use vars qw($Preamble $Cipher $Debug $Verbose $Timeout $AUTHOR);
  2         3  
  2         165  
11 2     2   13 use vars qw($KeyServer $KeyServerPort $AutoKeyRetrieve $CanKeyRetrieve);
  2         4  
  2         110  
12 2     2   10 use vars qw($LegacySigFile);
  2         3  
  2         121  
13              
14 2     2   13 use constant CANNOT_VERIFY => '0E0';
  2         5  
  2         192  
15 2     2   10 use constant SIGNATURE_OK => 0;
  2         3  
  2         144  
16 2     2   12 use constant SIGNATURE_MISSING => -1;
  2         3  
  2         104  
17 2     2   9 use constant SIGNATURE_MALFORMED => -2;
  2         3  
  2         111  
18 2     2   9 use constant SIGNATURE_BAD => -3;
  2         3  
  2         106  
19 2     2   12 use constant SIGNATURE_MISMATCH => -4;
  2         3  
  2         100  
20 2     2   14 use constant MANIFEST_MISMATCH => -5;
  2         3  
  2         68  
21 2     2   15 use constant CIPHER_UNKNOWN => -6;
  2         4  
  2         75  
22              
23 2     2   1070 use ExtUtils::Manifest ();
  2         22506  
  2         73  
24 2     2   15 use Exporter;
  2         15  
  2         81  
25 2     2   9 use File::Spec;
  2         4  
  2         12548  
26              
27             @EXPORT_OK = (
28             qw(sign verify),
29             qw($SIGNATURE $AUTHOR $KeyServer $Cipher $Preamble),
30             (grep { /^[A-Z_]+_[A-Z_]+$/ } keys %Module::Signature::),
31             );
32             @ISA = 'Exporter';
33              
34             $AUTHOR = $ENV{MODULE_SIGNATURE_AUTHOR};
35             $SIGNATURE = 'SIGNATURE';
36             $Timeout = $ENV{MODULE_SIGNATURE_TIMEOUT} || 3;
37             $Verbose = $ENV{MODULE_SIGNATURE_VERBOSE} || 0;
38             $KeyServer = $ENV{MODULE_SIGNATURE_KEYSERVER} || 'keyserver.ubuntu.com';
39             $KeyServerPort = $ENV{MODULE_SIGNATURE_KEYSERVERPORT} || '11371';
40             $Cipher = $ENV{MODULE_SIGNATURE_CIPHER} || 'SHA256';
41             $Preamble = << ".";
42             This file contains message digests of all files listed in MANIFEST,
43             signed via the Module::Signature module, version $VERSION.
44              
45             To verify the content in this distribution, first make sure you have
46             Module::Signature installed, then type:
47              
48             % cpansign -v
49              
50             It will check each file's integrity, as well as the signature's
51             validity. If "==> Signature verified OK! <==" is not displayed,
52             the distribution may already have been compromised, and you should
53             not run its Makefile.PL or Build.PL.
54              
55             .
56              
57             $AutoKeyRetrieve = 1;
58             $CanKeyRetrieve = undef;
59             $LegacySigFile = 0;
60              
61             sub _cipher_map {
62 0     0     my($sigtext) = @_;
63 0           my @lines = split /\015?\012/, $sigtext;
64 0           my %map;
65 0           for my $line (@lines) {
66 0 0         last if $line eq '-----BEGIN PGP SIGNATURE-----';
67 0 0         next if $line =~ /^---/ .. $line eq '';
68 0 0         next if $line eq '';
69 0           my($cipher,$digest,$file) = split " ", $line, 3;
70 0 0         return unless defined $file;
71 0           $map{$file} = [$cipher, $digest];
72             }
73 0           return \%map;
74             }
75              
76             sub verify {
77 0     0 0   my %args = ( skip => $ENV{TEST_SIGNATURE}, @_ );
78 0           my $rv;
79              
80 0 0         (-r $SIGNATURE) or do {
81 0           warn "==> MISSING Signature file! <==\n";
82 0           return SIGNATURE_MISSING;
83             };
84              
85 0 0         (my $sigtext = _read_sigfile($SIGNATURE)) or do {
86 0           warn "==> MALFORMED Signature file! <==\n";
87 0           return SIGNATURE_MALFORMED;
88             };
89              
90 0 0         (my ($cipher_map) = _cipher_map($sigtext)) or do {
91 0           warn "==> MALFORMED Signature file! <==\n";
92 0           return SIGNATURE_MALFORMED;
93             };
94              
95 0 0         (defined(my $plaintext = _mkdigest($cipher_map))) or do {
96 0           warn "==> UNKNOWN Cipher format! <==\n";
97 0           return CIPHER_UNKNOWN;
98             };
99              
100 0           $rv = _verify($SIGNATURE, $sigtext, $plaintext);
101              
102 0 0         if ($rv == SIGNATURE_OK) {
    0          
    0          
103 0           my ($mani, $file) = _fullcheck($args{skip});
104              
105 0 0 0       if (@{$mani} or @{$file}) {
  0            
  0            
106 0           warn "==> MISMATCHED content between MANIFEST and distribution files! <==\n";
107 0           return MANIFEST_MISMATCH;
108             }
109             else {
110 0 0         warn "==> Signature verified OK! <==\n" if $Verbose;
111             }
112             }
113             elsif ($rv == SIGNATURE_BAD) {
114 0           warn "==> BAD/TAMPERED signature detected! <==\n";
115             }
116             elsif ($rv == SIGNATURE_MISMATCH) {
117 0           warn "==> MISMATCHED content between SIGNATURE and distribution files! <==\n";
118             }
119              
120 0           return $rv;
121             }
122              
123             sub _verify {
124 0   0 0     my $signature = shift || $SIGNATURE;
125 0   0       my $sigtext = shift || '';
126 0   0       my $plaintext = shift || '';
127              
128             # Avoid loading modules from relative paths in @INC.
129 0           local @INC = grep { File::Spec->file_name_is_absolute($_) } @INC;
  0            
130 0 0         local $SIGNATURE = $signature if $signature ne $SIGNATURE;
131              
132 0 0 0       if ($AutoKeyRetrieve and !$CanKeyRetrieve) {
133 0 0         if (!defined $CanKeyRetrieve) {
134 0           require IO::Socket::INET;
135 0           my $sock = IO::Socket::INET->new(
136             Timeout => $Timeout,
137             PeerAddr => "$KeyServer:$KeyServerPort",
138             );
139 0 0         $CanKeyRetrieve = ($sock ? 1 : 0);
140 0 0         $sock->shutdown(2) if $sock;
141             }
142 0           $AutoKeyRetrieve = $CanKeyRetrieve;
143             }
144              
145 0 0         if (my $version = _has_gpg()) {
    0          
146 0           return _verify_gpg($sigtext, $plaintext, $version);
147             }
148 0           elsif (eval {require Crypt::OpenPGP; 1}) {
  0            
149 0           return _verify_crypt_openpgp($sigtext, $plaintext);
150             }
151             else {
152 0           warn "Cannot use GnuPG or Crypt::OpenPGP, please install either one first!\n";
153 0           return _compare($sigtext, $plaintext, CANNOT_VERIFY);
154             }
155             }
156              
157             sub _has_gpg {
158 0 0   0     my $gpg = _which_gpg() or return;
159 0 0         `$gpg --version` =~ /GnuPG.*?(\S+)\s*$/m or return;
160 0           return $1;
161             }
162              
163             sub _fullcheck {
164 0     0     my $skip = shift;
165 0           my @extra;
166              
167 0           local $^W;
168 0           local $ExtUtils::Manifest::Quiet = 1;
169              
170 0           my($mani, $file);
171 0 0         if( _legacy_extutils() ) {
172 0           my $_maniskip;
173 0 0         if ( _public_maniskip() ) {
174 0           $_maniskip = &ExtUtils::Manifest::maniskip;
175             } else {
176 0           $_maniskip = &ExtUtils::Manifest::_maniskip;
177             }
178              
179             local *ExtUtils::Manifest::_maniskip = sub { sub {
180 0 0         return unless $skip;
181 0           my $ok = $_maniskip->(@_);
182 0 0 0       if ($ok ||= (!-e 'MANIFEST.SKIP' and _default_skip(@_))) {
      0        
183 0           print "Skipping $_\n" for @_;
184 0           push @extra, @_;
185             }
186 0           return $ok;
187 0     0     } };
  0            
188              
189 0           ($mani, $file) = ExtUtils::Manifest::fullcheck();
190             }
191             else {
192 0           my $_maniskip = &ExtUtils::Manifest::maniskip;
193             local *ExtUtils::Manifest::maniskip = sub { sub {
194 0 0         return unless $skip;
195 0           return $_maniskip->(@_);
196 0     0     } };
  0            
197 0           ($mani, $file) = ExtUtils::Manifest::fullcheck();
198             }
199              
200 0           foreach my $makefile ('Makefile', 'Build') {
201             warn "==> SKIPPED CHECKING '$_'!" .
202             (-e "$_.PL" && " (run $_.PL to ensure its integrity)") .
203 0   0       " <===\n" for grep $_ eq $makefile, @extra;
204             }
205              
206 0           @{$mani} = grep {$_ ne 'SIGNATURE'} @{$mani};
  0            
  0            
  0            
207              
208 0           warn "Not in MANIFEST: $_\n" for @{$file};
  0            
209 0           warn "No such file: $_\n" for @{$mani};
  0            
210              
211 0           return ($mani, $file);
212             }
213              
214             sub _legacy_extutils {
215             # ExtUtils::Manifest older than 1.58 does not handle MYMETA.
216 0     0     return (ExtUtils::Manifest->VERSION < 1.58);
217             }
218              
219             sub _public_maniskip {
220             # ExtUtils::Manifest 1.54 onwards have public maniskip
221 0     0     return (ExtUtils::Manifest->VERSION > 1.53);
222             }
223              
224             sub _default_skip {
225 0     0     local $_ = shift;
226 0 0 0       return 1 if /\bRCS\b/ or /\bCVS\b/ or /\B\.svn\b/ or /,v$/
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
227             or /^MANIFEST\.bak/ or /^Makefile$/ or /^blib\//
228             or /^MakeMaker-\d/ or /^pm_to_blib/ or /^blibdirs/
229             or /^_build\// or /^Build$/ or /^pmfiles\.dat/
230             or /^MYMETA\./
231             or /~$/ or /\.old$/ or /\#$/ or /^\.#/;
232             }
233              
234             my $which_gpg;
235             sub _which_gpg {
236             # Cache it so we don't need to keep checking.
237 0 0   0     return $which_gpg if $which_gpg;
238              
239 0           for my $gpg_bin ('gpg', 'gpg2', 'gnupg', 'gnupg2') {
240 0           my $version = `$gpg_bin --version 2>&1`;
241 0 0 0       if( $version && $version =~ /GnuPG/ ) {
242 0           $which_gpg = $gpg_bin;
243 0           return $which_gpg;
244             }
245             }
246             }
247              
248             sub _verify_gpg {
249 0     0     my ($sigtext, $plaintext, $version) = @_;
250              
251 0 0 0       local $SIGNATURE = Win32::GetShortPathName($SIGNATURE)
252             if defined &Win32::GetShortPathName and $SIGNATURE =~ /[^-\w.:~\\\/]/;
253              
254 0           my $keyserver = _keyserver($version);
255              
256 0           require File::Temp;
257 0           my $fh = File::Temp->new();
258 0   0       print $fh $sigtext || _read_sigfile($SIGNATURE);
259 0           close $fh;
260              
261 0           my $gpg = _which_gpg();
262 0 0         my @quiet = $Verbose ? () : qw(-q --logger-fd=1);
263 0 0 0       my @cmd = (
    0          
264             $gpg, qw(--verify --batch --no-tty), @quiet, ($KeyServer ? (
265             "--keyserver=$keyserver",
266             ($AutoKeyRetrieve and $version ge '1.0.7')
267             ? '--keyserver-options=auto-key-retrieve'
268             : ()
269             ) : ()), $fh->filename
270             );
271              
272 0           my $output = '';
273 0 0         if( $Verbose ) {
274 0           warn "Executing @cmd\n";
275 0           system @cmd;
276             }
277             else {
278 0           my $cmd = join ' ', @cmd;
279 0           $output = `$cmd`;
280             }
281 0           unlink $fh->filename;
282              
283 0 0         if( $? ) {
    0          
284 0           print STDERR $output;
285             }
286             elsif ($output =~ /((?: +[\dA-F]{4}){10,})/) {
287 0           warn "WARNING: This key is not certified with a trusted signature!\n";
288 0           warn "Primary key fingerprint:$1\n";
289             }
290              
291 0 0 0       return SIGNATURE_BAD if ($? and $AutoKeyRetrieve);
292 0 0         return _compare($sigtext, $plaintext, (!$?) ? SIGNATURE_OK : CANNOT_VERIFY);
293             }
294              
295             sub _keyserver {
296 0     0     my $version = shift;
297 0           my $scheme = 'x-hkp';
298 0 0         $scheme = 'hkp' if $version ge '1.2.0';
299              
300 0           return "$scheme://$KeyServer:$KeyServerPort";
301             }
302              
303             sub _verify_crypt_openpgp {
304 0     0     my ($sigtext, $plaintext) = @_;
305              
306 0           require Crypt::OpenPGP;
307 0 0         my $pgp = Crypt::OpenPGP->new(
308             ($KeyServer) ? ( KeyServer => $KeyServer, AutoKeyRetrieve => $AutoKeyRetrieve ) : (),
309             );
310 0 0         my $rv = $pgp->handle( Data => $sigtext )
311             or die $pgp->errstr;
312              
313 0 0 0       return SIGNATURE_BAD if (!$rv->{Validity} and $AutoKeyRetrieve);
314              
315 0 0         if ($rv->{Validity}) {
316             warn 'Signature made ', scalar localtime($rv->{Signature}->timestamp),
317 0 0         ' using key ID ', substr(uc(unpack('H*', $rv->{Signature}->key_id)), -8), "\n",
318             "Good signature from \"$rv->{Validity}\"\n" if $Verbose;
319             }
320             else {
321 0           warn "Cannot verify signature; public key not found\n";
322             }
323              
324 0 0         return _compare($sigtext, $plaintext, $rv->{Validity} ? SIGNATURE_OK : CANNOT_VERIFY);
325             }
326              
327             sub _read_sigfile {
328 0     0     my $sigfile = shift;
329 0           my $signature = '';
330 0           my $well_formed;
331              
332             my $sigfile_fh;
333 0 0         open ($sigfile_fh, '<', $sigfile) or die "Could not open $sigfile: $!";
334              
335 0 0 0       if ($] >= 5.006 and <$sigfile_fh> =~ /\r/) {
336 0           close $sigfile_fh;
337 0 0         open ($sigfile_fh, '<', $sigfile) or die "Could not open $sigfile: $!";
338 0           binmode $sigfile_fh, ':crlf';
339             } else {
340 0           close $sigfile_fh;
341 0 0         open ($sigfile_fh, '<', $sigfile) or die "Could not open $sigfile: $!";
342             }
343              
344 0           my $begin = "-----BEGIN PGP SIGNED MESSAGE-----\n";
345 0           my $end = "-----END PGP SIGNATURE-----\n";
346 0           my $found = 0;
347 0           while (<$sigfile_fh>) {
348 0 0         if (1 .. ($_ eq $begin)) {
349 0 0 0       if (!$found and /signed via the Module::Signature module, version ([0-9\.]+)\./) {
350 0           $found = 1;
351 0 0         if (eval { require version; version->parse($1) < version->parse("0.82") }) {
  0            
  0            
352 0           $LegacySigFile = 1;
353 0           warn "Old $SIGNATURE detected. Please inform the module author to regenerate " .
354             "$SIGNATURE using Module::Signature version 0.82 or newer.\n";
355             }
356             }
357 0           next;
358             }
359 0           $signature .= $_;
360 0 0         return "$begin$signature" if $_ eq $end;
361             }
362              
363 0           return;
364             }
365              
366             sub _compare {
367 0     0     my ($str1, $str2, $ok) = @_;
368              
369             # normalize all linebreaks
370 0           $str1 =~ s/^-----BEGIN PGP SIGNED MESSAGE-----\n(?:.+\n)*\n//;
371 0           $str1 =~ s/[^\S ]+/\n/g; $str2 =~ s/[^\S ]+/\n/g;
  0            
372 0           $str1 =~ s/-----BEGIN PGP SIGNATURE-----\n(?:.+\n)*$//;
373              
374 0 0         return $ok if $str1 eq $str2;
375              
376 0 0         if (eval { require Text::Diff; 1 }) {
  0            
  0            
377 0           warn "--- $SIGNATURE ".localtime((stat($SIGNATURE))[9])."\n";
378 0           warn '+++ (current) '.localtime()."\n";
379 0           warn Text::Diff::diff( \$str1, \$str2, { STYLE => 'Unified' } );
380             }
381             else {
382 0           my $diff_fh;
383             my $signature_fh;
384 0 0         open ($signature_fh, '<', $SIGNATURE) or die "Could not open $SIGNATURE: $!";
385 0 0         open ($diff_fh, '|-', "diff -u --strip-trailing-cr $SIGNATURE -")
386             or (warn "Could not call diff: $!", return SIGNATURE_MISMATCH);
387 0           while (<$signature_fh>) {
388 0 0         print $diff_fh $_ if (1 .. /^-----BEGIN PGP SIGNED MESSAGE-----/);
389 0 0         print $diff_fh if (/^Hash: / .. /^$/);
390 0 0         next if (1 .. /^-----BEGIN PGP SIGNATURE/);
391 0 0         print $diff_fh $str2, "-----BEGIN PGP SIGNATURE-----\n", $_ and last;
392             }
393 0           print $diff_fh (<$signature_fh>);
394 0           close $diff_fh;
395             }
396              
397 0           return SIGNATURE_MISMATCH;
398             }
399              
400             sub sign {
401 0     0 0   my %args = ( skip => 1, @_ );
402 0           my $overwrite = $args{overwrite};
403 0           my $plaintext = _mkdigest();
404              
405 0           my ($mani, $file) = _fullcheck($args{skip});
406              
407 0 0 0       if (@{$mani} or @{$file}) {
  0            
  0            
408 0           warn "==> MISMATCHED content between MANIFEST and the distribution! <==\n";
409 0           warn "==> Please correct your MANIFEST file and/or delete extra files. <==\n";
410             }
411              
412 0 0 0       if (!$overwrite and -e $SIGNATURE and IO::Interactive::is_interactive()) {
      0        
413 0           local $/ = "\n";
414 0           print "$SIGNATURE already exists; overwrite [y/N]? ";
415 0 0         return unless =~ /[Yy]/;
416             }
417              
418 0 0         if (my $version = _has_gpg()) {
    0          
419 0           _sign_gpg($SIGNATURE, $plaintext, $version);
420             }
421 0           elsif (eval {require Crypt::OpenPGP; 1}) {
  0            
422 0           _sign_crypt_openpgp($SIGNATURE, $plaintext);
423             }
424             else {
425 0           die 'Cannot use GnuPG or Crypt::OpenPGP, please install either one first!';
426             }
427              
428 0           warn "==> SIGNATURE file created successfully. <==\n";
429 0           return SIGNATURE_OK;
430             }
431              
432             sub _sign_gpg {
433 0     0     my ($sigfile, $plaintext, $version) = @_;
434              
435 0 0 0       die "Could not write to $sigfile"
      0        
436             if -e $sigfile and (-d $sigfile or not -w $sigfile);
437              
438 0           my $gpg = _which_gpg();
439              
440 0           my $gpg_fh;
441 0           my $set_key = '';
442 0 0         $set_key = qq{--default-key "$AUTHOR"} if($AUTHOR);
443 0 0         open ($gpg_fh, '|-', "$gpg $set_key --clearsign --openpgp --personal-digest-preferences RIPEMD160 >> $sigfile.tmp")
444             or die "Could not call $gpg: $!";
445 0           print $gpg_fh $plaintext;
446 0           close $gpg_fh;
447              
448 0 0 0       (-e "$sigfile.tmp" and -s "$sigfile.tmp") or do {
449 0           unlink "$sigfile.tmp";
450 0           die "Cannot find $sigfile.tmp, signing aborted.\n";
451             };
452              
453 0           my $sigfile_tmp_fh;
454 0 0         open ($sigfile_tmp_fh, '<', "$sigfile.tmp") or die "Cannot open $sigfile.tmp: $!";
455              
456 0           my $sigfile_fh;
457 0 0         open ($sigfile_fh, '>', $sigfile) or do {
458 0           unlink "$sigfile.tmp";
459 0           die "Could not write to $sigfile: $!";
460             };
461              
462 0           print $sigfile_fh $Preamble;
463 0           print $sigfile_fh (<$sigfile_tmp_fh>);
464              
465 0           close $sigfile_fh;
466 0           close $sigfile_tmp_fh;
467              
468 0           unlink("$sigfile.tmp");
469              
470 0           my $key_id;
471             my $key_name;
472             # This doesn't work because the output from verify goes to STDERR.
473             # If I try to redirect it using "--logger-fd 1" it just hangs.
474             # WTF?
475 0           my @verify = `$gpg --batch --verify $SIGNATURE`;
476 0           while (@verify) {
477 0 0         if (/key ID ([0-9A-F]+)$/) {
    0          
478 0           $key_id = $1;
479             } elsif (/signature from "(.+)"$/) {
480 0           $key_name = $1;
481             }
482             }
483              
484 0           my $found_name;
485             my $found_key;
486 0 0 0       if (defined $key_id && defined $key_name) {
487 0           my $keyserver = _keyserver($version);
488 0           while (`$gpg --batch --keyserver=$keyserver --search-keys '$key_name'`) {
489 0 0         if (/^\(\d+\)/) {
    0          
490 0           $found_name = 0;
491             } elsif ($found_name) {
492 0 0         if (/key \Q$key_id\E/) {
493 0           $found_key = 1;
494 0           last;
495             }
496             }
497              
498 0 0         if (/\Q$key_name\E/) {
499 0           $found_name = 1;
500 0           next;
501             }
502             }
503              
504 0 0         unless ($found_key) {
505 0           _warn_non_public_signature($key_name);
506             }
507             }
508              
509 0           return 1;
510             }
511              
512             sub _sign_crypt_openpgp {
513 0     0     my ($sigfile, $plaintext) = @_;
514              
515 0           require Crypt::OpenPGP;
516 0           my $pgp = Crypt::OpenPGP->new;
517             my $ring = Crypt::OpenPGP::KeyRing->new(
518 0 0         Filename => $pgp->{cfg}->get('SecRing')
519             ) or die $pgp->error(Crypt::OpenPGP::KeyRing->errstr);
520              
521 0           my $uid = '';
522 0 0         $uid = $AUTHOR if($AUTHOR);
523              
524 0           my $kb;
525 0 0         if ($uid) {
526 0 0         $kb = $ring->find_keyblock_by_uid($uid)
527             or die $pgp->error(qq{Can't find '$uid': } . $ring->errstr);
528             }
529             else {
530 0 0         $kb = $ring->find_keyblock_by_index(-1)
531             or die $pgp->error(q{Can't find last keyblock: } . $ring->errstr);
532             }
533              
534 0           my $cert = $kb->signing_key;
535 0           $uid = $cert->uid($kb->primary_uid);
536 0 0         warn "Debug: acquiring signature from $uid\n" if $Debug;
537              
538 0 0         my $signature = $pgp->sign(
539             Data => $plaintext,
540             Detach => 0,
541             Clearsign => 1,
542             Armour => 1,
543             Key => $cert,
544             PassphraseCallback => \&Crypt::OpenPGP::_default_passphrase_cb,
545             ) or die $pgp->errstr;
546              
547 0           my $sigfile_fh;
548 0 0         open ($sigfile_fh, '>', $sigfile) or die "Could not write to $sigfile: $!";
549 0           print $sigfile_fh $Preamble;
550 0           print $sigfile_fh $signature;
551 0           close $sigfile_fh;
552              
553 0           require Crypt::OpenPGP::KeyServer;
554 0           my $server = Crypt::OpenPGP::KeyServer->new(Server => $KeyServer);
555              
556 0 0         unless ($server->find_keyblock_by_keyid($cert->key_id)) {
557 0           _warn_non_public_signature($uid);
558             }
559              
560 0           return 1;
561             }
562              
563             sub _warn_non_public_signature {
564 0     0     my $uid = shift;
565              
566 0           warn <<"EOF"
567             You have signed this distribution with a key ($uid) that cannot be
568             found on the public key server at $KeyServer.
569              
570             This will probably cause signature verification to fail if your module
571             is distributed on CPAN.
572             EOF
573             }
574              
575             sub _mkdigest {
576 0 0   0     my $digest = _mkdigest_files(@_) or return;
577 0           my $plaintext = '';
578              
579 0           foreach my $file (sort keys %$digest) {
580 0 0         next if $file eq $SIGNATURE;
581 0           $plaintext .= "@{$digest->{$file}} $file\n";
  0            
582             }
583              
584 0           return $plaintext;
585             }
586              
587             sub _digest_object {
588 0     0     my($algorithm) = @_;
589              
590             # Avoid loading Digest::* from relative paths in @INC.
591 0           local @INC = grep { File::Spec->file_name_is_absolute($_) } @INC;
  0            
592              
593             # Constrain algorithm name to be of form ABC123.
594 0 0         my ($base, $variant) = ($algorithm =~ /^([_a-zA-Z]+)([0-9]+)$/g)
595             or die "Malformed algorithm name: $algorithm (should match /\\w+\\d+/)";
596              
597             my $obj = eval { Digest->new($algorithm) } || eval {
598             my $module = "Digest/$base.pm";
599             require $module; "Digest::$base"->new($variant)
600             } || eval {
601             my $module = "Digest/$algorithm.pm";
602             require $module; "Digest::$algorithm"->new
603             } || eval {
604             my $module = "Digest/$base/PurePerl.pm";
605             require $module; "Digest::$base\::PurePerl"->new($variant)
606             } || eval {
607             my $module = "Digest/$algorithm/PurePerl.pm";
608             require $module; "Digest::$algorithm\::PurePerl"->new
609 0 0         } or do { eval {
610 0           warn "Unknown cipher: $algorithm, please install Digest::$base, Digest::$base$variant, or Digest::$base\::PurePerl\n";
611 0 0 0       } and return } or do {
      0        
612 0           warn "Unknown cipher: $algorithm, please install Digest::$algorithm\n"; return;
  0            
613             };
614 0           $obj;
615             }
616              
617             sub _mkdigest_files {
618 0     0     my $verify_map = shift;
619 0   0       my $dosnames = (defined(&Dos::UseLFN) && Dos::UseLFN()==0);
620 0   0       my $read = ExtUtils::Manifest::maniread() || {};
621 0           my $found = ExtUtils::Manifest::manifind();
622 0           my(%digest) = ();
623 0           my($default_obj) = _digest_object($Cipher);
624 0           FILE: foreach my $file (sort keys %$read){
625 0 0         next FILE if $file eq $SIGNATURE;
626 0           my($obj,$this_cipher,$this_hexdigest,$verify_digest);
627 0 0         if ($verify_map) {
628 0 0         if (my $vmf = $verify_map->{$file}) {
629 0           ($this_cipher,$verify_digest) = @$vmf;
630 0 0         if ($this_cipher eq $Cipher) {
631 0           $obj = $default_obj;
632             } else {
633 0           $obj = _digest_object($this_cipher);
634             }
635             } else {
636 0           $this_cipher = $Cipher;
637 0           $obj = $default_obj;
638             }
639             } else {
640 0           $this_cipher = $Cipher;
641 0           $obj = $default_obj;
642             }
643 0 0         warn "Debug: collecting digest from $file\n" if $Debug;
644 0 0         if ($dosnames){
645 0           $file = lc $file;
646 0           $file =~ s!(\.(\w|-)+)!substr ($1,0,4)!ge;
  0            
647 0           $file =~ s!((\w|-)+)!substr ($1,0,8)!ge;
  0            
648             }
649 0 0         unless ( exists $found->{$file} ) {
650 0 0         warn "No such file: $file\n" if $Verbose;
651             }
652             else {
653 0           my $file_fh;
654 0 0         open( $file_fh, '<', $file ) or die "Cannot open $file for reading: $!";
655 0 0         if ($LegacySigFile) {
656 0 0         if (-B $file) {
657 0           binmode($file_fh);
658 0           $obj->addfile($file_fh);
659 0           $this_hexdigest = $obj->hexdigest;
660             }
661             else {
662             # Normalize by hand...
663 0           local $/;
664 0           binmode($file_fh);
665 0           my $input = <$file_fh>;
666 0           VERIFYLOOP: for my $eol ("","\015\012","\012") {
667 0           my $lax_input = $input;
668 0 0         if (! length $eol) {
669             # first try is binary
670             } else {
671 0           my @lines = split /$eol/, $input, -1;
672 0 0         if (grep /[\015\012]/, @lines) {
673             # oops, apparently not a text file, treat as binary, forget @lines
674             } else {
675 0 0         my $other_eol = $eol eq "\012" ? "\015\012" : "\012";
676 0           $lax_input = join $other_eol, @lines;
677             }
678             }
679 0           $obj->add($lax_input);
680 0           $this_hexdigest = $obj->hexdigest;
681 0 0         if ($verify_digest) {
682 0 0         if ($this_hexdigest eq $verify_digest) {
683 0           last VERIFYLOOP;
684             }
685 0           $obj->reset;
686             } else {
687 0           last VERIFYLOOP;
688             }
689             }
690             }
691             } else {
692 0 0         binmode($file_fh, ((-B $file) ? ':raw' : ':crlf'));
693 0           $obj->addfile($file_fh);
694 0           $this_hexdigest = $obj->hexdigest;
695             }
696 0           $digest{$file} = [$this_cipher, $this_hexdigest];
697 0           $obj->reset;
698             }
699             }
700              
701 0           return \%digest;
702             }
703              
704             1;
705              
706             __END__