File Coverage

lib/Crypt/Perl/X509/Extensions.pm
Criterion Covered Total %
statement 55 59 93.2
branch 15 20 75.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 0 1 0.0
total 86 96 89.5


line stmt bran cond sub pod time code
1             package Crypt::Perl::X509::Extensions;
2              
3 4     4   1058 use strict;
  4         10  
  4         149  
4 4     4   26 use warnings;
  4         8  
  4         149  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Crypt::Perl::X509::Extensions - extensions list for X.509 certificates
11              
12             =head1 SYNOPSIS
13              
14             #Each object passed should be an instance of a subclass of
15             #Crypt::Perl::X509::Extension
16             my $exreq = Crypt::Perl::X509::Extensions->new( @EXTN_OBJS );
17              
18             #...or:
19              
20             my $exreq = Crypt::Perl::X509::Extensions->new(
21             [ $extn_type1 => @args1 ],
22             [ $extn_type2 => @args2 ],
23             );
24              
25             #...for example:
26              
27             my $exreq = Crypt::Perl::X509::Extensions->new(
28             [ 'subjectAltName',
29             [ dNSName => 'foo.com' ],
30             [ dNSName => 'haha.tld' ],
31             ],
32             );
33              
34             =head1 DESCRIPTION
35              
36             Instances of this class represent the list of extensions in an X.509 (SSL)
37             certificate.
38              
39             You probably don’t need to
40             instantiate this class directly; instead, you can instantiate it
41             implicitly by listing out arguments to
42             L’s constructor. See that module’s
43             L for an example.
44              
45             Look in the L distribution’s
46             C namespace for supported extensions.
47              
48             =cut
49              
50 4     4   24 use Try::Tiny;
  4         8  
  4         201  
51              
52 4     4   24 use Module::Load ();
  4         10  
  4         65  
53              
54 4     4   20 use Crypt::Perl::ASN1 ();
  4         8  
  4         110  
55              
56 4         22 use parent qw(
57             Crypt::Perl::ASN1::Encodee
58 4     4   26 );
  4         17  
59              
60 4     4   300 use constant OID => '1.2.840.113549.1.9.14';
  4         9  
  4         355  
61              
62 4     4   28 use constant ASN1 => <
  4         9  
  4         2564  
63             Extension ::= SEQUENCE {
64             extnID OBJECT IDENTIFIER,
65             critical BOOLEAN OPTIONAL,
66             extnValue OCTET STRING
67             }
68              
69             Extensions ::= SEQUENCE OF Extension
70             END
71              
72             my $EXT_BASE = 'Crypt::Perl::X509::Extension';
73              
74             sub new {
75 17     17 0 1496 my ($class, @extensions) = @_;
76              
77 17 50       100 if (!@extensions) {
78 0         0 die Crypt::Perl::X::create('Generic', "Empty “extensions”!");
79             }
80              
81 17         123 for my $ext (@extensions) {
82 137 100   137   778 if (!try { $ext->isa($EXT_BASE) }) {
  137         5223  
83 136 100       2340 if ( 'HASH' eq ref $ext ) {
    50          
84 1 50   1   7 if ( !try { $ext->{'extension'}->isa($EXT_BASE) }) {
  1         47  
85 0 0       0 if ( 'ARRAY' ne ref $ext->{'extension'} ) {
86 0         0 die Crypt::Perl::X::create('Generic', "“extension” in HASH reference must be ARRAY reference or instance of $EXT_BASE, not “$ext”!");
87             }
88             }
89             }
90             elsif ( 'ARRAY' ne ref $ext ) {
91 0         0 die Crypt::Perl::X::create('Generic', "Extension must be HASH reference, ARRAY reference, or instance of $EXT_BASE, not “$ext”!");
92             }
93             }
94             }
95              
96 17         205 return bless \@extensions, $class;
97             }
98              
99             sub _new_parse_arrayref {
100 135     135   311 my ($ext) = @_;
101 135         434 my $module = $ext->[0];
102              
103             # For the acmeValdation-v1 extension …
104 135         296 $module =~ tr<-><_>;
105              
106 135         360 my $class = "Crypt::Perl::X509::Extension::$module";
107 135         506 Module::Load::load($class);
108 135         10622 return $class->new( @{$ext}[ 1 .. $#$ext ] );
  135         3098  
109             }
110              
111             sub _encode_params {
112 17     17   91 my ($self) = @_;
113              
114 17         98 my @exts_asn1;
115              
116 17         97 for my $ext ( @$self ) {
117 137         23522 my ($critical, $real_ext);
118 137 100       463 if ('HASH' eq ref $ext) {
119 1         15 ($critical, $real_ext) = @{$ext}{ qw(critical extension) };
  1         4  
120             }
121             else {
122 136         245 $real_ext = $ext;
123             }
124              
125 137 100       390 if ('ARRAY' eq ref $real_ext) {
126 135         358 $real_ext = _new_parse_arrayref($real_ext);
127             }
128              
129 137 100       532 if (!defined $critical) {
130 136         975 $critical = $real_ext->can('CRITICAL');
131 136   100     759 $critical &&= $critical->();
132             }
133              
134 137 100       1202 push @exts_asn1, {
135             extnID => $real_ext->OID(),
136             ($critical ? (critical => Crypt::Perl::ASN1->ASN_BOOLEAN()) : ()),
137             extnValue => $real_ext->encode(),
138             },
139             };
140              
141 17         1865 return \@exts_asn1;
142             }
143              
144             1;