"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: #