"Fossies" - the Fresh Open Source Software Archive

Member "install-tl-20231204/tlpkg/TeXLive/TLCrypto.pm" (20 Feb 2023, 20371 Bytes) of package /linux/misc/install-tl-unx.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 # $Id: TLCrypto.pm 65994 2023-02-20 23:40:00Z karl $
    2 # TeXLive::TLCrypto.pm - handle checksums and signatures.
    3 # Copyright 2016-2023 Norbert Preining
    4 # This file is licensed under the GNU General Public License version 2
    5 # or any later version.
    6 
    7 package TeXLive::TLCrypto;
    8 
    9 use Digest::MD5;
   10 
   11 use TeXLive::TLConfig;
   12 use TeXLive::TLUtils qw(debug ddebug wndws which platform
   13                         conv_to_w32_path tlwarn tldie);
   14 
   15 my $svnrev = '$Revision: 65994 $';
   16 my $_modulerevision = ($svnrev =~ m/: ([0-9]+) /) ? $1 : "unknown";
   17 sub module_revision { return $_modulerevision; }
   18 
   19 =pod
   20 
   21 =head1 NAME
   22 
   23 C<TeXLive::TLCrypto> -- TeX Live checksums and cryptographic signatures
   24 
   25 =head1 SYNOPSIS
   26 
   27   use TeXLive::TLCrypto;  # requires Digest::MD5 and Digest::SHA
   28 
   29 =head2 Setup
   30 
   31   TeXLive::TLCrypto::setup_checksum_method();
   32 
   33 =head2 Checksums
   34 
   35   TeXLive::TLCrypto::tlchecksum($path);
   36   TeXLive::TLCrypto::verify_checksum($file, $url);
   37   TeXLive::TLCrypto::verify_checksum_and_check_return($file, $url);
   38 
   39 =head2 Signatures
   40 
   41   TeXLive::TLCrypto::setup_gpg();
   42   TeXLive::TLCrypto::verify_signature($file, $url);
   43 
   44 =head1 DESCRIPTION
   45 
   46 =cut
   47 
   48 BEGIN {
   49   use Exporter ();
   50   use vars qw(@ISA @EXPORT_OK @EXPORT);
   51   @ISA = qw(Exporter);
   52   @EXPORT_OK = qw(
   53     &tlchecksum
   54     &tl_short_digest
   55     &verify_checksum
   56     &verify_checksum_and_check_return
   57     &setup_gpg
   58     &verify_signature
   59     %VerificationStatusDescription
   60     $VS_VERIFIED $VS_CHECKSUM_ERROR $VS_SIGNATURE_ERROR $VS_CONNECTION_ERROR
   61     $VS_UNSIGNED $VS_GPG_UNAVAILABLE $VS_PUBKEY_MISSING $VS_UNKNOWN
   62     $VS_EXPKEYSIG $VS_REVKEYSIG
   63   );
   64   @EXPORT = qw(
   65     %VerificationStatusDescription
   66     $VS_VERIFIED $VS_CHECKSUM_ERROR $VS_SIGNATURE_ERROR $VS_CONNECTION_ERROR
   67     $VS_UNSIGNED $VS_GPG_UNAVAILABLE $VS_PUBKEY_MISSING $VS_UNKNOWN
   68     $VS_EXPKEYSIG $VS_REVKEYSIG
   69   );
   70 }
   71 
   72 =pod
   73 
   74 =over 4
   75 
   76 =item C<< setup_checksum_method() >>
   77 
   78 Tries to find a checksum method: check usability of C<Digest::SHA>,
   79 then the programs C<openssl>, C<sha512sum>, and C<shasum>, in that
   80 order.  On old-enough Macs, C<openssl> is present but does not have
   81 the option C<-sha512>, while the separate program C<shasum> does suffice.
   82 
   83 Returns the checksum method as a string, and also sets
   84 C<<$::checksum_method>>, or false if none found.
   85 
   86 =cut
   87 
   88 sub setup_checksum_method {
   89   # make it a noop if already defined
   90   # the checksum method could also be "" meaning that there
   91   # is none. We do not need to check again. Thus we check
   92   # on defined.
   93   return ($::checksum_method) if defined($::checksum_method);
   94   # default is no checksum
   95   $::checksum_method = "";
   96   # for debugging
   97   # $::checksum_method = "sha512sum";
   98   # return($::checksum_method);
   99   # try to load Digest::SHA, and if that fails, use our own slow modules
  100   eval { 
  101     require Digest::SHA;
  102     Digest::SHA->import('sha512_hex');
  103     debug("Using checksum method digest::sha\n");
  104     $::checksum_method = "digest::sha";
  105   };
  106   if ($@ && ($^O !~ /^MSWin/i)) {
  107     # for unix like environments we test other programs (openssl, sha512sum,
  108     # shasum), too
  109     my $ret;
  110 
  111     # first for openssl dgst -sha512
  112     # old MacOS openssl does not support -sha512!
  113     $ret = system("openssl dgst -sha512 >/dev/null 2>&1 </dev/null" );
  114     if ($ret == 0) {
  115       debug("Using checksum method openssl\n");
  116       return($::checksum_method = "openssl");
  117     }
  118 
  119     # next for sha512sum, but this is not available on old MacOS
  120     if (TeXLive::TLUtils::which("sha512sum")) {
  121       debug("Using checksum method sha512sum\n");
  122       return($::checksum_method = "sha512sum");
  123     }
  124 
  125     # shasum for old Macs
  126     $ret = system("shasum -a 512 >/dev/null 2>&1 </dev/null" );
  127     if ($ret == 0) {
  128       debug("Using checksum method shasum\n");
  129       return($::checksum_method = "shasum");
  130     }
  131 
  132     debug("Cannot find usable checksum method!\n");
  133   }
  134   return($::checksum_method);
  135 }
  136 
  137 
  138 =pod
  139 
  140 =item C<< tlchecksum($file) >>
  141 
  142 Return checksum of C<$file>.
  143 
  144 =cut
  145 
  146 sub tlchecksum {
  147   my ($file) = @_;
  148   # this is here for the case that a script forgets to
  149   # set up the checksum method!
  150   if (!$::checksum_method) {
  151     setup_checksum_method();
  152   }
  153   tldie("TLCRYPTO::tlchecksum: no checksum method available\n")
  154     if (!$::checksum_method);
  155 
  156   if (-r $file) {
  157     my ($out, $ret);
  158     if ($::checksum_method eq "openssl") {
  159       ($out, $ret) = TeXLive::TLUtils::run_cmd("openssl dgst -sha512 $file");
  160       chomp($out);
  161     } elsif ($::checksum_method eq "sha512sum") {
  162       ($out, $ret) = TeXLive::TLUtils::run_cmd("sha512sum $file");
  163       chomp($out);
  164     } elsif ($::checksum_method eq "shasum") {
  165       ($out, $ret) = TeXLive::TLUtils::run_cmd("shasum -a 512 $file");
  166       chomp($out);
  167     } elsif ($::checksum_method eq "digest::sha") {
  168       open(FILE, $file) || die "open($file) failed: $!";
  169       binmode(FILE);
  170       $out = Digest::SHA->new(512)->addfile(*FILE)->hexdigest;
  171       close(FILE);
  172       $ret = 0;
  173     } else {
  174       tldie("TLCRYPTO::tlchecksum: unknown checksum program: $::checksum_method\n");
  175     }
  176     if ($ret != 0) {
  177       tlwarn("TLCRYPTO::tlchecksum: cannot compute checksum: $file\n");
  178       return "";
  179     }
  180     ddebug("tlchecksum: out = $out\n");
  181     my $cs;
  182     if ($::checksum_method eq "openssl") {
  183       (undef,$cs) = split(/= /,$out);
  184     } elsif ($::checksum_method eq "sha512sum") {
  185       ($cs,undef) = split(' ',$out);
  186     } elsif ($::checksum_method eq "shasum") {
  187       ($cs,undef) = split(' ',$out);
  188     } elsif ($::checksum_method eq "digest::sha") {
  189       $cs = $out;
  190     }
  191     debug("tlchecksum($file): ===$cs===\n");
  192     if (length($cs) != 128) {
  193       tlwarn("TLCRYPTO::tlchecksum: unexpected output from $::checksum_method:"
  194              . " $out\n");
  195       return "";
  196     }
  197     return $cs;
  198   } else {
  199     tlwarn("TLCRYPTO::tlchecksum: given file not readable: $file\n");
  200     return "";
  201   }
  202 }
  203 
  204 # sub tlchecksum {
  205 #   my ($file) = @_;
  206 #   if (-r $file) {
  207 #     open(FILE, $file) || die "open($file) failed: $!";
  208 #     binmode(FILE);
  209 #     my $cshash = $dig->new(512)->addfile(*FILE)->hexdigest;
  210 #     close(FILE);
  211 #     return $cshash;
  212 #   } else {
  213 #     tlwarn("tlchecksum: given file not readable: $file\n");
  214 #     return "";
  215 #   }
  216 # } 
  217 
  218 =pod
  219 
  220 =item C<< tl_short_digest($str) >>
  221 
  222 Return short digest (MD5) of C<$str>.
  223 
  224 =cut
  225 
  226 sub tl_short_digest { return (Digest::MD5::md5_hex(shift)); }
  227 
  228 # emacs-page
  229 =pod
  230 
  231 =item C<< verify_checksum_and_check_return($file, $tlpdburl [, $is_main, $localcopymode ]) >>
  232 
  233 Calls C<<verify_checksum>> and checks the various return values
  234 for critical errors, and dies if necessary.
  235 
  236 If C<$is_main> is given and true, an unsigned tlpdb is considered
  237 fatal. If C<$localcopymode> is given and true, do not die for 
  238 checksum and connection errors, thus allowing for re-downloading
  239 of a copy.
  240 
  241 =cut
  242 
  243 sub verify_checksum_and_check_return {
  244   my ($file, $path, $is_main, $localcopymode) = @_;
  245   my ($r, $m) = verify_checksum($file, "$path.$ChecksumExtension");
  246   if ($r == $VS_CHECKSUM_ERROR) {
  247     if (!$localcopymode) {
  248       tldie("$0: checksum error when downloading $file from $path: $m\n");
  249     }
  250     return(0, $r);
  251   } elsif ($r == $VS_SIGNATURE_ERROR) {
  252     tldie("$0: signature verification error of $file from $path: $m\n");
  253   } elsif ($r == $VS_CONNECTION_ERROR) {
  254     if ($localcopymode) {
  255       return(0, $r);
  256     } else {
  257       tldie("$0: cannot download: $m\n");
  258     }
  259   } elsif ($r == $VS_UNSIGNED) {
  260     if ($is_main) {
  261       tldie("$0: main database at $path is not signed: $m\n");
  262     }
  263     debug("$0: remote database checksum is not signed, continuing anyway\n");
  264     return(0, $r);
  265   } elsif ($r == $VS_EXPKEYSIG) {
  266     debug("$0: good signature bug gpg key expired, continuing anyway!\n");
  267     return(0, $r);
  268   } elsif ($r == $VS_REVKEYSIG) {
  269     debug("$0: good signature but from revoked gpg key, continuing anyway!\n");
  270     return(0, $r);
  271   } elsif ($r == $VS_GPG_UNAVAILABLE) {
  272     debug("$0: TLPDB: no gpg available, continuing anyway!\n");
  273     return(0, $r);
  274   } elsif ($r == $VS_PUBKEY_MISSING) {
  275     debug("$0: TLPDB: pubkey missing, continuing anyway!\n");
  276     return(0, $r);
  277   } elsif ($r == $VS_VERIFIED) {
  278     return(1, $r);
  279   } else {
  280     tldie("$0: unexpected return value from verify_checksum: $r\n");
  281   }
  282   # we should never come here, but just to be sure
  283   return(0, $r);
  284 }
  285 
  286 
  287 
  288 # emacs-page
  289 =pod
  290 
  291 =item C<< verify_checksum($file, $checksum_url) >>
  292 
  293 Verifies that C<$file> has checksum C<$checksum_url>, and if gpg is
  294 available also verifies that the checksum is signed.
  295 
  296 Returns 
  297 C<$VS_VERIFIED> on success, 
  298 C<$VS_CONNECTION_ERROR> on connection error,
  299 C<$VS_UNSIGNED> on missing signature file, 
  300 C<$VS_GPG_UNAVAILABLE> if no gpg program is available,
  301 C<$VS_PUBKEY_MISSING> if the pubkey is not available, 
  302 C<$VS_CHECKSUM_ERROR> on checksum errors, 
  303 C<$VS_EXPKEYSIG> if the signature is good but was made with an expired key,
  304 C<$VS_REVKEYSIG> if the signature is good but was made with a revoked key,
  305 and C<$VS_SIGNATURE_ERROR> on signature errors.
  306 In case of errors returns an informal message as second argument.
  307 
  308 =cut
  309 
  310 sub verify_checksum {
  311   my ($file, $checksum_url) = @_;
  312   # don't do anything if we cannot determine a checksum method
  313   # return -2 which is as much as missing signature
  314   return($VS_UNSIGNED, "no checksum method found") if (!$::checksum_method);
  315   my $checksum_file
  316     = TeXLive::TLUtils::download_to_temp_or_file($checksum_url);
  317 
  318   # next step is verification of tlpdb checksum with checksum file
  319   # existenc of checksum_file was checked above
  320   if (!$checksum_file) {
  321     debug("verify_checksum: download did not succeed for $checksum_url\n");
  322     return($VS_CONNECTION_ERROR, "download did not succeed: $checksum_url");
  323   }
  324 
  325   # check that we have a non-trivial size for the checksum file
  326   # the size should be at least 128 + 1 + length(filename) > 129
  327   {
  328     my $css = -s $checksum_file;
  329     if ($css <= 128) {
  330       debug("verify_checksum: size of checksum file suspicious: $css\n");
  331       return($VS_CONNECTION_ERROR, "download corrupted: $checksum_url");
  332     }
  333   }
  334 
  335   # check the signature
  336   my ($ret, $msg) = verify_signature($checksum_file, $checksum_url);
  337 
  338   if ($ret != 0) {
  339     debug("verify_checksum: returning $ret and $msg\n");
  340     return ($ret, $msg)
  341   }
  342 
  343   # verify local data
  344   open $cs_fh, "<$checksum_file" or die("cannot read file: $!");
  345   if (read ($cs_fh, $remote_digest, $ChecksumLength) != $ChecksumLength) {
  346     close($cs_fh);
  347     debug("verify_checksum: incomplete read from\n  $checksum_file\nfor\n  $file\nand\n  $checksum_url\n");
  348     return($VS_CHECKSUM_ERROR, "incomplete read from $checksum_file");
  349   } else {
  350     close($cs_fh);
  351     debug("verify_checksum: found remote digest\n  $remote_digest\nfrom\n  $checksum_file\nfor\n  $file\nand\n  $checksum_url\n");
  352   }
  353   $local_digest = tlchecksum($file);
  354   debug("verify_checksum: local_digest = $local_digest\n");
  355   if ($local_digest ne $remote_digest) {
  356     return($VS_CHECKSUM_ERROR, "digest disagree");
  357   }
  358 
  359   # we are still here, so checksum also succeeded
  360   debug("checksum of local copy identical with remote hash\n");
  361 
  362   return($VS_VERIFIED);
  363 }
  364 
  365 # emacs-page
  366 =pod
  367 
  368 =item C<< setup_gpg() >>
  369 
  370 Tries to set up gpg command line C<$::gpg> used for verification of
  371 downloads. Checks for the environment variable C<TL_GNUPG>; if that
  372 envvar is not set, first C<gpg>, then C<gpg2>, then, on Windows only,
  373 C<tlpkg/installer/gpg/gpg.exe> is looked for.  Further adaptation of the
  374 invocation of C<gpg> can be done using the two enviroment variables
  375 C<TL_GNUPGHOME>, which is passed to C<gpg> with C<--homedir>, and
  376 C<TL_GNUPGARGS>, which replaces the default arguments
  377 C<--no-secmem-warning --no-permission-warning>.
  378 
  379 Returns 1/0 on success/failure.
  380 
  381 =cut
  382 
  383 sub setup_gpg {
  384   my $master = shift;
  385   my $found = 0;
  386   my $prg;
  387   if ($ENV{'TL_GNUPG'}) {
  388     # if envvar is set, don't look for anything else.
  389     $prg = test_one_gpg($ENV{'TL_GNUPG'});
  390     $found = 1 if ($prg);
  391   } else {
  392     # no envvar, look for gpg
  393     $prg = test_one_gpg('gpg');
  394     $found = 1 if ($prg);
  395   
  396     # no gpg, look for gpg2
  397     if (!$found) {
  398       $prg = test_one_gpg('gpg2');
  399       $found = 1 if ($prg);
  400     }
  401     if (!$found) {
  402       # test also a shipped version from tlgpg
  403       my $p = "$master/tlpkg/installer/gpg/gpg." .
  404         ($^O =~ /^MSWin/i ? "exe" : platform()) ;
  405       debug("Testing for gpg in $p\n");
  406       if (-r $p) {
  407         if ($^O =~ /^MSWin/i) {
  408           $prg = conv_to_w32_path($p);
  409         } else {
  410           $prg = "\"$p\"";
  411         }
  412         $found = 1;
  413       }
  414     }
  415   }
  416   return 0 if (!$found);
  417 
  418   # $prg is already properly quoted!
  419 
  420   # ok, we found one
  421   # Set up the gpg invocation:
  422   my $gpghome = ($ENV{'TL_GNUPGHOME'} ? $ENV{'TL_GNUPGHOME'} : 
  423                                         "$master/tlpkg/gpg" );
  424   $gpghome =~ s!/!\\!g if wndws();
  425   my $gpghome_quote = "\"$gpghome\"";
  426   # mind the final space for following args
  427   $::gpg = "$prg --homedir $gpghome_quote ";
  428   #
  429   # check for additional keyring
  430   # originally we wanted to use TEXMFSYSCONFIG, but gnupg on Windows
  431   # is so stupid that it *prepends* GNUPGHOME to paths starting with
  432   # a drive letter like c:/
  433   # Thus we switch to using repository-keys.gpg in GNUPGHOME!
  434   my $addkr = "$gpghome/repository-keys.gpg";
  435   if (-r $addkr) {
  436     debug("setup_gpg: using additional keyring $addkr\n");
  437     $::gpg .= "--keyring repository-keys.gpg ";
  438   }
  439   if ($ENV{'TL_GNUPGARGS'}) {
  440     $::gpg .= $ENV{'TL_GNUPGARGS'};
  441   } else {
  442     $::gpg .= "--no-secmem-warning --no-permission-warning --lock-never ";
  443   }
  444   debug("gpg command line: $::gpg\n");
  445   return 1;
  446 }
  447 
  448 sub test_one_gpg {
  449   my $prg = shift;
  450   my $cmdline;
  451   debug("Testing for gpg in $prg\n");
  452   if ($^O =~ /^MSWin/i) {
  453     # Perl on Windows somehow does not allow calling a program
  454     # without a full path - at least a call to "gpg" tells me
  455     # that "c:/Users/norbert/gpg" is not recognized ...
  456     # consequence - use which!
  457     $prg = which($prg);
  458     return "" if (!$prg);
  459     $prg = conv_to_w32_path($prg);
  460     $cmdline = "$prg --version >nul 2>&1";
  461   } else {
  462     $cmdline = "$prg --version >/dev/null 2>&1";
  463   }
  464   my $ret = system($cmdline);
  465   if ($ret == 0) {
  466     debug(" ... gpg ok! [$cmdline]\n");
  467     return $prg;
  468   } else {
  469     debug(" ... gpg not ok! [$cmdline]\n");
  470     return "";
  471   }
  472 }
  473 
  474 # emacs-page
  475 =pod
  476 
  477 =item C<< verify_signature($file, $url) >>
  478 
  479 Verifies a download of C<$url> into C<$file> by cheking the 
  480 gpg signature in C<$url.asc>.
  481 
  482 Returns 
  483 $VS_VERIFIED on success, 
  484 $VS_REVKEYSIG on good signature but from revoked key,
  485 $VS_EXPKEYSIG on good signature but from expired key,
  486 $VS_UNSIGNED on missing signature file, 
  487 $VS_SIGNATURE_ERROR on signature error,
  488 $VS_GPG_UNAVAILABLE if no gpg is available, and 
  489 $VS_PUBKEY_MISSING if a pubkey is missing.
  490 In case of errors returns an informal message as second argument.
  491 
  492 =cut
  493 
  494 sub verify_signature {
  495   my ($file, $url) = @_;
  496   my $signature_url = "$url.asc";
  497 
  498   # if we have $::gpg set, we try to verify cryptographic signatures
  499   if ($::gpg) {
  500     my $signature_file
  501       = TeXLive::TLUtils::download_to_temp_or_file($signature_url);
  502     if ($signature_file) {
  503       {
  504         # we expect a signature to be at least
  505         # 30 header line + 30 footer line + 256 > 300
  506         my $sigsize = -s $signature_file;
  507         if ($sigsize < 300) {
  508           debug("cryptographic signature seems to be corrupted (size $sigsize<300): $signature_url, $signature_file\n");
  509           return($VS_UNSIGNED, "cryptographic signature download seems to be corrupted (size $sigsize<300)");
  510         }
  511       }
  512       # check also the first line of the signature file for
  513       # -----BEGIN PGP SIGNATURE-----
  514       {
  515         open my $file, '<', $signature_file;
  516         chomp(my $firstLine = <$file>);
  517         close $file;
  518         if ($firstLine !~ m/^-----BEGIN PGP SIGNATURE-----/) {
  519           debug("cryptographic signature seems to be corrupted (first line not signature): $signature_url, $signature_file, $firstLine\n");
  520           return($VS_UNSIGNED, "cryptographic signature download seems to be corrupted (first line of $signature_url not signature: $firstLine)");
  521         }
  522       }
  523       my ($ret, $out) = gpg_verify_signature($file, $signature_file);
  524       if ($ret == $VS_VERIFIED) {
  525         # no need to show the output
  526         debug("cryptographic signature of $url verified\n");
  527         return($VS_VERIFIED);
  528       } elsif ($ret == $VS_PUBKEY_MISSING) {
  529         return($VS_PUBKEY_MISSING, $out);
  530       } elsif ($ret == $VS_EXPKEYSIG) {
  531         return($VS_EXPKEYSIG, $out);
  532       } elsif ($ret == $VS_REVKEYSIG) {
  533         return($VS_REVKEYSIG, $out);
  534       } else {
  535         return($VS_SIGNATURE_ERROR, <<GPGERROR);
  536 cryptographic signature verification of
  537   $file
  538 against
  539   $signature_url
  540 failed. Output was:
  541 $out
  542 Please try from a different mirror and/or wait a few minutes
  543 and try again; usually this is because of transient updates.
  544 If problems persist, feel free to report to texlive\@tug.org.
  545 GPGERROR
  546       }
  547     } else {
  548       debug("no access to cryptographic signature $signature_url\n");
  549       return($VS_UNSIGNED, "no access to cryptographic signature");
  550     }
  551   } else {
  552     debug("gpg prog not defined, no checking of signatures\n");
  553     # we return 0 (= success) if not gpg is available
  554     return($VS_GPG_UNAVAILABLE, "no gpg available");
  555   }
  556   # not reached
  557   return ($VS_UNKNOWN);
  558 }
  559 
  560 =pod
  561 
  562 =item C<< gpg_verify_signature($file, $sig) >>
  563 
  564 Internal routine running gpg to verify signature C<$sig> of C<$file>.
  565 
  566 =cut
  567 
  568 sub gpg_verify_signature {
  569   my ($file, $sig) = @_;
  570   my ($file_quote, $sig_quote);
  571   if (wndws()) {
  572     $file =~ s!/!\\!g;
  573     $sig =~ s!/!\\!g;
  574   }
  575   $file_quote = TeXLive::TLUtils::quotify_path_with_spaces ($file);
  576   $sig_quote = TeXLive::TLUtils::quotify_path_with_spaces ($sig);
  577   my ($status_fh, $status_file) = TeXLive::TLUtils::tl_tmpfile();
  578   close($status_fh);
  579   my ($out, $ret)
  580     = TeXLive::TLUtils::run_cmd("$::gpg --status-file \"$status_file\" --verify $sig_quote $file_quote 2>&1");
  581   # read status file
  582   open($status_fd, "<", $status_file) || die("Cannot open status file: $!");
  583   my @status_lines = <$status_fd>;
  584   close($status_fd);
  585   chomp(@status_lines);
  586   debug(join("\n", "STATUS OUTPUT", @status_lines));
  587   if ($ret == 0) {
  588     # verification still might return success but key is expired!
  589     if (grep(/EXPKEYSIG/, @status_lines)) {
  590       return($VS_EXPKEYSIG, "expired key");
  591     }
  592     if (grep(/REVKEYSIG/, @status_lines)) {
  593       return($VS_REVKEYSIG, "revoked key");
  594     }
  595     debug("verification succeeded, output:\n$out\n");
  596     return ($VS_VERIFIED, $out);
  597   } else {
  598     my @nopb = grep(/^\[GNUPG:\] NO_PUBKEY /, @status_lines);
  599     if (@nopb) {
  600       my $mpk = $nopb[-1];
  601       $mpk =~ s/^\[GNUPG:\] NO_PUBKEY //;
  602       debug("missing pubkey $mpk\n");
  603       return ($VS_PUBKEY_MISSING, "missing pubkey $mpk");
  604     }
  605     # we could do more checks on what is the actual problem here!
  606     return ($VS_SIGNATURE_ERROR, $out);
  607   }
  608 }
  609 
  610 =pod
  611 
  612 =item C<< %VerificationStatusDescription >>
  613 
  614 Provides a textual representation for the verification status values.
  615 
  616 =cut
  617 
  618 our $VS_VERIFIED = 0;
  619 our $VS_CHECKSUM_ERROR = 1;
  620 our $VS_SIGNATURE_ERROR = 2;
  621 our $VS_CONNECTION_ERROR = -1;
  622 our $VS_UNSIGNED = -2;
  623 our $VS_GPG_UNAVAILABLE = -3;
  624 our $VS_PUBKEY_MISSING = -4;
  625 our $VS_EXPKEYSIG = -5;
  626 our $VS_EXPSIG = -6;
  627 our $VS_REVKEYSIG = -7;
  628 our $VS_UNKNOWN = -100;
  629 
  630 our %VerificationStatusDescription = (
  631   $VS_VERIFIED         => 'verified',
  632   $VS_CHECKSUM_ERROR   => 'checksum error',
  633   $VS_SIGNATURE_ERROR  => 'signature error',
  634   $VS_CONNECTION_ERROR => 'connection error',
  635   $VS_UNSIGNED         => 'unsigned',
  636   $VS_GPG_UNAVAILABLE  => 'gpg unavailable',
  637   $VS_PUBKEY_MISSING   => 'pubkey missing',
  638   $VS_EXPKEYSIG        => 'valid signature with expired key',
  639   $VS_EXPSIG           => 'valid but expired signature',
  640   $VS_UNKNOWN          => 'unknown',
  641 );
  642 
  643 =back
  644 =cut
  645 
  646 1;
  647 __END__
  648 
  649 =head1 SEE ALSO
  650 
  651 The modules L<TeXLive::Config>, L<TeXLive::TLUtils>, etc.,
  652 and the documentation in the repository: C<Master/tlpkg/doc/>.
  653 Also the standard modules L<Digest::MD5> and L<Digest::SHA>.
  654 
  655 =head1 AUTHORS AND COPYRIGHT
  656 
  657 This script and its documentation were written for the TeX Live
  658 distribution (L<https://tug.org/texlive>) and both are licensed under the
  659 GNU General Public License Version 2 or later.
  660 
  661 =cut
  662 
  663 ### Local Variables:
  664 ### perl-indent-level: 2
  665 ### tab-width: 2
  666 ### indent-tabs-mode: nil
  667 ### End:
  668 # vim:set tabstop=2 expandtab: #