File Coverage

blib/lib/Convert/ASN1.pm
Criterion Covered Total %
statement 201 248 81.0
branch 56 86 65.1
condition 13 21 61.9
subroutine 39 45 86.6
pod 15 19 78.9
total 324 419 77.3


line stmt bran cond sub pod time code
1             # Copyright (c) 2000-2002 Graham Barr . All rights reserved.
2             # This program is free software; you can redistribute it and/or
3             # modify it under the same terms as Perl itself.
4              
5             package Convert::ASN1;
6             $Convert::ASN1::VERSION = '0.34';
7 23     23   15613 use 5.004;
  23         217  
8 23     23   125 use strict;
  23         56  
  23         891  
9 23     23   223 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS @opParts @opName $AUTOLOAD);
  23         68  
  23         2673  
10 23     23   199 use Exporter;
  23         59  
  23         1357  
11              
12 23     23   147 use constant CHECK_UTF8 => $] > 5.007;
  23         58  
  23         7967  
13              
14             BEGIN {
15 23     23   152 local $SIG{__DIE__};
16 23 50       55 eval { require bytes and 'bytes'->import };
  23         14955  
17              
18 23         594 if (CHECK_UTF8) {
19 23         13141 require Encode;
20 23         249647 require utf8;
21             }
22              
23 23         726 @ISA = qw(Exporter);
24              
25 23         558 %EXPORT_TAGS = (
26             io => [qw(asn_recv asn_send asn_read asn_write asn_get asn_ready)],
27              
28             debug => [qw(asn_dump asn_hexdump)],
29              
30             const => [qw(ASN_BOOLEAN ASN_INTEGER ASN_BIT_STR ASN_OCTET_STR
31             ASN_NULL ASN_OBJECT_ID ASN_REAL ASN_ENUMERATED
32             ASN_SEQUENCE ASN_SET ASN_PRINT_STR ASN_IA5_STR
33             ASN_UTC_TIME ASN_GENERAL_TIME ASN_RELATIVE_OID
34             ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE
35             ASN_PRIMITIVE ASN_CONSTRUCTOR ASN_LONG_LEN ASN_EXTENSION_ID ASN_BIT)],
36              
37             tag => [qw(asn_tag asn_decode_tag2 asn_decode_tag asn_encode_tag asn_decode_length asn_encode_length)]
38             );
39              
40 23         124 @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
  92         318  
41 23         81 $EXPORT_TAGS{all} = \@EXPORT_OK;
42              
43 23         78 @opParts = qw(
44             cTAG cTYPE cVAR cLOOP cOPT cEXT cCHILD cDEFINE
45             );
46              
47 23         128 @opName = qw(
48             opUNKNOWN opBOOLEAN opINTEGER opBITSTR opSTRING opNULL opOBJID opREAL
49             opSEQUENCE opEXPLICIT opSET opUTIME opGTIME opUTF8 opANY opCHOICE opROID opBCD
50             opEXTENSIONS
51             );
52              
53 23         70 foreach my $l (\@opParts, \@opName) {
54 46         97 my $i = 0;
55 46         116 foreach my $name (@$l) {
56 621         956 my $j = $i++;
57 23     23   178 no strict 'refs';
  23         47  
  23         2322  
58 621         7106 *{__PACKAGE__ . '::' . $name} = sub () { $j }
  0         0  
59 621         2343 }
60             }
61             }
62              
63             sub _internal_syms {
64 23     23   92 my $pkg = caller;
65 23     23   166 no strict 'refs';
  23         49  
  23         92325  
66 23         94 for my $sub (@opParts,@opName,'dump_op') {
67 644         907 *{$pkg . '::' . $sub} = \&{__PACKAGE__ . '::' . $sub};
  644         128724  
  644         1445  
68             }
69             }
70              
71             sub ASN_BOOLEAN () { 0x01 }
72             sub ASN_INTEGER () { 0x02 }
73             sub ASN_BIT_STR () { 0x03 }
74             sub ASN_OCTET_STR () { 0x04 }
75             sub ASN_NULL () { 0x05 }
76             sub ASN_OBJECT_ID () { 0x06 }
77             sub ASN_REAL () { 0x09 }
78             sub ASN_ENUMERATED () { 0x0A }
79             sub ASN_RELATIVE_OID () { 0x0D }
80             sub ASN_SEQUENCE () { 0x10 }
81             sub ASN_SET () { 0x11 }
82             sub ASN_PRINT_STR () { 0x13 }
83             sub ASN_IA5_STR () { 0x16 }
84             sub ASN_UTC_TIME () { 0x17 }
85             sub ASN_GENERAL_TIME () { 0x18 }
86              
87             sub ASN_UNIVERSAL () { 0x00 }
88             sub ASN_APPLICATION () { 0x40 }
89             sub ASN_CONTEXT () { 0x80 }
90             sub ASN_PRIVATE () { 0xC0 }
91              
92             sub ASN_PRIMITIVE () { 0x00 }
93             sub ASN_CONSTRUCTOR () { 0x20 }
94              
95             sub ASN_LONG_LEN () { 0x80 }
96             sub ASN_EXTENSION_ID () { 0x1F }
97             sub ASN_BIT () { 0x80 }
98              
99              
100             sub new {
101 31     31 1 532 my $pkg = shift;
102 31         109 my $self = bless {}, $pkg;
103              
104 31         141 $self->configure(@_);
105 31         173 $self;
106             }
107              
108              
109             sub configure {
110 38     38 1 215 my $self = shift;
111 38         103 my %opt = @_;
112              
113 38   100     375 $self->{options}{encoding} = uc($opt{encoding} || 'BER');
114              
115 38 50       293 unless ($self->{options}{encoding} =~ /^[BD]ER$/) {
116 0         0 require Carp;
117 0         0 Carp::croak("Unsupported encoding format '$opt{encoding}'");
118             }
119              
120             # IMPLICIT as default for backwards compatibility, even though it's wrong.
121 38   100     254 $self->{options}{tagdefault} = uc($opt{tagdefault} || 'IMPLICIT');
122              
123 38 50       233 unless ($self->{options}{tagdefault} =~ /^(?:EXPLICIT|IMPLICIT)$/) {
124 0         0 require Carp;
125 0         0 Carp::croak("Default tagging must be EXPLICIT/IMPLICIT. Not $opt{tagdefault}");
126             }
127              
128              
129 38         111 for my $type (qw(encode decode)) {
130 76 100       224 if (exists $opt{$type}) {
131 7         22 while(my($what,$value) = each %{$opt{$type}}) {
  14         56  
132 7         26 $self->{options}{"${type}_${what}"} = $value;
133             }
134             }
135             }
136             }
137              
138              
139              
140             sub find {
141 16     16 1 40 my $self = shift;
142 16         107 my $what = shift;
143 16 50       136 return unless exists $self->{tree}{$what};
144 16         96 my %new = %$self;
145 16         53 $new{script} = $new{tree}->{$what};
146 16         110 bless \%new, ref($self);
147             }
148              
149              
150             sub prepare {
151 98     98 1 1300 my $self = shift;
152 98         178 my $asn = shift;
153              
154 98 50       268 $self = $self->new unless ref($self);
155 98         155 my $tree;
156 98 50       243 if( ref($asn) eq 'GLOB' ){
157 0         0 local $/ = undef;
158 0         0 my $txt = <$asn>;
159 0         0 $tree = Convert::ASN1::parser::parse($txt,$self->{options}{tagdefault});
160             } else {
161 98         335 $tree = Convert::ASN1::parser::parse($asn,$self->{options}{tagdefault});
162             }
163              
164 98 50       275 unless ($tree) {
165 0         0 $self->{error} = $@;
166 0         0 return;
167             ### If $self has been set to a new object, not returning
168             ### this object here will destroy the object, so the caller
169             ### won't be able to get at the error.
170             }
171              
172 98         257 $self->{tree} = _pack_struct($tree);
173 98         333 $self->{script} = (values %$tree)[0];
174 98         579 $self;
175             }
176              
177             sub prepare_file {
178 0     0 1 0 my $self = shift;
179 0         0 my $asnp = shift;
180              
181 0         0 local *ASN;
182             open( ASN, $asnp )
183 0 0       0 or do{ $self->{error} = $@; return; };
  0         0  
  0         0  
184 0         0 my $ret = $self->prepare( \*ASN );
185 0         0 close( ASN );
186 0         0 $ret;
187             }
188              
189             sub registeroid {
190 2     2 1 3 my $self = shift;
191 2         4 my $oid = shift;
192 2         3 my $handler = shift;
193              
194 2         5 $self->{options}{oidtable}{$oid}=$handler;
195 2         9 $self->{oidtable}{$oid}=$handler;
196             }
197              
198             sub registertype {
199 0     0 1 0 my $self = shift;
200 0         0 my $def = shift;
201 0         0 my $type = shift;
202 0         0 my $handler = shift;
203              
204 0         0 $self->{options}{handlers}{$def}{$type}=$handler;
205             }
206              
207             # In XS the will convert the tree between perl and C structs
208              
209 98     98   239 sub _pack_struct { $_[0] }
210 0     0   0 sub _unpack_struct { $_[0] }
211              
212             ##
213             ## Encoding
214             ##
215              
216             sub encode {
217 106     106 1 482 my $self = shift;
218 106 100       409 my $stash = @_ == 1 ? shift : { @_ };
219 106         206 my $buf = '';
220 106         344 local $SIG{__DIE__};
221 106         373 eval { _encode($self->{options}, $self->{script}, $stash, [], $buf) }
222 106 100       170 or do { $self->{error} = $@; undef }
  3         259  
  3         28  
223             }
224              
225              
226              
227             # Encode tag value for encoding.
228             # We assume that the tag has been correctly generated with asn_tag()
229              
230             sub asn_encode_tag {
231 832 50   832 1 3095 $_[0] >> 8
    100          
    100          
232             ? $_[0] & 0x8000
233             ? $_[0] & 0x800000
234             ? pack("V",$_[0])
235             : substr(pack("V",$_[0]),0,3)
236             : pack("v", $_[0])
237             : pack("C",$_[0]);
238             }
239              
240              
241             # Encode a length. If < 0x80 then encode as a byte. Otherwise encode
242             # 0x80 | num_bytes followed by the bytes for the number. top end
243             # bytes of all zeros are not encoded
244              
245             sub asn_encode_length {
246              
247 198 100   198 1 465 if($_[0] >> 7) {
248 6         19 my $lenlen = &num_length;
249              
250 6         39 return pack("Ca*", $lenlen | 0x80, substr(pack("N",$_[0]), -$lenlen));
251             }
252              
253 192         712 return pack("C", $_[0]);
254             }
255              
256              
257             ##
258             ## Decoding
259             ##
260              
261             sub decode {
262 132     132 1 10281 my $self = shift;
263 132         191 my $ret;
264              
265 132         503 local $SIG{__DIE__};
266             eval {
267 132         221 my (%stash, $result);
268 132         303 my $script = $self->{script};
269 132         241 my $stash = \$result;
270              
271 132         365 while ($script) {
272 169 50       422 my $child = $script->[0] or last;
273 169 100 100     765 if (@$script > 1 or defined $child->[cVAR]) {
274 112         229 $result = $stash = \%stash;
275 112         200 last;
276             }
277 57 100 100     327 last if $child->[cTYPE] == opCHOICE or $child->[cLOOP];
278 44         124 $script = $child->[cCHILD];
279             }
280              
281             _decode(
282             $self->{options},
283             $self->{script},
284 132         721 $stash,
285             0,
286             length $_[0],
287             undef,
288             {},
289             $_[0]);
290              
291 129         256 $ret = $result;
292 129         369 1;
293 132 100 50     263 } or $self->{'error'} = $@ || 'Unknown error';
294              
295 132         807 $ret;
296             }
297              
298              
299             sub asn_decode_length {
300 9 50   9 1 56 return unless length $_[0];
301              
302 9         22 my $len = unpack("C",$_[0]);
303              
304 9 100       34 if($len & 0x80) {
305 4 50       12 $len &= 0x7f or return (1,-1);
306              
307 4 50       11 return if $len >= length $_[0];
308              
309 4         25 return (1+$len, unpack("N", "\0" x (4 - $len) . substr($_[0],1,$len)));
310             }
311 5         30 return (1, $len);
312             }
313              
314              
315             sub asn_decode_tag {
316 11 100   11 1 34 return unless length $_[0];
317              
318 9         19 my $tag = unpack("C", $_[0]);
319 9         14 my $n = 1;
320              
321 9 100       62 if(($tag & 0x1f) == 0x1f) {
322 4         7 my $b;
323 4         7 do {
324 6 50       10 return if $n >= length $_[0];
325 6         12 $b = unpack("C",substr($_[0],$n,1));
326 6         17 $tag |= $b << (8 * $n++);
327             } while($b & 0x80);
328             }
329 9         49 ($n, $tag);
330             }
331              
332              
333             sub asn_decode_tag2 {
334 0 0   0 0 0 return unless length $_[0];
335              
336 0         0 my $tag = unpack("C",$_[0]);
337 0         0 my $num = $tag & 0x1f;
338 0         0 my $len = 1;
339              
340 0 0       0 if($num == 0x1f) {
341 0         0 $num = 0;
342 0         0 my $b;
343 0         0 do {
344 0 0       0 return if $len >= length $_[0];
345 0         0 $b = unpack("C",substr($_[0],$len++,1));
346 0         0 $num = ($num << 7) + ($b & 0x7f);
347             } while($b & 0x80);
348             }
349 0         0 ($len, $tag, $num);
350             }
351              
352              
353             ##
354             ## Utilities
355             ##
356              
357             # How many bytes are needed to encode a number
358              
359             sub num_length {
360 66 100   66 0 230 $_[0] >> 8
    100          
    100          
361             ? $_[0] >> 16
362             ? $_[0] >> 24
363             ? 4
364             : 3
365             : 2
366             : 1
367             }
368              
369             # Convert from a bigint to an octet string
370              
371             sub i2osp {
372 12     12 0 30 my($num, $biclass) = @_;
373 12     1   919 eval "use $biclass";
  1     1   8  
  1     1   2  
  1     1   15  
  1     1   8  
  1     1   2  
  1     1   5  
  1     1   7  
  1     1   2  
  1     1   43  
  1     1   7  
  1     1   3  
  1         4  
  1         8  
  1         2  
  1         4  
  1         8  
  1         2  
  1         4  
  1         7  
  1         2  
  1         5  
  1         8  
  1         2  
  1         6  
  1         8  
  1         2  
  1         4  
  1         8  
  1         3  
  1         5  
  1         8  
  1         2  
  1         6  
  1         7  
  1         2  
  1         6  
374 12         3927 $num = $biclass->new($num);
375 12 100       948 my $neg = $num < 0
376             and $num = abs($num+1);
377 12         3223 my $base = $biclass->new(256);
378 12         436 my $result = '';
379 12         35 while($num != 0) {
380 101         18086 my $r = $num % $base;
381 101         10166 $num = ($num-$r) / $base;
382 101         25493 $result .= pack("C",$r);
383             }
384 12 100       2117 $result ^= pack("C",255) x length($result) if $neg;
385 12         60 return scalar reverse $result;
386             }
387              
388             # Convert from an octet string to a bigint
389              
390             sub os2ip {
391 16     16 0 43 my($os, $biclass) = @_;
392 16         942 eval "require $biclass";
393 16         52220 my $base = $biclass->new(256);
394 16         47897 my $result = $biclass->new(0);
395 16 100       1587 my $neg = unpack("C",$os) >= 0x80
396             and $os ^= pack("C",255) x length($os);
397 16         54 for (unpack("C*",$os)) {
398 171         35062 $result = ($result * $base) + $_;
399             }
400 16 100       3750 return $neg ? ($result + 1) * -1 : $result;
401             }
402              
403             # Given a class and a tag, calculate an integer which when encoded
404             # will become the tag. This means that the class bits are always
405             # in the bottom byte, so are the tag bits if tag < 30. Otherwise
406             # the tag is in the upper 3 bytes. The upper bytes are encoded
407             # with bit8 representing that there is another byte. This
408             # means the max tag we can do is 0x1fffff
409              
410             sub asn_tag {
411 96     96 1 223 my($class,$value) = @_;
412              
413 96 50       225 die sprintf "Bad tag class 0x%x",$class
414             if $class & ~0xe0;
415              
416 96 100 66     376 unless ($value & ~0x1f or $value == 0x1f) {
417 90         252 return (($class & 0xe0) | $value);
418             }
419              
420 6 50       17 die sprintf "Tag value 0x%08x too big\n",$value
421             if $value & 0xffe00000;
422              
423 6         9 $class = ($class | 0x1f) & 0xff;
424              
425 6         11 my @t = ($value & 0x7f);
426 6         19 unshift @t, (0x80 | ($value & 0x7f)) while $value >>= 7;
427 6         50 unpack("V",pack("C4",$class,@t,0,0));
428             }
429              
430              
431             BEGIN {
432             # When we have XS &_encode will be defined by the XS code
433             # so will all the subs in these required packages
434 23 50   23   231 unless (defined &_encode) {
435 23         12163 require Convert::ASN1::_decode;
436 23         10622 require Convert::ASN1::_encode;
437 23         10302 require Convert::ASN1::IO;
438             }
439              
440 23         12239 require Convert::ASN1::parser;
441             }
442              
443             sub AUTOLOAD {
444 0 0   0   0 require Convert::ASN1::Debug if $AUTOLOAD =~ /dump/;
445 0 0       0 goto &{$AUTOLOAD} if defined &{$AUTOLOAD};
  0         0  
  0         0  
446 0         0 require Carp;
447 0   0     0 my $pkg = ref($_[0]) || ($_[0] =~ /^[\w\d]+(?:::[\w\d]+)*$/)[0];
448 0 0 0     0 if ($pkg and UNIVERSAL::isa($pkg, __PACKAGE__)) { # guess it was a method call
449 0         0 $AUTOLOAD =~ s/.*:://;
450 0         0 Carp::croak(sprintf q{Can't locate object method "%s" via package "%s"},$AUTOLOAD,$pkg);
451             }
452             else {
453 0         0 Carp::croak(sprintf q{Undefined subroutine &%s called}, $AUTOLOAD);
454             }
455             }
456              
457       0     sub DESTROY {}
458              
459 6     6 1 60 sub error { $_[0]->{error} }
460             1;