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.32'; # TRIAL
7 23     23   13742 use 5.004;
  23         206  
8 23     23   115 use strict;
  23         53  
  23         818  
9 23     23   157 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS @opParts @opName $AUTOLOAD);
  23         72  
  23         2278  
10 23     23   175 use Exporter;
  23         61  
  23         1269  
11              
12 23     23   154 use constant CHECK_UTF8 => $] > 5.007;
  23         60  
  23         7109  
13              
14             BEGIN {
15 23     23   131 local $SIG{__DIE__};
16 23 50       44 eval { require bytes and 'bytes'->import };
  23         13678  
17              
18 23         500 if (CHECK_UTF8) {
19 23         10857 require Encode;
20 23         224418 require utf8;
21             }
22              
23 23         681 @ISA = qw(Exporter);
24              
25 23         479 %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         328  
41 23         86 $EXPORT_TAGS{all} = \@EXPORT_OK;
42              
43 23         83 @opParts = qw(
44             cTAG cTYPE cVAR cLOOP cOPT cEXT cCHILD cDEFINE
45             );
46              
47 23         96 @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         68 foreach my $l (\@opParts, \@opName) {
54 46         72 my $i = 0;
55 46         83 foreach my $name (@$l) {
56 621         876 my $j = $i++;
57 23     23   170 no strict 'refs';
  23         54  
  23         2225  
58 621         6268 *{__PACKAGE__ . '::' . $name} = sub () { $j }
  0         0  
59 621         2179 }
60             }
61             }
62              
63             sub _internal_syms {
64 23     23   73 my $pkg = caller;
65 23     23   155 no strict 'refs';
  23         45  
  23         80497  
66 23         85 for my $sub (@opParts,@opName,'dump_op') {
67 644         801 *{$pkg . '::' . $sub} = \&{__PACKAGE__ . '::' . $sub};
  644         116388  
  644         1309  
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 469 my $pkg = shift;
102 31         97 my $self = bless {}, $pkg;
103              
104 31         146 $self->configure(@_);
105 31         158 $self;
106             }
107              
108              
109             sub configure {
110 38     38 1 227 my $self = shift;
111 38         99 my %opt = @_;
112              
113 38   100     393 $self->{options}{encoding} = uc($opt{encoding} || 'BER');
114              
115 38 50       276 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       229 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       222 if (exists $opt{$type}) {
131 7         9 while(my($what,$value) = each %{$opt{$type}}) {
  14         61  
132 7         29 $self->{options}{"${type}_${what}"} = $value;
133             }
134             }
135             }
136             }
137              
138              
139              
140             sub find {
141 16     16 1 38 my $self = shift;
142 16         67 my $what = shift;
143 16 50       93 return unless exists $self->{tree}{$what};
144 16         75 my %new = %$self;
145 16         53 $new{script} = $new{tree}->{$what};
146 16         88 bless \%new, ref($self);
147             }
148              
149              
150             sub prepare {
151 98     98 1 1369 my $self = shift;
152 98         169 my $asn = shift;
153              
154 98 50       259 $self = $self->new unless ref($self);
155 98         149 my $tree;
156 98 50       252 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         356 $tree = Convert::ASN1::parser::parse($asn,$self->{options}{tagdefault});
162             }
163              
164 98 50       265 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         246 $self->{tree} = _pack_struct($tree);
173 98         337 $self->{script} = (values %$tree)[0];
174 98         620 $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 4 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         7 $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   237 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 495 my $self = shift;
218 106 100       382 my $stash = @_ == 1 ? shift : { @_ };
219 106         201 my $buf = '';
220 106         409 local $SIG{__DIE__};
221 106         435 eval { _encode($self->{options}, $self->{script}, $stash, [], $buf) }
222 106 100       186 or do { $self->{error} = $@; undef }
  3         201  
  3         19  
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 3008 $_[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 449 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         715 return pack("C", $_[0]);
254             }
255              
256              
257             ##
258             ## Decoding
259             ##
260              
261             sub decode {
262 132     132 1 7489 my $self = shift;
263 132         195 my $ret;
264              
265 132         429 local $SIG{__DIE__};
266             eval {
267 132         215 my (%stash, $result);
268 132         378 my $script = $self->{script};
269 132         245 my $stash = \$result;
270              
271 132         353 while ($script) {
272 169 50       426 my $child = $script->[0] or last;
273 169 100 100     777 if (@$script > 1 or defined $child->[cVAR]) {
274 112         225 $result = $stash = \%stash;
275 112         200 last;
276             }
277 57 100 100     295 last if $child->[cTYPE] == opCHOICE or $child->[cLOOP];
278 44         104 $script = $child->[cCHILD];
279             }
280              
281             _decode(
282             $self->{options},
283             $self->{script},
284 132         769 $stash,
285             0,
286             length $_[0],
287             undef,
288             {},
289             $_[0]);
290              
291 129         253 $ret = $result;
292 129         402 1;
293 132 100 50     246 } or $self->{'error'} = $@ || 'Unknown error';
294              
295 132         789 $ret;
296             }
297              
298              
299             sub asn_decode_length {
300 9 50   9 1 52 return unless length $_[0];
301              
302 9         19 my $len = unpack("C",$_[0]);
303              
304 9 100       34 if($len & 0x80) {
305 4 50       9 $len &= 0x7f or return (1,-1);
306              
307 4 50       8 return if $len >= length $_[0];
308              
309 4         22 return (1+$len, unpack("N", "\0" x (4 - $len) . substr($_[0],1,$len)));
310             }
311 5         22 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         13 my $n = 1;
320              
321 9 100       23 if(($tag & 0x1f) == 0x1f) {
322 4         6 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         33 ($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 205 $_[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 32 my($num, $biclass) = @_;
373 12     1   1498 eval "use $biclass";
  1     1   9  
  1     1   2  
  1     1   6  
  1     1   13  
  1     1   4  
  1     1   6  
  1     1   8  
  1     1   2  
  1     1   62  
  1     1   9  
  1     1   2  
  1         6  
  1         9  
  1         2  
  1         6  
  1         9  
  1         2  
  1         7  
  1         11  
  1         2  
  1         6  
  1         11  
  1         3  
  1         4  
  1         10  
  1         2  
  1         8  
  1         10  
  1         2  
  1         7  
  1         11  
  1         3  
  1         7  
  1         9  
  1         3  
  1         5  
374 12         4540 $num = $biclass->new($num);
375 12 100       1278 my $neg = $num < 0
376             and $num = abs($num+1);
377 12         3755 my $base = $biclass->new(256);
378 12         472 my $result = '';
379 12         44 while($num != 0) {
380 101         17875 my $r = $num % $base;
381 101         10337 $num = ($num-$r) / $base;
382 101         26570 $result .= pack("C",$r);
383             }
384 12 100       2151 $result ^= pack("C",255) x length($result) if $neg;
385 12         91 return scalar reverse $result;
386             }
387              
388             # Convert from an octet string to a bigint
389              
390             sub os2ip {
391 16     16 0 48 my($os, $biclass) = @_;
392 16         1310 eval "require $biclass";
393 16         41723 my $base = $biclass->new(256);
394 16         39568 my $result = $biclass->new(0);
395 16 100       1491 my $neg = unpack("C",$os) >= 0x80
396             and $os ^= pack("C",255) x length($os);
397 16         64 for (unpack("C*",$os)) {
398 171         32922 $result = ($result * $base) + $_;
399             }
400 16 100       3597 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 190 my($class,$value) = @_;
412              
413 96 50       200 die sprintf "Bad tag class 0x%x",$class
414             if $class & ~0xe0;
415              
416 96 100 66     314 unless ($value & ~0x1f or $value == 0x1f) {
417 90         226 return (($class & 0xe0) | $value);
418             }
419              
420 6 50       13 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         13 my @t = ($value & 0x7f);
426 6         16 unshift @t, (0x80 | ($value & 0x7f)) while $value >>= 7;
427 6         33 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   260 unless (defined &_encode) {
435 23         11683 require Convert::ASN1::_decode;
436 23         10546 require Convert::ASN1::_encode;
437 23         9666 require Convert::ASN1::IO;
438             }
439              
440 23         11377 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 46 sub error { $_[0]->{error} }
460             1;