File Coverage

blib/lib/IO/Socket/SSL/Utils.pm
Criterion Covered Total %
statement 96 221 43.4
branch 28 116 24.1
condition 19 65 29.2
subroutine 11 22 50.0
pod 15 15 100.0
total 169 439 38.5


line stmt bran cond sub pod time code
1              
2             package IO::Socket::SSL::Utils;
3 14     14   7787 use strict;
  14         32  
  14         522  
4 14     14   91 use warnings;
  14         29  
  14         596  
5 14     14   73 use Carp 'croak';
  14         28  
  14         719  
6 14     14   84 use Net::SSLeay;
  14         35  
  14         5041  
7              
8             # old versions of Exporter do not export 'import' yet
9             require Exporter;
10             *import = \&Exporter::import;
11              
12             our $VERSION = '2.015';
13             our @EXPORT = qw(
14             PEM_file2cert PEM_file2certs PEM_string2cert PEM_cert2file PEM_certs2file PEM_cert2string
15             PEM_file2key PEM_string2key PEM_key2file PEM_key2string
16             KEY_free CERT_free
17             KEY_create_rsa CERT_asHash CERT_create
18             );
19              
20             sub PEM_file2cert {
21 10     10 1 373 my $file = shift;
22 10 50       391 my $bio = Net::SSLeay::BIO_new_file($file,'r') or
23             croak "cannot read $file: $!";
24 10         960 my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
25 10         188 Net::SSLeay::BIO_free($bio);
26 10 50       42 $cert or croak "cannot parse $file as PEM X509 cert: ".
27             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
28 10         45 return $cert;
29             }
30              
31             sub PEM_cert2file {
32 0     0 1 0 my ($cert,$file) = @_;
33 0 0       0 my $string = Net::SSLeay::PEM_get_string_X509($cert)
34             or croak("cannot get string from cert");
35 0 0       0 open( my $fh,'>',$file ) or croak("cannot write $file: $!");
36 0         0 print $fh $string;
37             }
38              
39 14     14   118 use constant PEM_R_NO_START_LINE => 108;
  14         30  
  14         58399  
