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.33';
7 23     23   14591 use 5.004;
  23         212  
8 23     23   118 use strict;
  23         51  
  23         934  
9 23     23   200 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS @opParts @opName $AUTOLOAD);
  23         65  
  23         2451  
10 23     23   183 use Exporter;
  23         47  
  23         1261  
11              
12 23     23   141 use constant CHECK_UTF8 => $] > 5.007;
  23         70  
  23         7724  
13              
14             BEGIN {
15 23     23   139 local $SIG{__DIE__};
16 23 50       51 eval { require bytes and 'bytes'->import };
  23         13602  
17              
18 23         552 if (CHECK_UTF8) {
19 23         11840 require Encode;
20 23         230955 require utf8;
21             }
22              
23 23         806 @ISA = qw(Exporter);
24              
25 23         496 %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         115 @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
  92         294  
41 23         75 $EXPORT_TAGS{all} = \@EXPORT_OK;
42              
43 23         77 @opParts = qw(
44             cTAG cTYPE cVAR cLOOP cOPT cEXT cCHILD cDEFINE
45             );
46              
47 23         101 @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         61 foreach my $l (\@opParts, \@opName) {
54 46         87 my $i = 0;
55 46         96 foreach my $name (@$l) {
56 621         917 my $j = $i++;
57 23     23   163 no strict 'refs';
  23         52  
  23         2206  
58 621         6297 *{__PACKAGE__ . '::' . $name} = sub () { $j }
  0         0  
59 621         2294 }
60             }
61             }
62              
63             sub _internal_syms {
64 23     23   71 my $pkg = caller;
65 23     23   152 no strict 'refs';
  23         47  
  23         84552  
66 23         81 for my $sub (@opParts,@opName,'dump_op') {
67 644         867 *{$pkg . '::' . $sub} = \&{__PACKAGE__ . '::' . $sub};
  644         123906  
  644         1330  
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 516 my $pkg = shift;
102 31         96 my $self = bless {}, $pkg;
103              
104 31         136 $self->configure(@_);
105 31         176 $self;
106             }
107              
108              
109             sub configure {
110 38     38 1 235 my $self = shift;
111 38         103 my %opt = @_;
112              
113 38   100     387 $self->{options}{encoding} = uc($opt{encoding} || 'BER');
114              
115 38 50       299 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     264 $self->{options}{tagdefault} = uc($opt{tagdefault} || 'IMPLICIT');
122              
123 38 50       220 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         113 for my $type (qw(encode decode)) {
130 76 100       226 if (exists $opt{$type}) {
131 7         13 while(my($what,$value) = each %{$opt{$type}}) {
  14         53  
132 7         28 $self->{options}{"${type}_${what}"} = $value;
133             }
134             }
135             }
136             }
137              
138              
139              
140             sub find {
141 16     16 1 41 my $self = shift;
142 16         79 my $what = shift;
143 16 50       110 return unless exists $self->{tree}{$what};
144 16         88 my %new = %$self;
145 16         49 $new{script} = $new{tree}->{$what};
146 16         113 bless \%new, ref($self);
147             }
148              
149              
150             sub prepare {
151 98     98 1 1350 my $self = shift;
152 98         162 my $asn = shift;
153              
154 98 50       268 $self = $self->new unless ref($self);
155 98         149 my $tree;
156 98 50       247 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         342 $tree = Convert::ASN1::parser::parse($asn,$self->{options}{tagdefault});
162             }
163              
164 98 50       264 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         247 $self->{tree} = _pack_struct($tree);
173 98         382 $self->{script} = (values %$tree)[0];
174 98         546 $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 5 my $self = shift;
191 2         3 my $oid = shift;
192 2         3 my $handler = shift;
193              
194 2         5 $self->{options}{oidtable}{$oid}=$handler;
195 2         21 $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   231 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 506 my $self = shift;
218 106 100       425 my $stash = @_ == 1 ? shift : { @_ };
219 106         200 my $buf = '';
220 106         343 local $SIG{__DIE__};
221 106         390 eval { _encode($self->{options}, $self->{script}, $stash, [], $buf) }
222 106 100       197 or do { $self->{error} = $@; undef }
  3         207  
  3         20  
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 3090 $_[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 441 if($_[0] >> 7) {
248 6         11 my $lenlen = &num_length;
249              
250 6         37 return pack("Ca*", $lenlen | 0x80, substr(pack("N",$_[0]), -$lenlen));
251             }
252              
253 192         707 return pack("C", $_[0]);
254             }
255              
256              
257             ##
258             ## Decoding
259             ##
260              
261             sub decode {
262 132     132 1 9500 my $self = shift;
263 132         189 my $ret;
264              
265 132         475 local $SIG{__DIE__};
266             eval {
267 132         233 my (%stash, $result);
268 132         348 my $script = $self->{script};
269 132         238 my $stash = \$result;
270              
271 132         375 while ($script) {
272 169 50       424 my $child = $script->[0] or last;
273 169 100 100     767 if (@$script > 1 or defined $child->[cVAR]) {
274 112         233 $result = $stash = \%stash;
275 112         198 last;
276             }
277 57 100 100     331 last if $child->[cTYPE] == opCHOICE or $child->[cLOOP];
278 44         121 $script = $child->[cCHILD];
279             }
280              
281             _decode(
282             $self->{options},
283             $self->{script},
284 132         741 $stash,
285             0,
286             length $_[0],
287             undef,
288             {},
289             $_[0]);
290              
291 129         267 $ret = $result;
292 129         388 1;
293 132 100 50     249 } or $self->{'error'} = $@ || 'Unknown error';
294              
295 132         787 $ret;
296             }
297              
298              
299             sub asn_decode_length {
300 9 50   9 1 51 return unless length $_[0];
301              
302 9         20 my $len = unpack("C",$_[0]);
303              
304 9 100       37 if($len & 0x80) {
305 4 50       10 $len &= 0x7f or return (1,-1);
306              
307 4 50       7 return if $len >= length $_[0];
308              
309 4         23 return (1+$len, unpack("N", "\0" x (4 - $len) . substr($_[0],1,$len)));
310             }
311 5         20 return (1, $len);
312             }
313              
314              
315             sub asn_decode_tag {
316 11 100   11 1 31 return unless length $_[0];
317              
318 9         22 my $tag = unpack("C", $_[0]);
319 9         13 my $n = 1;
320              
321 9 100       23 if(($tag & 0x1f) == 0x1f) {
322 4         5 my $b;
323 4         5 do {
324 6 50       13 return if $n >= length $_[0];
325 6         12 $b = unpack("C",substr($_[0],$n,1));
326 6         18 $tag |= $b << (8 * $n++);
327             } while($b & 0x80);
328             }
329 9         32 ($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 226 $_[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 35 my($num, $biclass) = @_;
373 12     1   986 eval "use $biclass";
  1     1   9  
  1     1   2  
  1     1   5  
  1     1   10  
  1     1   3  
  1     1   6  
  1     1   7  
  1     1   3  
  1     1   58  
  1     1   7  
  1     1   3  
  1         5  
  1         8  
  1         2  
  1         4  
  1         8  
  1         2  
  1         6  
  1         8  
  1         3  
  1         6  
  1         7  
  1         3  
  1         15  
  1         8  
  1         2  
  1         5  
  1         9  
  1         3  
  1         4  
  1         8  
  1         3  
  1         17  
  1         7  
  1         3  
  1         4  
374 12         3998 $num = $biclass->new($num);
375 12 100       1100 my $neg = $num < 0
376             and $num = abs($num+1);
377 12         3409 my $base = $biclass->new(256);
378 12         437 my $result = '';
379 12         32 while($num != 0) {
380 101         17956 my $r = $num % $base;
381 101         10312 $num = ($num-$r) / $base;
382 101         26283 $result .= pack("C",$r);
383             }
384 12 100       2165 $result ^= pack("C",255) x length($result) if $neg;
385 12         67 return scalar reverse $result;
386             }
387              
388             # Convert from an octet string to a bigint
389              
390             sub os2ip {
391 16     16 0 49 my($os, $biclass) = @_;
392 16         1551 eval "require $biclass";
393 16         50117 my $base = $biclass->new(256);
394 16         46962 my $result = $biclass->new(0);
395 16 100       1671 my $neg = unpack("C",$os) >= 0x80
396             and $os ^= pack("C",255) x length($os);
397 16         71 for (unpack("C*",$os)) {
398 171         35557 $result = ($result * $base) + $_;
399             }
400 16 100       3728 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 222 my($class,$value) = @_;
412              
413 96 50       402 die sprintf "Bad tag class 0x%x",$class
414             if $class & ~0xe0;
415              
416 96 100 66     377 unless ($value & ~0x1f or $value == 0x1f) {
417 90         254 return (($class & 0xe0) | $value);
418             }
419              
420 6 50       12 die sprintf "Tag value 0x%08x too big\n",$value
421             if $value & 0xffe00000;
422              
423 6         10 $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         31 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   243 unless (defined &_encode) {
435 23         11010 require Convert::ASN1::_decode;
436 23         10166 require Convert::ASN1::_encode;
437 23         9569 require Convert::ASN1::IO;
438             }
439              
440 23         11300 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 71 sub error { $_[0]->{error} }
460             1;