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   7837 use strict;
  14         36  
  14         550  
4 14     14   76 use warnings;
  14         46  
  14         487  
5 14     14   83 use Carp 'croak';
  14         44  
  14         756  
6 14     14   95 use Net::SSLeay;
  14         35  
  14         4635  
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 334 my $file = shift;
22 10 50       340 my $bio = Net::SSLeay::BIO_new_file($file,'r') or
23             croak "cannot read $file: $!";
24 10         842 my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
25 10         159 Net::SSLeay::BIO_free($bio);
26 10 50       39 $cert or croak "cannot parse $file as PEM X509 cert: ".
27             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
28 10         103 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   106 use constant PEM_R_NO_START_LINE => 108;
  14         31  
  14         57509  
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 287741 my $string = shift;
72 592         3115 my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem());
73 592         4102 Net::SSLeay::BIO_write($bio,$string);
74 592         54222 my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
75 592         3202 Net::SSLeay::BIO_free($bio);
76 592 50       1892 $cert or croak "cannot parse string as PEM X509 cert: ".
77             Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error());
78 592         2093 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 36543 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 98 my $bits = shift || 2048;
133 16         81 my $key = Net::SSLeay::EVP_PKEY_new();
134 16         2330899 my $rsa = Net::SSLeay::RSA_generate_key($bits, 0x10001); # 0x10001 = RSA_F4
135 16         286 Net::SSLeay::EVP_PKEY_assign_RSA($key,$rsa);
136 16         167 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 46207 my %args = @_%2 ? %{ shift() } : @_;
  0         0  
272              
273 120         1268 my $cert = Net::SSLeay::X509_new();
274 120   50     732 my $digest_name = delete $args{digest} || 'sha256';
275              
276             Net::SSLeay::ASN1_INTEGER_set(
277             Net::SSLeay::X509_get_serialNumber($cert),
278 120   33     1142 delete $args{serial} || rand(2**32),
279             );
280              
281             # version default to 2 (V3)
282             Net::SSLeay::X509_set_version($cert,
283 120   50     678 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     1640 );
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     859 delete $args{not_after} || time() + 365*86400
295             );
296              
297             # set subject
298 120         320 my $subj_e = Net::SSLeay::X509_get_subject_name($cert);
299             my $subj = delete $args{subject} || {
300 120   100     461 organizationName => 'IO::Socket::SSL',
301             commonName => 'IO::Socket::SSL Test'
302             };
303              
304 120         640 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       362 for (ref($v) ? @$v : ($v)) {
308 116 0 33     2175 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         3833 my @ext = (
317             &Net::SSLeay::NID_subject_key_identifier => 'hash',
318             &Net::SSLeay::NID_authority_key_identifier => 'keyid',
319             );
320 120 100       4268 if ( my $altsubj = delete $args{subjectAltNames} ) {
321             push @ext,
322             &Net::SSLeay::NID_subject_alt_name =>
323 22         347 join(',', map { "$_->[0]:$_->[1]" } @$altsubj)
  57         686  
324             }
325              
326 120   66     437 my $key = delete $args{key} || KEY_create_rsa();
327 120         1314 Net::SSLeay::X509_set_pubkey($cert,$key);
328              
329 120         308 my $is = delete $args{issuer};
330 120   66     578 my $issuer_cert = delete $args{issuer_cert} || $is && $is->[0] || $cert;
331 120   66     460 my $issuer_key = delete $args{issuer_key} || $is && $is->[1] || $key;
332              
333 120         218 my %purpose;
334 120 100       287 if (my $p = delete $args{purpose}) {
335 2 50       23 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         27 while( my ($k,$v) = each %$p) {
345 2 50 33     24 $purpose{lc($k)} = ($v && $v ne '-')?1:0;
346             }
347             }
348             }
349 120 100       293 if (delete $args{CA}) {
350             # add defaults for CA
351 10         123 %purpose = (
352             ca => 1, sslca => 1, emailca => 1, objca => 1,
353             %purpose
354             );
355             }
356 120 100       290 if (!%purpose) {
357 108         390 %purpose = (server => 1, client => 1);
358             }
359              
360 120         257 my (%key_usage,%ext_key_usage,%cert_type,%basic_constraints);
361              
362 120         383 my %dS = ( digitalSignature => \%key_usage );
363 120         288 my %kE = ( keyEncipherment => \%key_usage );
364 120         558 my %CA = ( 'CA:TRUE' => \%basic_constraints, %dS, keyCertSign => \%key_usage );
365 120         254 my @disable;
366 120         4143 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       5429 exists $purpose{lc($_->[0])} or next;
394 258 50       662 if (delete $purpose{lc($_->[0])}) {
395 258         369 while (my($k,$h) = each %{$_->[1]}) {
  1280         3444  
396 1022         1909 $h->{$k} = 1;
397             }
398             } else {
399 0         0 push @disable, $_->[1];
400             }
401             }
402 120 50       1564 die "unknown purpose ".join(",",keys %purpose) if %purpose;
403 120         314 for(@disable) {
404 0         0 while (my($k,$h) = each %$_) {
405 0         0 delete $h->{$k};
406             }
407             }
408              
409 120 100       276 if (%basic_constraints) {
410 10         383 push @ext,&Net::SSLeay::NID_basic_constraints,
411             => join(",",'critical', sort keys %basic_constraints);
412             } else {
413 110         2916 push @ext, &Net::SSLeay::NID_basic_constraints => 'critical,CA:FALSE';
414             }
415 120 50       3835 push @ext,&Net::SSLeay::NID_key_usage
416             => join(",",'critical', sort keys %key_usage) if %key_usage;
417 120 50       3586 push @ext,&Net::SSLeay::NID_netscape_cert_type
418             => join(",",sort keys %cert_type) if %cert_type;
419 120 100       3314 push @ext,&Net::SSLeay::NID_ext_key_usage
420             => join(",",sort keys %ext_key_usage) if %ext_key_usage;
421 120         7557 Net::SSLeay::P_X509_add_extensions($cert, $issuer_cert, @ext);
422              
423 120         289 my %have_ext;
424 120         433 for(my $i=0;$i<@ext;$i+=2) {
425 732         2177 $have_ext{ $ext[$i] }++
426             }
427 120 50       300 for my $ext (@{ $args{ext} || [] }) {
  120         1017  
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         2229 Net::SSLeay::X509_set_issuer_name($cert,
448             Net::SSLeay::X509_get_subject_name($issuer_cert));
449 120         460 Net::SSLeay::X509_sign($cert,$issuer_key,_digest($digest_name));
450              
451 120         1550 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   239 my $digest_name = shift;
483 120   66     231336 return $digest{$digest_name} ||= do {
484 6         89 Net::SSLeay::SSLeay_add_ssl_algorithms();
485 6 50       16712 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__