40             sub PEM_file2certs {
41 0     0 1 0 my $file = shift;
42 0 0       0 my $bio = Net::SSLeay::BIO_new_file($file,'r') or
43             croak "cannot read $file: $!";
44 0         0 my @certs;
45 0         0 while (1) {
46 0 0       0 if (my $cert = Net::SSLeay::PEM_read_bio_X509($bio)) {
47 0         0 push @certs, $cert;
48             } else {
49 0         0 Net::SSLeay::BIO_free($bio);
50 0         0 my $error = Net::SSLeay::ERR_get_error();
51 0 0 0     0 last if ($error & 0xfff) == PEM_R_NO_START_LINE && @certs;
52 0         0 croak "cannot parse $file as PEM X509 cert: " .
53             Net::SSLeay::ERR_error_string($error);
54             }
55             }
56 0         0 return @certs;
57             }
58              
59             sub PEM_certs2file {
60 0     0 1 0 my $file = shift;
61 0 0       0 open( my $fh,'>',$file ) or croak("cannot write $file: $!");
62 0         0 for my $cert (@_) {
63 0 0       0 my $string = Net::SSLeay::PEM_get_string_X509($cert)
64             or croak("cannot get string from cert");
65 0         0 print $fh $string;
66             }
67             }
68              
69              
70             sub PEM_string2cert {
71 592     592 1 287076 my $string = shift;
72 592         3219 my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem());
73 592         4143 Net::SSLeay::BIO_write($bio,$string);
74 592         52904 my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
75 592         3224 Net::SSLeay::BIO_free($bio);
76 592 50       1739 $cert or croak "cannot parse string as PEM X509 cert: ".
77             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
78 592         2051 return $cert;
79             }
80              
81             sub PEM_cert2string {
82 0     0 1 0 my $cert = shift;
83 0   0     0 return Net::SSLeay::PEM_get_string_X509($cert)
84             || croak("cannot get string from cert");
85             }
86              
87             sub PEM_file2key {
88 0     0 1 0 my $file = shift;
89 0 0       0 my $bio = Net::SSLeay::BIO_new_file($file,'r') or
90             croak "cannot read $file: $!";
91 0         0 my $key = Net::SSLeay::PEM_read_bio_PrivateKey($bio);
92 0         0 Net::SSLeay::BIO_free($bio);
93 0 0       0 $key or croak "cannot parse $file as PEM private key: ".
94             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
95 0         0 return $key;
96             }
97              
98             sub PEM_key2file {
99 0     0 1 0 my ($key,$file) = @_;
100 0 0       0 my $string = Net::SSLeay::PEM_get_string_PrivateKey($key)
101             or croak("cannot get string from key");
102 0 0       0 open( my $fh,'>',$file ) or croak("cannot write $file: $!");
103 0         0 print $fh $string;
104             }
105              
106             sub PEM_string2key {
107 0     0 1 0 my $string = shift;
108 0         0 my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem());
109 0         0 Net::SSLeay::BIO_write($bio,$string);
110 0         0 my $key = Net::SSLeay::PEM_read_bio_PrivateKey($bio);
111 0         0 Net::SSLeay::BIO_free($bio);
112 0 0       0 $key or croak "cannot parse string as PEM private key: ".
113             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
114 0         0 return $key;
115             }
116              
117             sub PEM_key2string {
118 0     0 1 0 my $key = shift;
119 0   0     0 return Net::SSLeay::PEM_get_string_PrivateKey($key)
120             || croak("cannot get string from key");
121             }
122              
123             sub CERT_free {
124 78     78 1 27288 Net::SSLeay::X509_free($_) for @_;
125             }
126              
127             sub KEY_free {
128 0     0 1 0 Net::SSLeay::EVP_PKEY_free($_) for @_;
129             }
130              
131             sub KEY_create_rsa {
132 16   50 16 1 86 my $bits = shift || 2048;
133 16         94 my $key = Net::SSLeay::EVP_PKEY_new();
134 16         2788489 my $rsa = Net::SSLeay::RSA_generate_key($bits, 0x10001); # 0x10001 = RSA_F4
135 16         328 Net::SSLeay::EVP_PKEY_assign_RSA($key,$rsa);
136 16         199 return $key;
137             }
138              
139             if (defined &Net::SSLeay::EC_KEY_generate_key) {
140             push @EXPORT,'KEY_create_ec';
141             *KEY_create_ec = sub {
142 0   0 0   0 my $curve = shift || 'prime256v1';
143 0         0 my $key = Net::SSLeay::EVP_PKEY_new();
144 0         0 my $ec = Net::SSLeay::EC_KEY_generate_key($curve);
145 0         0 Net::SSLeay::EVP_PKEY_assign_EC_KEY($key,$ec);
146 0         0 return $key;
147             }
148             }
149              
150             # extract information from cert
151             my %gen2i = qw( OTHERNAME 0 EMAIL 1 DNS 2 X400 3 DIRNAME 4 EDIPARTY 5 URI 6 IP 7 RID 8 );
152             my %i2gen = reverse %gen2i;
153             sub CERT_asHash {
154 0     0 1 0 my $cert = shift;
155 0   0     0 my $digest_name = shift || 'sha256';
156              
157 0         0 my %hash = (
158             version => Net::SSLeay::X509_get_version($cert),
159             not_before => _asn1t2t(Net::SSLeay::X509_get_notBefore($cert)),
160             not_after => _asn1t2t(Net::SSLeay::X509_get_notAfter($cert)),
161             serial => Net::SSLeay::P_ASN1_INTEGER_get_dec(
162             Net::SSLeay::X509_get_serialNumber($cert)),
163             signature_alg => Net::SSLeay::OBJ_obj2txt (
164             Net::SSLeay::P_X509_get_signature_alg($cert)),
165             crl_uri => [ Net::SSLeay::P_X509_get_crl_distribution_points($cert) ],
166             keyusage => [ Net::SSLeay::P_X509_get_key_usage($cert) ],
167             extkeyusage => {
168             oid => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,0) ],
169             nid => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,1) ],
170             sn => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,2) ],
171             ln => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,3) ],
172             },
173             "pubkey_digest_$digest_name" => Net::SSLeay::X509_pubkey_digest(
174             $cert,_digest($digest_name)),
175             "x509_digest_$digest_name" => Net::SSLeay::X509_digest(
176             $cert,_digest($digest_name)),
177             "fingerprint_$digest_name" => Net::SSLeay::X509_get_fingerprint(
178             $cert,$digest_name),
179             );
180              
181 0         0 for([ subject => Net::SSLeay::X509_get_subject_name($cert) ],
182             [ issuer => Net::SSLeay::X509_get_issuer_name($cert) ]) {
183 0         0 my ($what,$subj) = @$_;
184 0         0 my %subj;
185 0         0 for ( 0..Net::SSLeay::X509_NAME_entry_count($subj)-1 ) {
186 0         0 my $e = Net::SSLeay::X509_NAME_get_entry($subj,$_);
187 0         0 my $k = Net::SSLeay::OBJ_obj2txt(
188             Net::SSLeay::X509_NAME_ENTRY_get_object($e));
189 0         0 my $v = Net::SSLeay::P_ASN1_STRING_get(
190             Net::SSLeay::X509_NAME_ENTRY_get_data($e));
191 0 0       0 if (!exists $subj{$k}) {
    0          
192 0         0 $subj{$k} = $v;
193             } elsif (!ref $subj{$k}) {
194 0         0 $subj{$k} = [ $subj{$k}, $v ];
195             } else {
196 0         0 push @{$subj{$k}}, $v;
  0         0  
197             }
198             }
199 0         0 $hash{$what} = \%subj;
200             }
201              
202              
203 0 0       0 if ( my @names = Net::SSLeay::X509_get_subjectAltNames($cert) ) {
204 0         0 my $alt = $hash{subjectAltNames} = [];
205 0         0 while (my ($t,$v) = splice(@names,0,2)) {
206 0   0     0 $t = $i2gen{$t} || die "unknown type $t in subjectAltName";
207 0 0       0 if ( $t eq 'IP' ) {
208 0 0       0 if (length($v) == 4) {
    0          
209 0         0 $v = join('.',unpack("CCCC",$v));
210             } elsif ( length($v) == 16 ) {
211 0         0 my @v = unpack("nnnnnnnn",$v);
212 0         0 my ($best0,$last0);
213 0         0 for(my $i=0;$i<@v;$i++) {
214 0 0       0 if ($v[$i] == 0) {
215 0 0       0 if ($last0) {
216 0         0 $last0->[1] = $i;
217 0         0 $last0->[2]++;
218 0 0       0 $best0 = $last0 if ++$last0->[2]>$best0->[2];
219             } else {
220 0         0 $last0 = [ $i,$i,0 ];
221 0   0     0 $best0 ||= $last0;
222             }
223             } else {
224 0         0 $last0 = undef;
225             }
226             }
227 0 0       0 if ($best0) {
228 0         0 $v = '';
229 0 0       0 $v .= join(':', map { sprintf( "%x",$_) } @v[0..$best0->[0]-1]) if $best0->[0]>0;
  0         0  
230 0         0 $v .= '::';
231 0 0       0 $v .= join(':', map { sprintf( "%x",$_) } @v[$best0->[1]+1..$#v]) if $best0->[1]<$#v;
  0         0  
232             } else {
233 0         0 $v = join(':', map { sprintf( "%x",$_) } @v);
  0         0  
234             }
235             }
236             }
237 0         0 push @$alt,[$t,$v]
238             }
239             }
240              
241 0         0 my @ext;
242 0         0 for( 0..Net::SSLeay::X509_get_ext_count($cert)-1 ) {
243 0         0 my $e = Net::SSLeay::X509_get_ext($cert,$_);
244 0         0 my $o = Net::SSLeay::X509_EXTENSION_get_object($e);
245 0         0 my $nid = Net::SSLeay::OBJ_obj2nid($o);
246 0 0       0 push @ext, {
    0          
247             oid => Net::SSLeay::OBJ_obj2txt($o),
248             nid => ( $nid > 0 ) ? $nid : undef,
249             sn => ( $nid > 0 ) ? Net::SSLeay::OBJ_nid2sn($nid) : undef,
250             critical => Net::SSLeay::X509_EXTENSION_get_critical($e),
251             data => Net::SSLeay::X509V3_EXT_print($e),
252             }
253             }
254 0         0 $hash{ext} = \@ext;
255              
256 0 0       0 if ( defined(&Net::SSLeay::P_X509_get_ocsp_uri)) {
257 0         0 $hash{ocsp_uri} = [ Net::SSLeay::P_X509_get_ocsp_uri($cert) ];
258             } else {
259 0         0 $hash{ocsp_uri} = [];
260 0         0 for( @ext ) {
261 0 0       0 $_->{sn} or next;
262 0 0       0 $_->{sn} eq 'authorityInfoAccess' or next;
263 0         0 push @{ $hash{ocsp_uri}}, $_->{data} =~m{\bOCSP - URI:(\S+)}g;
  0         0  
264             }
265             }
266              
267 0         0 return \%hash;
268             }
269              
270             sub CERT_create {
271 120 50   120 1 50145 my %args = @_%2 ? %{ shift() } : @_;
  0         0  
272              
273 120         1168 my $cert = Net::SSLeay::X509_new();
274 120   50     709 my $digest_name = delete $args{digest} || 'sha256';
275              
276             Net::SSLeay::ASN1_INTEGER_set(
277             Net::SSLeay::X509_get_serialNumber($cert),
278 120   33     1191 delete $args{serial} || rand(2**32),
279             );
280              
281             # version default to 2 (V3)
282             Net::SSLeay::X509_set_version($cert,
283 120   50     720 delete $args{version} || 2 );
284              
285             # not_before default to now
286             Net::SSLeay::ASN1_TIME_set(
287             Net::SSLeay::X509_get_notBefore($cert),
288             delete $args{not_before} || time()
289 120   33     1664 );
290              
291             # not_after default to now+365 days
292             Net::SSLeay::ASN1_TIME_set(
293             Net::SSLeay::X509_get_notAfter($cert),
294 120   33     946 delete $args{not_after} || time() + 365*86400
295             );
296              
297             # set subject
298 120         343 my $subj_e = Net::SSLeay::X509_get_subject_name($cert);
299             my $subj = delete $args{subject} || {
300 120   100     381 organizationName => 'IO::Socket::SSL',
301             commonName => 'IO::Socket::SSL Test'
302             };
303              
304 120         651 while ( my ($k,$v) = each %$subj ) {
305             # Not everything we get is nice - try with MBSTRING_UTF8 first and if it
306             # fails try V_ASN1_T61STRING and finally V_ASN1_OCTET_STRING
307 116 50       355 for (ref($v) ? @$v : ($v)) {
308 116 0 33     2181 Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,0x1000,$_,-1,0)
      33        
309             or Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,20,$_,-1,0)
310             or Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,4,$_,-1,0)
311             or croak("failed to add entry for $k - ".
312             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()));
313             }
314             }
315              
316 120         3846 my @ext = (
317             &Net::SSLeay::NID_subject_key_identifier => 'hash',
318             &Net::SSLeay::NID_authority_key_identifier => 'keyid',
319             );
320 120 100       5796 if ( my $altsubj = delete $args{subjectAltNames} ) {
321             push @ext,
322             &Net::SSLeay::NID_subject_alt_name =>
323 22         379 join(',', map { "$_->[0]:$_->[1]" } @$altsubj)
  57         779  
324             }
325              
326 120   66     460 my $key = delete $args{key} || KEY_create_rsa();
327 120         1340 Net::SSLeay::X509_set_pubkey($cert,$key);
328              
329 120         321 my $is = delete $args{issuer};
330 120   66     604 my $issuer_cert = delete $args{issuer_cert} || $is && $is->[0] || $cert;
331 120   66     458 my $issuer_key = delete $args{issuer_key} || $is && $is->[1] || $key;
332              
333 120         220 my %purpose;
334 120 100       308 if (my $p = delete $args{purpose}) {
335 2 50       29 if (!ref($p)) {
    50          
336 0 0 0     0 $purpose{lc($2)} = (!$1 || $1 eq '+') ? 1:0
337             while $p =~m{([+-]?)(\w+)}g;
338             } elsif (ref($p) eq 'ARRAY') {
339 0         0 for(@$p) {
340 0 0       0 m{^([+-]?)(\w+)$} or die "invalid entry in purpose: $_";
341 0 0 0     0 $purpose{lc($2)} = (!$1 || $1 eq '+') ? 1:0
342             }
343             } else {
344 2         38 while( my ($k,$v) = each %$p) {
345 2 50 33     33 $purpose{lc($k)} = ($v && $v ne '-')?1:0;
346             }
347             }
348             }
349 120 100       304 if (delete $args{CA}) {
350             # add defaults for CA
351 10         137 %purpose = (
352             ca => 1, sslca => 1, emailca => 1, objca => 1,
353             %purpose
354             );
355             }
356 120 100       303 if (!%purpose) {
357 108         408 %purpose = (server => 1, client => 1);
358             }
359              
360 120         281 my (%key_usage,%ext_key_usage,%cert_type,%basic_constraints);
361              
362 120         365 my %dS = ( digitalSignature => \%key_usage );
363 120         330 my %kE = ( keyEncipherment => \%key_usage );
364 120         507 my %CA = ( 'CA:TRUE' => \%basic_constraints, %dS, keyCertSign => \%key_usage );
365 120         290 my @disable;
366 120         4235 for(
367             [ client => { %dS, %kE, clientAuth => \%ext_key_usage, client => \%cert_type } ],
368             [ server => { %dS, %kE, serverAuth => \%ext_key_usage, server => \%cert_type } ],
369             [ email => { %dS, %kE, emailProtection => \%ext_key_usage, email => \%cert_type } ],
370             [ objsign => { %dS, %kE, codeSigning => \%ext_key_usage, objsign => \%cert_type } ],
371              
372             [ CA => { %CA }],
373             [ sslCA => { %CA, sslCA => \%cert_type }],
374             [ emailCA => { %CA, emailCA => \%cert_type }],
375             [ objCA => { %CA, objCA => \%cert_type }],
376              
377             [ emailProtection => { %dS, %kE, emailProtection => \%ext_key_usage, email => \%cert_type } ],
378             [ codeSigning => { %dS, %kE, codeSigning => \%ext_key_usage, objsign => \%cert_type } ],
379              
380             [ timeStamping => { timeStamping => \%ext_key_usage } ],
381             [ digitalSignature => { digitalSignature => \%key_usage } ],
382             [ nonRepudiation => { nonRepudiation => \%key_usage } ],
383             [ keyEncipherment => { keyEncipherment => \%key_usage } ],
384             [ dataEncipherment => { dataEncipherment => \%key_usage } ],
385             [ keyAgreement => { keyAgreement => \%key_usage } ],
386             [ keyCertSign => { keyCertSign => \%key_usage } ],
387             [ cRLSign => { cRLSign => \%key_usage } ],
388             [ encipherOnly => { encipherOnly => \%key_usage } ],
389             [ decipherOnly => { decipherOnly => \%key_usage } ],
390             [ clientAuth => { clientAuth => \%ext_key_usage } ],
391             [ serverAuth => { serverAuth => \%ext_key_usage } ],
392             ) {
393 2640 100       5332 exists $purpose{lc($_->[0])} or next;
394 258 50       670 if (delete $purpose{lc($_->[0])}) {
395 258         434 while (my($k,$h) = each %{$_->[1]}) {
  1280         3489  
396 1022         1911 $h->{$k} = 1;
397             }
398             } else {
399 0         0 push @disable, $_->[1];
400             }
401             }
402 120 50       1519 die "unknown purpose ".join(",",keys %purpose) if %purpose;
403 120         300 for(@disable) {
404 0         0 while (my($k,$h) = each %$_) {
405 0         0 delete $h->{$k};
406             }
407             }
408              
409 120 100       272 if (%basic_constraints) {
410 10         367 push @ext,&Net::SSLeay::NID_basic_constraints,
411             => join(",",'critical', sort keys %basic_constraints);
412             } else {
413 110         3114 push @ext, &Net::SSLeay::NID_basic_constraints => 'critical,CA:FALSE';
414             }
415 120 50       4353 push @ext,&Net::SSLeay::NID_key_usage
416             => join(",",'critical', sort keys %key_usage) if %key_usage;
417 120 50       3753 push @ext,&Net::SSLeay::NID_netscape_cert_type
418             => join(",",sort keys %cert_type) if %cert_type;
419 120 100       3461 push @ext,&Net::SSLeay::NID_ext_key_usage
420             => join(",",sort keys %ext_key_usage) if %ext_key_usage;
421 120         8038 Net::SSLeay::P_X509_add_extensions($cert, $issuer_cert, @ext);
422              
423 120         272 my %have_ext;
424 120         415 for(my $i=0;$i<@ext;$i+=2) {
425 732         2162 $have_ext{ $ext[$i] }++
426             }
427 120 50       300 for my $ext (@{ $args{ext} || [] }) {
  120         1021  
428             my $nid = $ext->{nid}
429             || $ext->{sn} && Net::SSLeay::OBJ_sn2nid($ext->{sn})
430 0   0     0 || croak "cannot determine NID of extension";
431 0 0       0 $have_ext{$nid} and next;
432 0         0 my $val = $ext->{data};
433 0 0       0 if ($nid == 177) {
434             # authorityInfoAccess:
435             # OpenSSL i2v does not output the same way as expected by i2v :(
436 0         0 for (split(/\n/,$val)) {
437 0         0 s{ - }{;}; # "OCSP - URI:..." -> "OCSP;URI:..."
438 0 0       0 $_ = "critical,$_" if $ext->{critical};
439 0         0 Net::SSLeay::P_X509_add_extensions($cert,$issuer_cert,$nid,$_);
440             }
441             } else {
442 0 0       0 $val = "critical,$val" if $ext->{critical};
443 0         0 Net::SSLeay::P_X509_add_extensions($cert, $issuer_cert, $nid, $val);
444             }
445             }
446              
447 120         2231 Net::SSLeay::X509_set_issuer_name($cert,
448             Net::SSLeay::X509_get_subject_name($issuer_cert));
449 120         388 Net::SSLeay::X509_sign($cert,$issuer_key,_digest($digest_name));
450              
451 120         1650 return ($cert,$key);
452             }
453              
454              
455              
456             if ( defined &Net::SSLeay::ASN1_TIME_timet ) {
457             *_asn1t2t = \&Net::SSLeay::ASN1_TIME_timet
458             } else {
459             require Time::Local;
460             my %mon2i = qw(
461             Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5
462             Jul 6 Aug 7 Sep 8 Oct 9 Nov 10 Dec 11
463             );
464             *_asn1t2t = sub {
465             my $t = Net::SSLeay::P_ASN1_TIME_put2string( shift );
466             my ($mon,$d,$h,$m,$s,$y,$tz) = split(/[\s:]+/,$t);
467             defined( $mon = $mon2i{$mon} ) or die "invalid month in $t";
468             $tz ||= $y =~s{^(\d+)([A-Z]\S*)}{$1} && $2;
469             if ( ! $tz ) {
470             return Time::Local::timelocal($s,$m,$h,$d,$mon,$y)
471             } elsif ( $tz eq 'GMT' ) {
472             return Time::Local::timegm($s,$m,$h,$d,$mon,$y)
473             } else {
474             die "unexpected TZ $tz from ASN1_TIME_print";
475             }
476             }
477             }
478              
479             {
480             my %digest;
481             sub _digest {
482 120     120   242 my $digest_name = shift;
483 120   66     228327 return $digest{$digest_name} ||= do {
484 6         112 Net::SSLeay::SSLeay_add_ssl_algorithms();
485 6 50       16883 Net::SSLeay::EVP_get_digestbyname($digest_name)
486             or die "Digest algorithm $digest_name is not available";
487             };
488             }
489             }
490              
491              
492             1;
493              
494             __END__