summaryrefslogtreecommitdiff
path: root/Master/tlpkg/tlperl0/lib/Module/Signature.pm
diff options
context:
space:
mode:
authorSiep Kroonenberg <siepo@cybercomm.nl>2011-02-17 12:20:49 +0000
committerSiep Kroonenberg <siepo@cybercomm.nl>2011-02-17 12:20:49 +0000
commit316ee97c621496b0fe3267f57cce81bee44ca1e6 (patch)
treecb2cab1192b4f58a7971af19b213e980bceda4b4 /Master/tlpkg/tlperl0/lib/Module/Signature.pm
parentcd0f87b5d39480d85ad9bd4ee37f520f75bed560 (diff)
Moving old tlperl prior to committing new one
git-svn-id: svn://tug.org/texlive/trunk@21422 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/tlpkg/tlperl0/lib/Module/Signature.pm')
-rwxr-xr-xMaster/tlpkg/tlperl0/lib/Module/Signature.pm874
1 files changed, 874 insertions, 0 deletions
diff --git a/Master/tlpkg/tlperl0/lib/Module/Signature.pm b/Master/tlpkg/tlperl0/lib/Module/Signature.pm
new file mode 100755
index 00000000000..2d526ec3ed0
--- /dev/null
+++ b/Master/tlpkg/tlperl0/lib/Module/Signature.pm
@@ -0,0 +1,874 @@
+package Module::Signature;
+$Module::Signature::VERSION = '0.61';
+
+use 5.005;
+use strict;
+use vars qw($VERSION $SIGNATURE @ISA @EXPORT_OK);
+use vars qw($Preamble $Cipher $Debug $Verbose $Timeout);
+use vars qw($KeyServer $KeyServerPort $AutoKeyRetrieve $CanKeyRetrieve);
+
+use constant CANNOT_VERIFY => '0E0';
+use constant SIGNATURE_OK => 0;
+use constant SIGNATURE_MISSING => -1;
+use constant SIGNATURE_MALFORMED => -2;
+use constant SIGNATURE_BAD => -3;
+use constant SIGNATURE_MISMATCH => -4;
+use constant MANIFEST_MISMATCH => -5;
+use constant CIPHER_UNKNOWN => -6;
+
+use ExtUtils::Manifest ();
+use Exporter;
+
+@EXPORT_OK = (
+ qw(sign verify),
+ qw($SIGNATURE $KeyServer $Cipher $Preamble),
+ (grep { /^[A-Z_]+_[A-Z_]+$/ } keys %Module::Signature::),
+);
+@ISA = 'Exporter';
+
+$SIGNATURE = 'SIGNATURE';
+$Timeout = $ENV{MODULE_SIGNATURE_TIMEOUT} || 3;
+$Verbose = $ENV{MODULE_SIGNATURE_VERBOSE} || 0;
+$KeyServer = $ENV{MODULE_SIGNATURE_KEYSERVER} || 'pgp.mit.edu';
+$KeyServerPort = $ENV{MODULE_SIGNATURE_KEYSERVERPORT} || '11371';
+$Cipher = $ENV{MODULE_SIGNATURE_CIPHER} || 'SHA1';
+$Preamble = << ".";
+This file contains message digests of all files listed in MANIFEST,
+signed via the Module::Signature module, version $VERSION.
+
+To verify the content in this distribution, first make sure you have
+Module::Signature installed, then type:
+
+ % cpansign -v
+
+It will check each file's integrity, as well as the signature's
+validity. If "==> Signature verified OK! <==" is not displayed,
+the distribution may already have been compromised, and you should
+not run its Makefile.PL or Build.PL.
+
+.
+
+$AutoKeyRetrieve = 1;
+$CanKeyRetrieve = undef;
+
+sub verify {
+ my %args = ( skip => 1, @_ );
+ my $rv;
+
+ (-r $SIGNATURE) or do {
+ warn "==> MISSING Signature file! <==\n";
+ return SIGNATURE_MISSING;
+ };
+
+ (my $sigtext = _read_sigfile($SIGNATURE)) or do {
+ warn "==> MALFORMED Signature file! <==\n";
+ return SIGNATURE_MALFORMED;
+ };
+
+ (my ($cipher) = ($sigtext =~ /^(\w+) /)) or do {
+ warn "==> MALFORMED Signature file! <==\n";
+ return SIGNATURE_MALFORMED;
+ };
+
+ (defined(my $plaintext = _mkdigest($cipher))) or do {
+ warn "==> UNKNOWN Cipher format! <==\n";
+ return CIPHER_UNKNOWN;
+ };
+
+ $rv = _verify($SIGNATURE, $sigtext, $plaintext);
+
+ if ($rv == SIGNATURE_OK) {
+ my ($mani, $file) = _fullcheck($args{skip});
+
+ if (@{$mani} or @{$file}) {
+ warn "==> MISMATCHED content between MANIFEST and distribution files! <==\n";
+ return MANIFEST_MISMATCH;
+ }
+ else {
+ warn "==> Signature verified OK! <==\n" if $Verbose;
+ }
+ }
+ elsif ($rv == SIGNATURE_BAD) {
+ warn "==> BAD/TAMPERED signature detected! <==\n";
+ }
+ elsif ($rv == SIGNATURE_MISMATCH) {
+ warn "==> MISMATCHED content between SIGNATURE and distribution files! <==\n";
+ }
+
+ return $rv;
+}
+
+sub _verify {
+ my $signature = shift || $SIGNATURE;
+ my $sigtext = shift || '';
+ my $plaintext = shift || '';
+
+ local $SIGNATURE = $signature if $signature ne $SIGNATURE;
+
+ if ($AutoKeyRetrieve and !$CanKeyRetrieve) {
+ if (!defined $CanKeyRetrieve) {
+ require IO::Socket::INET;
+ my $sock = IO::Socket::INET->new(
+ Timeout => $Timeout,
+ PeerAddr => "$KeyServer:$KeyServerPort",
+ );
+ $CanKeyRetrieve = ($sock ? 1 : 0);
+ $sock->shutdown(2) if $sock;
+ }
+ $AutoKeyRetrieve = $CanKeyRetrieve;
+ }
+
+ if (my $version = _has_gpg()) {
+ return _verify_gpg($sigtext, $plaintext, $version);
+ }
+ elsif (eval {require Crypt::OpenPGP; 1}) {
+ return _verify_crypt_openpgp($sigtext, $plaintext);
+ }
+ else {
+ warn "Cannot use GnuPG or Crypt::OpenPGP, please install either one first!\n";
+ return _compare($sigtext, $plaintext, CANNOT_VERIFY);
+ }
+}
+
+sub _has_gpg {
+ `gpg --version` =~ /GnuPG.*?(\S+)$/m or return;
+ return $1;
+}
+
+sub _fullcheck {
+ my $skip = shift;
+ my @extra;
+
+ local $^W;
+ local $ExtUtils::Manifest::Quiet = 1;
+
+ my($mani, $file);
+ if( _legacy_extutils() ) {
+ my $_maniskip = &ExtUtils::Manifest::_maniskip;
+
+ local *ExtUtils::Manifest::_maniskip = sub { sub {
+ return unless $skip;
+ my $ok = $_maniskip->(@_);
+ if ($ok ||= (!-e 'MANIFEST.SKIP' and _default_skip(@_))) {
+ print "Skipping $_\n" for @_;
+ push @extra, @_;
+ }
+ return $ok;
+ } };
+
+ ($mani, $file) = ExtUtils::Manifest::fullcheck();
+ }
+ else {
+ ($mani, $file) = ExtUtils::Manifest::fullcheck();
+ }
+
+ foreach my $makefile ('Makefile', 'Build') {
+ warn "==> SKIPPED CHECKING '$_'!" .
+ (-e "$_.PL" && " (run $_.PL to ensure its integrity)") .
+ " <===\n" for grep $_ eq $makefile, @extra;
+ }
+
+ @{$mani} = grep {$_ ne 'SIGNATURE'} @{$mani};
+
+ warn "Not in MANIFEST: $_\n" for @{$file};
+ warn "No such file: $_\n" for @{$mani};
+
+ return ($mani, $file);
+}
+
+sub _legacy_extutils {
+ # ExtUtils::Manifest older than 1.41 does not handle default skips well.
+ return (ExtUtils::Manifest->VERSION < 1.41);
+}
+
+sub _default_skip {
+ local $_ = shift;
+ return 1 if /\bRCS\b/ or /\bCVS\b/ or /\B\.svn\b/ or /,v$/
+ or /^MANIFEST\.bak/ or /^Makefile$/ or /^blib\//
+ or /^MakeMaker-\d/ or /^pm_to_blib/ or /^blibdirs/
+ or /^_build\// or /^Build$/ or /^pmfiles\.dat/
+ or /~$/ or /\.old$/ or /\#$/ or /^\.#/;
+}
+
+sub _verify_gpg {
+ my ($sigtext, $plaintext, $version) = @_;
+
+ local $SIGNATURE = Win32::GetShortPathName($SIGNATURE)
+ if defined &Win32::GetShortPathName and $SIGNATURE =~ /[^-\w.:~\\\/]/;
+
+ my $keyserver = _keyserver($version);
+
+ my @quiet = $Verbose ? () : qw(-q --logger-fd=1);
+ my @cmd = (
+ qw(gpg --verify --batch --no-tty), @quiet, ($KeyServer ? (
+ "--keyserver=$keyserver",
+ ($AutoKeyRetrieve and $version ge '1.0.7')
+ ? '--keyserver-options=auto-key-retrieve'
+ : ()
+ ) : ()), $SIGNATURE
+ );
+
+ my $output = '';
+ if( $Verbose ) {
+ warn "Executing @cmd\n";
+ system @cmd;
+ }
+ else {
+ my $cmd = join ' ', @cmd;
+ $output = `$cmd`;
+ }
+
+ if( $? ) {
+ print STDERR $output;
+ }
+ elsif ($output =~ /((?: +[\dA-F]{4}){10,})/) {
+ warn "WARNING: This key is not certified with a trusted signature!\n";
+ warn "Primary key fingerprint:$1\n";
+ }
+
+ return SIGNATURE_BAD if ($? and $AutoKeyRetrieve);
+ return _compare($sigtext, $plaintext, (!$?) ? SIGNATURE_OK : CANNOT_VERIFY);
+}
+
+sub _keyserver {
+ my $version = shift;
+ my $scheme = 'x-hkp';
+ $scheme = 'hkp' if $version ge '1.2.0';
+
+ return "$scheme://$KeyServer:$KeyServerPort";
+}
+
+sub _verify_crypt_openpgp {
+ my ($sigtext, $plaintext) = @_;
+
+ require Crypt::OpenPGP;
+ my $pgp = Crypt::OpenPGP->new(
+ ($KeyServer) ? ( KeyServer => $KeyServer, AutoKeyRetrieve => $AutoKeyRetrieve ) : (),
+ );
+ my $rv = $pgp->handle( Filename => $SIGNATURE )
+ or die $pgp->errstr;
+
+ return SIGNATURE_BAD if (!$rv->{Validity} and $AutoKeyRetrieve);
+
+ if ($rv->{Validity}) {
+ warn 'Signature made ', scalar localtime($rv->{Signature}->timestamp),
+ ' using key ID ', substr(uc(unpack('H*', $rv->{Signature}->key_id)), -8), "\n",
+ "Good signature from \"$rv->{Validity}\"\n" if $Verbose;
+ }
+ else {
+ warn "Cannot verify signature; public key not found\n";
+ }
+
+ return _compare($sigtext, $plaintext, $rv->{Validity} ? SIGNATURE_OK : CANNOT_VERIFY);
+}
+
+sub _read_sigfile {
+ my $sigfile = shift;
+ my $signature = '';
+ my $well_formed;
+
+ local *D;
+ open D, $sigfile or die "Could not open $sigfile: $!";
+
+ if ($] >= 5.006 and <D> =~ /\r/) {
+ close D;
+ open D, $sigfile or die "Could not open $sigfile: $!";
+ binmode D, ':crlf';
+ } else {
+ close D;
+ open D, $sigfile or die "Could not open $sigfile: $!";
+ }
+
+ while (<D>) {
+ next if (1 .. /^-----BEGIN PGP SIGNED MESSAGE-----/);
+ last if /^-----BEGIN PGP SIGNATURE/;
+
+ $signature .= $_;
+ }
+
+ return ((split(/\n+/, $signature, 2))[1]);
+}
+
+sub _compare {
+ my ($str1, $str2, $ok) = @_;
+
+ # normalize all linebreaks
+ $str1 =~ s/[^\S ]+/\n/g; $str2 =~ s/[^\S ]+/\n/g;
+
+ return $ok if $str1 eq $str2;
+
+ if (eval { require Text::Diff; 1 }) {
+ warn "--- $SIGNATURE ".localtime((stat($SIGNATURE))[9])."\n";
+ warn '+++ (current) '.localtime()."\n";
+ warn Text::Diff::diff( \$str1, \$str2, { STYLE => 'Unified' } );
+ }
+ else {
+ local (*D, *S);
+ open S, $SIGNATURE or die "Could not open $SIGNATURE: $!";
+ open D, "| diff -u $SIGNATURE -" or (warn "Could not call diff: $!", return SIGNATURE_MISMATCH);
+ while (<S>) {
+ print D $_ if (1 .. /^-----BEGIN PGP SIGNED MESSAGE-----/);
+ print D if (/^Hash: / .. /^$/);
+ next if (1 .. /^-----BEGIN PGP SIGNATURE/);
+ print D $str2, "-----BEGIN PGP SIGNATURE-----\n", $_ and last;
+ }
+ print D <S>;
+ close D;
+ }
+
+ return SIGNATURE_MISMATCH;
+}
+
+sub sign {
+ my %args = ( skip => 1, @_ );
+ my $overwrite = $args{overwrite};
+ my $plaintext = _mkdigest();
+
+ my ($mani, $file) = _fullcheck($args{skip});
+
+ if (@{$mani} or @{$file}) {
+ warn "==> MISMATCHED content between MANIFEST and the distribution! <==\n";
+ warn "==> Please correct your MANIFEST file and/or delete extra files. <==\n";
+ }
+
+ if (!$overwrite and -e $SIGNATURE and -t STDIN) {
+ local $/ = "\n";
+ print "$SIGNATURE already exists; overwrite [y/N]? ";
+ return unless <STDIN> =~ /[Yy]/;
+ }
+
+ if (my $version = _has_gpg()) {
+ _sign_gpg($SIGNATURE, $plaintext, $version);
+ }
+ elsif (eval {require Crypt::OpenPGP; 1}) {
+ _sign_crypt_openpgp($SIGNATURE, $plaintext);
+ }
+ else {
+ die 'Cannot use GnuPG or Crypt::OpenPGP, please install either one first!';
+ }
+
+ warn "==> SIGNATURE file created successfully. <==\n";
+ return SIGNATURE_OK;
+}
+
+sub _sign_gpg {
+ my ($sigfile, $plaintext, $version) = @_;
+
+ die "Could not write to $sigfile"
+ if -e $sigfile and (-d $sigfile or not -w $sigfile);
+
+ local *D;
+ open D, "| gpg --clearsign >> $sigfile.tmp" or die "Could not call gpg: $!";
+ print D $plaintext;
+ close D;
+
+ (-e "$sigfile.tmp" and -s "$sigfile.tmp") or do {
+ unlink "$sigfile.tmp";
+ die "Cannot find $sigfile.tmp, signing aborted.\n";
+ };
+
+ open D, "$sigfile.tmp" or die "Cannot open $sigfile.tmp: $!";
+
+ open S, ">$sigfile" or do {
+ unlink "$sigfile.tmp";
+ die "Could not write to $sigfile: $!";
+ };
+
+ print S $Preamble;
+ print S <D>;
+
+ close S;
+ close D;
+
+ unlink("$sigfile.tmp");
+
+ my $key_id;
+ my $key_name;
+ # This doesn't work because the output from verify goes to STDERR.
+ # If I try to redirect it using "--logger-fd 1" it just hangs.
+ # WTF?
+ my @verify = `gpg --batch --verify $SIGNATURE`;
+ while (@verify) {
+ if (/key ID ([0-9A-F]+)$/) {
+ $key_id = $1;
+ } elsif (/signature from "(.+)"$/) {
+ $key_name = $1;
+ }
+ }
+
+ my $found_name;
+ my $found_key;
+ if (defined $key_id && defined $key_name) {
+ my $keyserver = _keyserver($version);
+ while (`gpg --batch --keyserver=$keyserver --search-keys '$key_name'`) {
+ if (/^\(\d+\)/) {
+ $found_name = 0;
+ } elsif ($found_name) {
+ if (/key \Q$key_id\E/) {
+ $found_key = 1;
+ last;
+ }
+ }
+
+ if (/\Q$key_name\E/) {
+ $found_name = 1;
+ next;
+ }
+ }
+
+ unless ($found_key) {
+ _warn_non_public_signature($key_name);
+ }
+ }
+
+ return 1;
+}
+
+sub _sign_crypt_openpgp {
+ my ($sigfile, $plaintext) = @_;
+
+ require Crypt::OpenPGP;
+ my $pgp = Crypt::OpenPGP->new;
+ my $ring = Crypt::OpenPGP::KeyRing->new(
+ Filename => $pgp->{cfg}->get('SecRing')
+ ) or die $pgp->error(Crypt::OpenPGP::KeyRing->errstr);
+ my $kb = $ring->find_keyblock_by_index(-1)
+ or die $pgp->error('Can\'t find last keyblock: ' . $ring->errstr);
+
+ my $cert = $kb->signing_key;
+ my $uid = $cert->uid($kb->primary_uid);
+ warn "Debug: acquiring signature from $uid\n" if $Debug;
+
+ my $signature = $pgp->sign(
+ Data => $plaintext,
+ Detach => 0,
+ Clearsign => 1,
+ Armour => 1,
+ Key => $cert,
+ PassphraseCallback => \&Crypt::OpenPGP::_default_passphrase_cb,
+ ) or die $pgp->errstr;
+
+
+ local *D;
+ open D, "> $sigfile" or die "Could not write to $sigfile: $!";
+ print D $Preamble;
+ print D $signature;
+ close D;
+
+ require Crypt::OpenPGP::KeyServer;
+ my $server = Crypt::OpenPGP::KeyServer->new(Server => $KeyServer);
+
+ unless ($server->find_keyblock_by_keyid($cert->key_id)) {
+ _warn_non_public_signature($uid);
+ }
+
+ return 1;
+}
+
+sub _warn_non_public_signature {
+ my $uid = shift;
+
+ warn <<"EOF"
+You have signed this distribution with a key ($uid) that cannot be
+found on the public key server at $KeyServer.
+
+This will probably cause signature verification to fail if your module
+is distributed on CPAN.
+EOF
+}
+
+sub _mkdigest {
+ my $digest = _mkdigest_files(undef, @_) or return;
+ my $plaintext = '';
+
+ foreach my $file (sort keys %$digest) {
+ next if $file eq $SIGNATURE;
+ $plaintext .= "@{$digest->{$file}} $file\n";
+ }
+
+ return $plaintext;
+}
+
+sub _mkdigest_files {
+ my $p = shift;
+ my $algorithm = shift || $Cipher;
+ my $dosnames = (defined(&Dos::UseLFN) && Dos::UseLFN()==0);
+ my $read = ExtUtils::Manifest::maniread() || {};
+ my $found = ExtUtils::Manifest::manifind($p);
+ my(%digest) = ();
+ my $obj = eval { Digest->new($algorithm) } || eval {
+ my ($base, $variant) = ($algorithm =~ /^(\w+?)(\d+)$/g) or die;
+ require "Digest/$base.pm"; "Digest::$base"->new($variant)
+ } || eval {
+ require "Digest/$algorithm.pm"; "Digest::$algorithm"->new
+ } || eval {
+ my ($base, $variant) = ($algorithm =~ /^(\w+?)(\d+)$/g) or die;
+ require "Digest/$base/PurePerl.pm"; "Digest::$base\::PurePerl"->new($variant)
+ } || eval {
+ require "Digest/$algorithm/PurePerl.pm"; "Digest::$algorithm\::PurePerl"->new
+ } or do { eval {
+ my ($base, $variant) = ($algorithm =~ /^(\w+?)(\d+)$/g) or die;
+ warn "Unknown cipher: $algorithm, please install Digest::$base, Digest::$base$variant, or Digest::$base\::PurePerl\n";
+ } and return } or do {
+ warn "Unknown cipher: $algorithm, please install Digest::$algorithm\n"; return;
+ };
+
+ foreach my $file (sort keys %$read){
+ warn "Debug: collecting digest from $file\n" if $Debug;
+ if ($dosnames){
+ $file = lc $file;
+ $file =~ s!(\.(\w|-)+)!substr ($1,0,4)!ge;
+ $file =~ s!((\w|-)+)!substr ($1,0,8)!ge;
+ }
+ unless ( exists $found->{$file} ) {
+ warn "No such file: $file\n" if $Verbose;
+ }
+ else {
+ local *F;
+ open F, $file or die "Cannot open $file for reading: $!";
+ if (-B $file) {
+ binmode(F);
+ $obj->addfile(*F);
+ }
+ elsif ($] >= 5.006) {
+ binmode(F, ':crlf');
+ $obj->addfile(*F);
+ }
+ elsif ($^O eq 'MSWin32') {
+ $obj->addfile(*F);
+ }
+ else {
+ # Normalize by hand...
+ local $/;
+ binmode(F);
+ my $input = <F>;
+ $input =~ s/\015?\012/\n/g;
+ $obj->add($input);
+ }
+ $digest{$file} = [$algorithm, $obj->hexdigest];
+ $obj->reset;
+ }
+ }
+
+ return \%digest;
+}
+
+1;
+
+__END__
+
+=encoding utf8
+
+=head1 NAME
+
+Module::Signature - Module signature file manipulation
+
+=head1 VERSION
+
+This document describes version 0.61 of B<Module::Signature>,
+released November 18, 2009.
+
+=head1 SYNOPSIS
+
+As a shell command:
+
+ % cpansign # verify an existing SIGNATURE, or
+ # make a new one if none exists
+
+ % cpansign sign # make signature; overwrites existing one
+ % cpansign -s # same thing
+
+ % cpansign verify # verify a signature
+ % cpansign -v # same thing
+ % cpansign -v --skip # ignore files in MANIFEST.SKIP
+
+ % cpansign help # display this documentation
+ % cpansign -h # same thing
+
+In programs:
+
+ use Module::Signature qw(sign verify SIGNATURE_OK);
+ sign();
+ sign(overwrite => 1); # overwrites without asking
+
+ # see the CONSTANTS section below
+ (verify() == SIGNATURE_OK) or die "failed!";
+
+=head1 DESCRIPTION
+
+B<Module::Signature> adds cryptographic authentications to CPAN
+distributions, via the special F<SIGNATURE> file.
+
+If you are a module user, all you have to do is to remember to run
+C<cpansign -v> (or just C<cpansign>) before issuing C<perl Makefile.PL>
+or C<perl Build.PL>; that will ensure the distribution has not been
+tampered with.
+
+Module authors can easily add the F<SIGNATURE> file to the distribution
+tarball; see L</NOTES> below for how to do it as part of C<make dist>.
+
+If you I<really> want to sign a distribution manually, simply add
+C<SIGNATURE> to F<MANIFEST>, then type C<cpansign -s> immediately
+before C<make dist>. Be sure to delete the F<SIGNATURE> file afterwards.
+
+Please also see L</NOTES> about F<MANIFEST.SKIP> issues, especially if
+you are using B<Module::Build> or writing your own F<MANIFEST.SKIP>.
+
+=head1 VARIABLES
+
+No package variables are exported by default.
+
+=over 4
+
+=item $Verbose
+
+If true, Module::Signature will give information during processing including
+gpg output. If false, Module::Signature will be as quiet as possible as
+long as everything is working ok. Defaults to false.
+
+=item $SIGNATURE
+
+The filename for a distribution's signature file. Defaults to
+C<SIGNATURE>.
+
+=item $KeyServer
+
+The OpenPGP key server for fetching the author's public key
+(currently only implemented on C<gpg>, not C<Crypt::OpenPGP>).
+May be set to a false value to prevent this module from
+fetching public keys.
+
+=item $KeyServerPort
+
+The OpenPGP key server port, defaults to C<11371>.
+
+=item $Timeout
+
+Maximum time to wait to try to establish a link to the key server.
+Defaults to C<3>.
+
+=item $AutoKeyRetrieve
+
+Whether to automatically fetch unknown keys from the key server.
+Defaults to C<1>.
+
+=item $Cipher
+
+The default cipher used by the C<Digest> module to make signature
+files. Defaults to C<SHA1>, but may be changed to other ciphers
+via the C<MODULE_SIGNATURE_CIPHER> environment variable if the SHA1
+cipher is undesirable for the user.
+
+The cipher specified in the F<SIGNATURE> file's first entry will
+be used to validate its integrity. For C<SHA1>, the user needs
+to have any one of these four modules installed: B<Digest::SHA>,
+B<Digest::SHA1>, B<Digest::SHA::PurePerl>, or (currently nonexistent)
+B<Digest::SHA1::PurePerl>.
+
+=item $Preamble
+
+The explanatory text written to newly generated F<SIGNATURE> files
+before the actual entries.
+
+=back
+
+=head1 ENVIRONMENT
+
+B<Module::Signature> honors these environment variables:
+
+=over 4
+
+=item MODULE_SIGNATURE_CIPHER
+
+Works like C<$Cipher>.
+
+=item MODULE_SIGNATURE_VERBOSE
+
+Works like C<$Verbose>.
+
+=item MODULE_SIGNATURE_KEYSERVER
+
+Works like C<$KeyServer>.
+
+=item MODULE_SIGNATURE_KEYSERVERPORT
+
+Works like C<$KeyServerPort>.
+
+=item MODULE_SIGNATURE_TIMEOUT
+
+Works like C<$Timeout>.
+
+=back
+
+=head1 CONSTANTS
+
+These constants are not exported by default.
+
+=over 4
+
+=item CANNOT_VERIFY (C<0E0>)
+
+Cannot verify the OpenPGP signature, maybe due to the lack of a network
+connection to the key server, or if neither gnupg nor Crypt::OpenPGP
+exists on the system.
+
+=item SIGNATURE_OK (C<0>)
+
+Signature successfully verified.
+
+=item SIGNATURE_MISSING (C<-1>)
+
+The F<SIGNATURE> file does not exist.
+
+=item SIGNATURE_MALFORMED (C<-2>)
+
+The signature file does not contains a valid OpenPGP message.
+
+=item SIGNATURE_BAD (C<-3>)
+
+Invalid signature detected -- it might have been tampered with.
+
+=item SIGNATURE_MISMATCH (C<-4>)
+
+The signature is valid, but files in the distribution have changed
+since its creation.
+
+=item MANIFEST_MISMATCH (C<-5>)
+
+There are extra files in the current directory not specified by
+the MANIFEST file.
+
+=item CIPHER_UNKNOWN (C<-6>)
+
+The cipher used by the signature file is not recognized by the
+C<Digest> and C<Digest::*> modules.
+
+=back
+
+=head1 NOTES
+
+=head2 Signing your module as part of C<make dist>
+
+The easiest way is to use B<Module::Install>:
+
+ sign; # put this before "WriteAll"
+ WriteAll;
+
+For B<ExtUtils::MakeMaker> (version 6.18 or above), you may do this:
+
+ WriteMakefile(
+ (MM->can('signature_target') ? (SIGN => 1) : ()),
+ # ... original arguments ...
+ );
+
+Users of B<Module::Build> may do this:
+
+ Module::Build->new(
+ (sign => 1),
+ # ... original arguments ...
+ )->create_build_script;
+
+=head2 F<MANIFEST.SKIP> Considerations
+
+(The following section is lifted from Iain Truskett's B<Test::Signature>
+module, under the Perl license. Thanks, Iain!)
+
+It is B<imperative> that your F<MANIFEST> and F<MANIFEST.SKIP> files be
+accurate and complete. If you are using C<ExtUtils::MakeMaker> and you
+do not have a F<MANIFEST.SKIP> file, then don't worry about the rest of
+this. If you do have a F<MANIFEST.SKIP> file, or you use
+C<Module::Build>, you must read this.
+
+Since the test is run at C<make test> time, the distribution has been
+made. Thus your F<MANIFEST.SKIP> file should have the entries listed
+below.
+
+If you're using C<ExtUtils::MakeMaker>, you should have, at least:
+
+ #defaults
+ ^Makefile$
+ ^blib/
+ ^pm_to_blib
+ ^blibdirs
+
+These entries are part of the default set provided by
+C<ExtUtils::Manifest>, which is ignored if you provide your own
+F<MANIFEST.SKIP> file.
+
+If you are using C<Module::Build>, you should have two extra entries:
+
+ ^Build$
+ ^_build/
+
+If you don't have the correct entries, C<Module::Signature> will
+complain that you have:
+
+ ==> MISMATCHED content between MANIFEST and distribution files! <==
+
+You should note this during normal development testing anyway.
+
+=head2 Testing signatures
+
+You may add this code as F<t/0-signature.t> in your distribution tree:
+
+ #!/usr/bin/perl
+
+ use strict;
+ print "1..1\n";
+
+ if (!$ENV{TEST_SIGNATURE}) {
+ print "ok 1 # skip Set the environment variable",
+ " TEST_SIGNATURE to enable this test\n";
+ }
+ elsif (!-s 'SIGNATURE') {
+ print "ok 1 # skip No signature file found\n";
+ }
+ elsif (!eval { require Module::Signature; 1 }) {
+ print "ok 1 # skip ",
+ "Next time around, consider install Module::Signature, ",
+ "so you can verify the integrity of this distribution.\n";
+ }
+ elsif (!eval { require Socket; Socket::inet_aton('pgp.mit.edu') }) {
+ print "ok 1 # skip ",
+ "Cannot connect to the keyserver\n";
+ }
+ else {
+ (Module::Signature::verify() == Module::Signature::SIGNATURE_OK())
+ or print "not ";
+ print "ok 1 # Valid signature\n";
+ }
+
+ __END__
+
+If you are already using B<Test::More> for testing, a more
+straightforward version of F<t/0-signature.t> can be found in the
+B<Module::Signature> distribution.
+
+Also, if you prefer a more full-fledged testing package, and are
+willing to inflict the dependency of B<Module::Build> on your users,
+Iain Truskett's B<Test::Signature> might be a better choice.
+
+=cut
+
+=head1 SEE ALSO
+
+L<Digest>, L<Digest::SHA>, L<Digest::SHA1>, L<Digest::SHA::PurePerl>
+
+L<ExtUtils::Manifest>, L<Crypt::OpenPGP>, L<Test::Signature>
+
+L<Module::Install>, L<ExtUtils::MakeMaker>, L<Module::Build>
+
+=head1 AUTHORS
+
+唐鳳 E<lt>cpan@audreyt.orgE<gt>
+
+=head1 CC0 1.0 Universal
+
+To the extent possible under law, 唐鳳 has waived all copyright and related
+or neighboring rights to Module-Signature.
+
+This work is published from Taiwan.
+
+L<http://creativecommons.org/publicdomain/zero/1.0>
+
+=cut