File Coverage

blib/lib/Convert/BER.pm
Criterion Covered Total %
statement 443 888 49.8
branch 131 382 34.2
condition 18 72 25.0
subroutine 51 81 62.9
pod 14 24 58.3
total 657 1447 45.4


line stmt bran cond sub pod time code
1             # Convert::BER.pm
2             #
3             # Copyright (c) 1995-1999 Graham Barr . All rights reserved.
4             # This program is free software; you can redistribute it and/or
5             # modify it under the same terms as Perl itself.
6              
7             package Convert::BER;
8              
9 10     10   17771 use vars qw($VERSION @ISA);
  10         20  
  10         893  
10 10     10   57 use Exporter ();
  10         17  
  10         451  
11 10     10   52 use strict;
  10         17  
  10         354  
12 10     10   49 use vars qw($VERSION @ISA @EXPORT_OK);
  10         26  
  10         2593  
13              
14             BEGIN {
15 10 50   10   247 if ($] >= 5.006) {
16 10         14496 require bytes; 'bytes'->import;
  10         163  
17             }
18              
19 10         45 $VERSION = "1.32";
20              
21 10         188 @ISA = qw(Exporter);
22            
23 10         64 @EXPORT_OK = qw(
24             BER_BOOLEAN
25             BER_INTEGER
26             BER_BIT_STR
27             BER_OCTET_STR
28             BER_NULL
29             BER_OBJECT_ID
30             BER_REAL
31             BER_SEQUENCE
32             BER_SET
33              
34             BER_UNIVERSAL
35             BER_APPLICATION
36             BER_CONTEXT
37             BER_PRIVATE
38              
39             BER_PRIMITIVE
40             BER_CONSTRUCTOR
41              
42             BER_LONG_LEN
43             BER_EXTENSION_ID
44             BER_BIT
45              
46             ber_tag
47             );
48              
49             # 5.003 does not have UNIVERSAL::can
50 10 50       9703 unless(defined &UNIVERSAL::can) {
51             *UNIVERSAL::can = sub {
52 0         0 my($obj,$meth) = @_;
53 0   0     0 my $pkg = ref($obj) || $obj;
54 0         0 my @pkg = ($pkg);
55 0         0 my %done;
56 0         0 while(@pkg) {
57 0         0 $pkg = shift @pkg;
58 0 0       0 next if exists $done{$pkg};
59 0         0 $done{$pkg} = 1;
60              
61 10     10   55 no strict 'refs';
  10         15  
  10         1598  
62              
63 0         0 unshift @pkg,@{$pkg . "::ISA"}
  0         0  
64 0 0       0 if(@{$pkg . "::ISA"});
65 0         0 return \&{$pkg . "::" . $meth}
  0         0  
66 0 0       0 if defined(&{$pkg . "::" . $meth});
67             }
68 0         0 undef;
69             }
70 0         0 }
71             }
72              
73             ##
74             ## Constants
75             ##
76              
77             sub BER_BOOLEAN () { 0x01 }
78             sub BER_INTEGER () { 0x02 }
79             sub BER_BIT_STR () { 0x03 }
80             sub BER_OCTET_STR () { 0x04 }
81             sub BER_NULL () { 0x05 }
82             sub BER_OBJECT_ID () { 0x06 }
83             sub BER_REAL () { 0x09 }
84             sub BER_ENUMERATED () { 0x0A }
85             sub BER_SEQUENCE () { 0x10 }
86             sub BER_SET () { 0x11 }
87             sub BER_PRINT_STR () { 0x13 }
88             sub BER_IA5_STR () { 0x16 }
89             sub BER_UTC_TIME () { 0x17 }
90             sub BER_GENERAL_TIME () { 0x18 }
91              
92             sub BER_UNIVERSAL () { 0x00 }
93             sub BER_APPLICATION () { 0x40 }
94             sub BER_CONTEXT () { 0x80 }
95             sub BER_PRIVATE () { 0xC0 }
96              
97             sub BER_PRIMITIVE () { 0x00 }
98             sub BER_CONSTRUCTOR () { 0x20 }
99              
100             sub BER_LONG_LEN () { 0x80 }
101             sub BER_EXTENSION_ID () { 0x1F }
102             sub BER_BIT () { 0x80 }
103              
104             # This module is used a lot so performance matters. For that reason it
105             # is implemented as an ARRAY instead of a HASH.
106             # inlined constants for array indices
107              
108             sub _BUFFER () { 0 }
109             sub _POS () { 1 }
110             sub _INDEX () { 2 }
111             sub _ERROR () { 3 }
112             sub _PEER () { 4 }
113              
114             sub _PACKAGE () { 0 }
115             sub _TAG () { 1 }
116             sub _PACK () { 2 }
117             sub _PACK_ARRAY () { 3 }
118             sub _UNPACK () { 4 }
119             sub _UNPACK_ARRAY () { 5 }
120              
121             {
122             Convert::BER->define(
123             ##
124             ## Syntax operator
125             ##
126              
127             [ BER => undef, undef ],
128             [ ANY => undef, undef ],
129             [ CONSTRUCTED => undef, undef ],
130             [ OPTIONAL => undef, undef ],
131             [ CHOICE => undef, undef ],
132              
133             ##
134             ## Primitive operators
135             ##
136              
137             [ BOOLEAN => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_BOOLEAN ],
138             [ INTEGER => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_INTEGER ],
139             [ STRING => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_OCTET_STR ],
140             [ NULL => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_NULL ],
141             [ OBJECT_ID => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_OBJECT_ID ],
142             [ BIT_STRING => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_BIT_STR ],
143             [ BIT_STRING8 => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_BIT_STR ],
144             [ REAL => undef, BER_UNIVERSAL | BER_PRIMITIVE | BER_REAL ],
145              
146             [ SEQUENCE => undef, BER_UNIVERSAL | BER_CONSTRUCTOR | BER_SEQUENCE ],
147             [ SEQUENCE_OF => undef, BER_UNIVERSAL | BER_CONSTRUCTOR | BER_SEQUENCE ],
148             );
149              
150             ##
151             ## These variables will be defined by the above ->define() call
152             ##
153              
154 10     10   81 use vars qw($INTEGER $SEQUENCE $STRING $SEQUENCE_OF);
  10         20  
  10         4039  
155              
156             Convert::BER->define(
157             ##
158             ## Sub-classed primitive operators
159             ##
160              
161             [ ENUM => $INTEGER, BER_UNIVERSAL | BER_PRIMITIVE | BER_ENUMERATED ],
162             [ SET => $SEQUENCE, BER_UNIVERSAL | BER_CONSTRUCTOR | BER_SET ],
163             [ SET_OF => $SEQUENCE_OF, BER_UNIVERSAL | BER_CONSTRUCTOR | BER_SET ],
164              
165             [ ObjectDescriptor => $STRING, BER_UNIVERSAL | 7],
166             [ UTF8String => $STRING, BER_UNIVERSAL | 12],
167             [ NumericString => $STRING, BER_UNIVERSAL | 18],
168             [ PrintableString => $STRING, BER_UNIVERSAL | 19],
169             [ TeletexString => $STRING, BER_UNIVERSAL | 20],
170             [ T61String => $STRING, BER_UNIVERSAL | 20],
171             [ VideotexString => $STRING, BER_UNIVERSAL | 21],
172             [ IA5String => $STRING, BER_UNIVERSAL | 22],
173             [ GraphicString => $STRING, BER_UNIVERSAL | 25],
174             [ VisibleString => $STRING, BER_UNIVERSAL | 26],
175             [ ISO646String => $STRING, BER_UNIVERSAL | 26],
176             [ GeneralString => $STRING, BER_UNIVERSAL | 27],
177             [ UTCTime => $STRING, BER_UNIVERSAL | 23],
178             [ GeneralizedTime => $STRING, BER_UNIVERSAL | 24],
179             );
180              
181             Convert::BER->define(
182             [ '_Time_generic' => $STRING, undef ],
183             [ TimeUZ => '_Time_generic', BER_UNIVERSAL | 23],
184             [ TimeUL => '_Time_generic', BER_UNIVERSAL | 23],
185              
186             [ TimeGZ => '_Time_generic', BER_UNIVERSAL | 24],
187             [ TimeGL => '_Time_generic', BER_UNIVERSAL | 24],
188             );
189             }
190              
191             # only load Carp when needed
192              
193             sub croak {
194 0     0 0 0 require Carp;
195 0         0 goto &Carp::croak;
196             }
197              
198             ##
199             ## define:
200             ## does all the hard work of dynamically building the BER class
201             ## and BER-type classes
202             ##
203              
204             sub define {
205 32     32 0 104 my $pkg = shift;
206              
207 10     10   78 no strict 'refs'; # we do some naughty stuff here :-)
  10         25  
  10         5836  
208              
209 32   33     278 $pkg = ref($pkg) || $pkg;
210              
211 32         112 while(@_) {
212 376         637 my($name,$isa,$tag) = @{ $_[0] }; shift;
  376         1022  
  376         919  
213 376         1043 my $subpkg = $pkg . "::" . $name;
214              
215 376 50       1255 croak("Bad tag name '$name'")
216             if($name =~ /\A(?:DESTROY|VERSION)\Z/);
217              
218 376 100       1361 if(defined $isa) {
219 226 50       1760 my $isapkg = $pkg->can('_' . $isa) or
220             croak "Unknown BER tag type '$isa'";
221              
222 226         3969 @{$subpkg . "::ISA"} = ( &{$isapkg}()->[ _PACKAGE ] )
  226         548  
  226         2558  
223 226 50       335 unless @{$subpkg . "::ISA"};
224              
225 226 100       3096 $tag = $subpkg->tag
226             unless defined $tag;
227             }
228              
229 376 50       416 if(defined &{$subpkg . "::tag"}) {
  376         2686  
230 0 0       0 croak "tags for '$name' do not match "
231             unless $subpkg->tag == $tag;
232             }
233             else {
234 376     387   1660 *{$subpkg . "::tag"} = sub { $tag };
  376         1974  
  387         785  
235             }
236              
237 376         727 push(@{$pkg . "::EXPORT_OK"}, '$' . $name, $name);
  376         1731  
238              
239 376         621 *{$pkg . "::" . $name} = \$name;
  376         3082  
240              
241 1504         17469 my @data = ( $subpkg, $subpkg->tag,
242 376         2136 map { $subpkg->can($_) }
243             qw(pack pack_array unpack unpack_array)
244             );
245              
246             {
247 376         635 my $const = $tag;
  376         961  
248 376     0   1641 *{$pkg . "::" . $name} = sub () { $const }
  0         0  
249 376 50       580 unless defined &{$pkg . "::" . $name};
  376         3968  
250             }
251              
252 376     557   1344 *{$pkg . "::_" . $name} = sub { \@data };
  376         3972  
  557         1293  
253             }
254             }
255              
256             # Now we have done the naughty stuff, make sure we do no more
257 10     10   62 use strict;
  10         23  
  10         119764  
258              
259             sub ber_tag {
260 44     44 0 173 my($t,$e) = @_;
261 44   100     97 $e ||= 0; # unsigned;
262              
263 44 100       95 if($e < 30) {
264 9         40 return (($t & 0xe0) | $e);
265             }
266              
267 35         44 $t = ($t | 0x1f) & 0xff;
268 35 50       66 if ($e & 0xffe00000) {
269 0         0 die "Too big";
270             }
271 35         46 my @t = ();
272              
273 35 50       105 push(@t, ($b >> 14) | 0x80)
274             if ($b = ($e & 0x001fc000));
275              
276 35 100       76 push(@t, ($b >> 7) | 0x80)
277             if ($b = ($e & 0xffffff80));
278              
279 35         211 unpack("V",pack("C4",$t,@t,$e & 0x7f,0,0));
280             }
281              
282             sub new {
283 155     155 1 27357 my $package = shift;
284 155   66     649 my $class = ref($package) || $package;
285              
286 155 100       1827 my $self = bless [
    100          
287             @_ == 1 ? shift : "",
288             0,
289             ref($package) ? $package->[ Convert::BER::_INDEX() ] : [],
290             ], $class;
291              
292 155 50       630 @_ ? $self->encode(@_) : $self;
293             }
294              
295             ##
296             ## Some basic subs for packing/unpacking data
297             ## These methods would be called by the BER-type classes
298             ##
299              
300             sub num_length {
301 51 100   51 0 171 return 1 if ( ($_[0] & 0xff) == $_[0]);
302 5 100       16 return 2 if ( ($_[0] & 0xffff) == $_[0]);
303 4 50       15 return 3 if ( ($_[0] & 0xffffff) == $_[0]);
304 0         0 return 4;
305             }
306              
307             sub pos {
308 0     0 1 0 my $ber = shift;
309 0 0       0 @_ ? ($ber->[ Convert::BER::_POS() ] = shift)
310             : $ber->[ Convert::BER::_POS() ];
311             }
312              
313             sub pack {
314 0     0 0 0 my $ber = shift;
315 0         0 $ber->[ Convert::BER::_BUFFER() ] .= $_[0];
316 0         0 1;
317             }
318              
319             sub unpack {
320 165     165 0 225 my($ber,$len) = @_;
321 165         240 my $pos = $ber->[ Convert::BER::_POS() ];
322 165         206 my $npos = $pos + $len;
323              
324 165 50       347 die "Buffer empty"
325             if ($npos > CORE::length($ber->[ Convert::BER::_BUFFER() ]));
326              
327 165         198 $ber->[ Convert::BER::_POS() ] = $npos;
328              
329 165         1324 substr($ber->[ Convert::BER::_BUFFER() ],$pos,$len);
330             }
331              
332             sub pack_tag {
333 178     178 0 250 my($ber,$tag) = @_;
334            
335             # small tag number are more common, so check $tag size in reverse order
336 178 100       501 unless(($tag & 0x1f) == 0x1f) {
337 142         310 $ber->[ Convert::BER::_BUFFER() ] .= chr( $tag );
338 142         231 return 1;
339             }
340              
341 36 100       87 unless($tag & ~0x7fff) {
342 16         48 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("v",$tag);
343 16         32 return 2;
344             }
345              
346 20 50       44 unless($tag & ~0x7fffff) {
347 20         67 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("vc",$tag, ($tag >> 16));
348 20         36 return 3;
349             }
350              
351 0         0 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("V",$tag);
352 0         0 return 4;
353             }
354              
355             sub unpack_tag {
356 199     199 0 274 my($ber,$expect) = @_;
357 199         571 my $pos = $ber->[ Convert::BER::_POS() ];
358 199         266 my $len = CORE::length($ber->[ Convert::BER::_BUFFER() ]);
359              
360 199 50       469 die "Buffer empty"
361             if($pos >= $len);
362              
363 199         620 my $tag = CORE::unpack("C",substr($ber->[ Convert::BER::_BUFFER() ],$pos++,1
364             ));
365              
366 199 100       470 if(($tag & 0x1f) == 0x1f) {
367 65         67 my $b;
368 65         72 my $s = 8;
369              
370 65         79 do {
371 100 50       182 die "Buffer empty"
372             if($pos >= $len);
373 100         203 $b = CORE::unpack("C",substr($ber->[ Convert::BER::_BUFFER() ],$pos++,1));
374 100         289 $tag |= $b << $s;
375 100         362 $s += 8;
376             } while($b & 0x80);
377             }
378              
379 199 50 66     842 die sprintf("Expecting tag 0x%x, found 0x%x",$expect,$tag)
380             if(defined($expect) && ($tag != $expect));
381              
382 199         268 $ber->[ Convert::BER::_POS() ] = $pos;
383              
384 199         471 $tag
385             }
386              
387             sub pack_length {
388 178     178 0 241 my($ber,$len) = @_;
389              
390 178 50       386 if($len & ~0x7f) {
391 0         0 my $lenlen = num_length($len);
392              
393 0         0 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("C", $lenlen | 0x80) . substr(CORE::pack("N",$len), 0 - $lenlen);
394              
395 0         0 return $lenlen + 1;
396             }
397              
398 178         12699 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("C", $len);
399 178         267 return 1;
400             }
401              
402              
403              
404             sub unpack_length {
405 169     169 0 678 my $ber = shift;
406 169         257 my $pos = $ber->[ Convert::BER::_POS() ];
407              
408 169 50       379 die "Buffer empty"
409             if($pos >= CORE::length($ber->[ Convert::BER::_BUFFER() ]));
410              
411 169         534 my $len = CORE::unpack("C", substr($ber->[ Convert::BER::_BUFFER() ],$pos++,1));
412              
413 169 50       632 if($len & 0x80) {
414 0         0 my $buf;
415              
416 0         0 $len &= 0x7f;
417              
418 0 0       0 die "Buffer empty"
419             if(($pos+$len) > CORE::length($ber->[ Convert::BER::_BUFFER() ]));
420              
421 0         0 my $tmp = "\0" x (4 - $len) . substr($ber->[ Convert::BER::_BUFFER() ],$pos,$len);
422              
423 0         0 $pos += $len;
424              
425 0 0       0 $len = $len ? CORE::unpack("N",$tmp) : -1;
426             }
427              
428 169         221 $ber->[ Convert::BER::_POS() ] = $pos;
429              
430 169         339 $len;
431             }
432              
433             ##
434             ## User interface (public) method
435             ##
436              
437             sub error {
438 0     0 1 0 my $ber = shift;
439 0         0 $ber->[ Convert::BER::_ERROR() ];
440             }
441              
442              
443             sub tag {
444 30     30 1 564 my $ber = shift;
445 30         48 my $pos = $ber->[ Convert::BER::_POS() ];
446 30 50       38 my $tag = eval {
447 30         670 local($SIG{'__DIE__'});
448 30         63 unpack_tag($ber)
449             } or return undef;
450 30         45 $ber->[ Convert::BER::_POS() ] = $pos;
451 30         65 $tag;
452             }
453              
454             sub length {
455 0     0 1 0 my $ber = shift;
456              
457 0         0 CORE::length($ber->[ Convert::BER::_BUFFER() ]);
458             }
459              
460             sub buffer {
461 75     75 1 894 my $ber = shift;
462 75 50       189 if(@_) {
463 0         0 $ber->[ Convert::BER::_POS() ] = 0;
464 0         0 $ber->[ Convert::BER::_BUFFER() ] = "" . shift;
465             }
466 75         324 $ber->[ Convert::BER::_BUFFER() ];
467             }
468              
469             ##
470             ## just for debug :-)
471             ##
472              
473             sub _hexdump {
474 0     0   0 my($fmt,$pos) = @_[1,2]; # Don't copy buffer
475              
476 0   0     0 $pos ||= 0;
477              
478 0         0 my $offset = 0;
479 0         0 my $cnt = 1 << 4;
480 0         0 my $len = CORE::length($_[0]);
481 0         0 my $linefmt = ("%02X " x $cnt) . "%s\n";
482              
483 0         0 print "\n";
484              
485 0         0 while ($offset < $len) {
486 0         0 my $data = substr($_[0],$offset,$cnt);
487 0         0 my @y = CORE::unpack("C*",$data);
488              
489 0 0       0 printf $fmt,$pos if $fmt;
490              
491             # On the last time through replace '%02X ' with '__ ' for the
492             # missing values
493 0 0       0 substr($linefmt, 5*@y,5*($cnt-@y)) = "__ " x ($cnt - @y)
494             if @y != $cnt;
495              
496             # Change non-printable chars to '.'
497 0         0 $data =~ s/[\x00-\x1f\x7f-\xff]/./sg;
498 0         0 printf $linefmt, @y,$data;
499              
500 0         0 $offset += $cnt;
501 0         0 $pos += $cnt;
502             }
503             }
504              
505             my %type = (
506             split(/[\t\n]\s*/,
507             q(10 SEQUENCE
508             01 BOOLEAN
509             0A ENUM
510             11 SET
511             02 INTEGER
512             03 BIT STRING
513             C0 PRIVATE [%d]
514             04 STRING
515             40 APPLICATION [%d]
516             05 NULL
517             06 OBJECT ID
518             80 CONTEXT [%d]
519             )
520             )
521             );
522              
523             sub dump {
524 0     0 1 0 my $ber = shift;
525 0 0       0 my $fh = @_ ? shift : \*STDERR;
526              
527 0         0 my $ofh = select($fh);
528              
529 0         0 my $pos = 0;
530 0         0 my $indent = "";
531 0         0 my @seqend = ();
532 0         0 my $length = CORE::length($ber->[ Convert::BER::_BUFFER() ]);
533 0 0       0 my $fmt = $length > 0xffff ? "%08X" : "%04X";
534              
535 0         0 local $ber->[ Convert::BER::_POS() ];
536              
537 0         0 $ber->[ Convert::BER::_POS() ] = 0;
538              
539 0         0 while(1) {
540 0   0     0 while (@seqend && $ber->[ Convert::BER::_POS() ] >= $seqend[0]) {
541 0         0 $indent = substr($indent,2);
542 0         0 shift @seqend;
543 0         0 printf "$fmt : %s}\n",$ber->[ Convert::BER::_POS() ],$indent;
544             }
545 0 0       0 last unless $ber->[ Convert::BER::_POS() ] < $length;
546            
547 0         0 my $start = $ber->[ Convert::BER::_POS() ];
548 0         0 my $tag = unpack_tag($ber);
549 0         0 my $pos = $ber->[ Convert::BER::_POS() ];
550 0         0 my $len = Convert::BER::unpack_length($ber);
551              
552 0 0 0     0 if($tag == 0 && $len == 0) {
553 0         0 $seqend[0] = 0;
554 0         0 redo;
555             }
556 0         0 printf $fmt. " %02X %4d: %s",$start,$tag,$len,$indent;
557              
558 0   0     0 my $label = $type{sprintf("%02X",$tag & ~0x20)}
559             || $type{sprintf("%02X",$tag & 0xC0)}
560             || "UNIVERSAL [%d]";
561              
562 0 0       0 if (($tag & 0x1f) == 0x1f) {
563 0         0 my $k = $tag >> 8;
564 0         0 my $j = 0;
565 0         0 while($k) {
566 0         0 $j = ($j << 7) | ($k & 0x7f);
567 0         0 $k >>= 8;
568             }
569 0         0 my $l = $label;
570 0         0 $l =~ s/%d/0x%x/;
571 0         0 printf $l, $j;
572             }
573             else {
574 0         0 printf $label, $tag & ~0xE0;
575             }
576            
577 0 0       0 if ($tag & BER_CONSTRUCTOR) {
578 0         0 print " {\n";
579 0 0       0 if($len < 0) {
580 0         0 unshift(@seqend, ~(1<<31));
581             }
582             else {
583 0         0 unshift(@seqend, $ber->[ Convert::BER::_POS() ] + $len);
584             }
585 0         0 $indent .= " ";
586 0         0 next;
587             }
588              
589 0         0 $ber->[ Convert::BER::_POS() ] = $pos;
590 0         0 my $tmp;
591              
592 0         0 for ($label) { # switch
593 0 0       0 /^INTEGER/ && do {
594 0         0 Convert::BER::INTEGER->unpack($ber,\$tmp);
595 0         0 printf " = %d\n",$tmp;
596 0         0 last;
597             };
598              
599 0 0       0 /^ENUM/ && do {
600 0         0 Convert::BER::ENUM->unpack($ber,\$tmp);
601 0         0 printf " = %d\n",$tmp;
602 0         0 last;
603             };
604              
605 0 0       0 /^BOOLEAN/ && do {
606 0         0 Convert::BER::BOOLEAN->unpack($ber,\$tmp);
607 0 0       0 printf " = %s\n",$tmp ? 'TRUE' : 'FALSE';
608 0         0 last;
609             };
610              
611 0 0       0 /^OBJECT ID/ && do {
612 0         0 Convert::BER::OBJECT_ID->unpack($ber,\$tmp);
613 0         0 printf " = %s\n",$tmp;
614 0         0 last;
615             };
616              
617 0 0       0 /^NULL/ && do {
618 0         0 $ber->[ Convert::BER::_POS() ] = $pos+1;
619 0         0 print "\n";
620 0         0 last;
621             };
622              
623 0 0       0 /^STRING/ && do {
624 0         0 Convert::BER::STRING->unpack($ber,\$tmp);
625 0 0       0 if ($tmp =~ /[\x00-\x1f\x7f-\xff]/s) {
626 0         0 _hexdump($tmp,$fmt . " : ".$indent, $pos);
627             }
628             else {
629 0         0 printf " = '%s'\n",$tmp;
630             }
631 0         0 last;
632             };
633              
634 0 0       0 /^BIT STRING/ && do {
635 0         0 Convert::BER::BIT_STRING->unpack($ber,\$tmp);
636 0         0 print " = ",$tmp,"\n";
637 0         0 last;
638             };
639              
640             # default -- dump hex data
641 0         0 Convert::BER::STRING->unpack($ber,\$tmp);
642 0         0 _hexdump($tmp,$fmt . " : ".$indent, $pos);
643             }
644             }
645              
646 0         0 select($ofh);
647             }
648              
649             sub hexdump {
650 0     0 1 0 my $ber = shift;
651 0 0       0 my $fh = @_ ? shift : \*STDERR;
652 0         0 my $ofh = select($fh);
653 0         0 _hexdump($ber->[ Convert::BER::_BUFFER() ]);
654 0         0 print "\n";
655 0         0 select($ofh);
656             }
657              
658             ##
659             ## And now the real guts of it, the encoding and decoding routines
660             ##
661              
662             sub encode {
663 70     70 1 131 my $ber = shift;
664 70         239 local($SIG{'__DIE__'});
665              
666 70         188 $ber->[ Convert::BER::_INDEX() ] = [];
667              
668             return $ber
669 70 50       139 if eval { Convert::BER::_encode($ber,\@_) };
  70         188  
670              
671 0         0 $ber->[ Convert::BER::_ERROR() ] = $@;
672              
673 0         0 undef;
674             }
675              
676             sub _encode {
677 124     124   299 my $ber = shift;
678 124         153 my $desc = shift;
679 124         146 my $i = 0;
680              
681 124         302 while($i < @$desc ) {
682 171         279 my $type = $desc->[$i++];
683 171         1041 my $arg = $desc->[$i++];
684 171         213 my $tag = undef;
685              
686 171 100       811 ($type,$tag) = @$type
687             if(ref($type) eq 'ARRAY');
688              
689 171         731 my $can = $ber->can('_' . $type);
690              
691 171 50       396 die "Unknown element '$type'"
692             unless $can;
693              
694 171         679 my $data = &$can();
695 171         265 my $pkg = $data->[ Convert::BER::_PACKAGE() ];
696              
697 171 100       408 $tag = $data->[ Convert::BER::_TAG() ]
698             unless defined $tag;
699              
700 171 100       3114 $arg = &{$arg}(@{$ber->[ Convert::BER::_INDEX() ]})
  14         37  
  14         23  
701             if(ref($arg) eq 'CODE');
702              
703 171 100       359 if(ref($arg) eq 'ARRAY') {
704 49 100       114 if($can = $data->[Convert::BER::_PACK_ARRAY() ]) {
705 43 100       143 pack_tag($ber,$tag)
706             if defined $tag;
707              
708 43         52 &{$can}($pkg,$ber,$arg);
  43         133  
709             }
710             else {
711 6         9 my $a;
712 6         11 foreach $a (@$arg) {
713 15 50       53 pack_tag($ber,$tag)
714             if defined $tag;
715              
716 15         23 &{$data->[Convert::BER::_PACK() ]}($pkg,$ber,$a);
  15         32  
717             }
718             }
719             }
720             else {
721 122 50       468 pack_tag($ber,$tag)
722             if defined $tag;
723 122         157 &{$data->[Convert::BER::_PACK() ]}($pkg,$ber,$arg);
  122         329  
724             }
725             }
726              
727 124         730 1;
728             }
729              
730             sub decode {
731 71     71 1 758 my $ber = shift;
732 71         109 my $pos = $ber->[ Convert::BER::_POS() ];
733 71         218 local($SIG{'__DIE__'});
734              
735 71         136 $ber->[ Convert::BER::_INDEX() ] = [];
736              
737             return $ber
738 71 50       113 if eval { Convert::BER::_decode($ber,\@_) };
  71         172  
739              
740 0         0 $ber->[ Convert::BER::_ERROR() ] = $@;
741 0         0 $ber->[ Convert::BER::_POS() ] = $pos;
742              
743 0         0 undef;
744             }
745              
746             sub _decode {
747 118     118   144 my $ber = shift;
748 118         271 my $desc = shift;
749 118         139 my $i = 0;
750              
751 118         122 my $argc;
752              
753             TAG:
754 118         346 for($argc = @$desc ; $argc > 0 ; $argc -= 2) {
755 160         290 my $type = $desc->[$i++];
756 160         204 my $arg = $desc->[$i++];
757 160         194 my $tag = undef;
758              
759 160 100       381 ($type,$tag) = @$type
760             if(ref($type) eq 'ARRAY');
761              
762 160         720 my $can = $ber->can('_' . $type);
763              
764 160 50       367 die "Unknown element '$type'"
765             unless $can;
766              
767 160         263 my $data = &$can();
768 160         258 my $pkg = $data->[ Convert::BER::_PACKAGE() ];
769              
770 160 100       364 $tag = $data->[ Convert::BER::_TAG() ]
771             unless defined $tag;
772              
773 160 100       470 $arg = &{$arg}(@{$ber->[ Convert::BER::_INDEX() ]})
  6         13  
  6         7  
774             if(ref($arg) eq 'CODE');
775              
776 160 100       565 if(ref($arg) eq 'ARRAY') {
777 42 100       982 if($data->[ Convert::BER::_UNPACK_ARRAY() ]) {
778              
779 36 100       147 unpack_tag($ber,$tag)
780             if(defined $tag);
781              
782 36         75 &{$data->[ Convert::BER::_UNPACK_ARRAY() ]}($pkg,$ber,$arg);
  36         98  
783             }
784             else {
785 6         8 @$arg = ();
786 6         17 while(CORE::length($ber->[ Convert::BER::_BUFFER() ]) > $ber->[ Convert::BER::_POS() ]) {
787 14 50       34 if(defined $tag) {
788             next TAG
789 14 50       18 unless eval { unpack_tag($ber,$tag) };
  14         28  
790             }
791              
792 14         22 push @$arg, undef;
793 14         60 &{$data->[ Convert::BER::_UNPACK() ]}($pkg,$ber,\$arg->[-1]);
  14         28  
794             }
795             }
796             }
797             else {
798 118 50       198 eval {
799 118 50       474 unpack_tag($ber,$tag)
800             if(defined $tag);
801              
802 118         151 &{$data->[ Convert::BER::_UNPACK() ]}($pkg,$ber,$arg);
  118         329  
803 118         868 1;
804             } or ($$arg = undef, die);
805             }
806             }
807              
808 118         521 1;
809             }
810              
811             ##
812             ## a couple of routines to interface to a file descriptor.
813             ##
814              
815             sub read {
816 1     1 1 16 my $ber = shift;
817 1         3 my $io = shift;
818 1         2 my $indef = shift;
819              
820             # We need to read one packet, and exactly only one packet.
821             # So we have to read the first few bytes one at a time, until
822             # we have enough to decode a tage and a length. We then know
823             # how many more bytes to read
824              
825 1 50       5 $ber = $ber->new unless ref($ber);
826 1 50       5 $ber->[ _BUFFER() ] = "" unless $indef;
827              
828 1         3 my $pos = CORE::length($ber->[ _BUFFER() ]);
829 1         2 my $start = $pos;
830              
831             # The first byte is the tag
832 1 50       14 sysread($io,$ber->[ _BUFFER() ],1,$pos++) or
833             goto READ_ERR;
834              
835             # print STDERR "-"x80,"\n";
836             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
837              
838 1         7 my $ch = ord(substr($ber->[ _BUFFER() ],-1));
839              
840             # Tag may be multi-byte
841 1 50       5 if(($ch & 0x1f) == 0x1f) {
842 0         0 do {
843 0 0       0 sysread($io, $ber->[ _BUFFER() ], 1, $pos++) or
844             goto READ_ERR;
845              
846 0         0 $ch = ord(substr($ber->[ _BUFFER() ],-1));
847              
848             } while($ch & 0x80);
849             }
850              
851             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
852              
853             # The next byte will be the first byte of the length
854 1 50       10 sysread($io, $ber->[ _BUFFER() ], 1, $pos++) or
855             goto READ_ERR;
856              
857             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
858              
859 1         4 $ch = ord(substr($ber->[ _BUFFER() ],-1));
860             # print STDERR CORE::unpack("H*",substr($ber->[ _BUFFER() ],-1))," $ch\n";
861              
862             # May be a multi-byte length
863 1 50       10 if($ch & 0x80) {
864 0         0 my $len = $ch & 0x7f;
865 0 0       0 unless ($len) {
866             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
867             # OK we have an indefinate length
868 0         0 while(1) {
869 0         0 Convert::BER::read($ber,$io,1);
870 0         0 my $p = CORE::length($ber->[ _BUFFER() ]);
871 0 0 0     0 if(($p - $pos) == 2 && substr($ber->[ _BUFFER() ],-2) eq "\0\0") {
872             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n","-"x80,"\n";
873 0         0 return $ber;
874             }
875 0         0 $pos = $p;
876             }
877             }
878 0         0 while($len) {
879 0 0       0 my $n = sysread($io, $ber->[ _BUFFER() ], $len, $pos) or
880             goto READ_ERR;
881 0         0 $len -= $n;
882 0         0 $pos += $n;
883             }
884             }
885              
886             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
887              
888             # We can now unpack a tage and a length to determine how many more
889             # bytes to read
890              
891 1         2 $ber->[ _POS() ] = $start;
892 1         5 unpack_tag($ber);
893 1         7 my $len = unpack_length($ber);
894              
895 1         5 while($len > 0) {
896 1         2 my $got;
897              
898             goto READ_ERR
899 1 50       11 unless( $got = sysread($io, $ber->[ _BUFFER() ],$len,CORE::length($ber->[ _BUFFER() ])) );
900              
901 1         4 $len -= $got;
902             }
903              
904             # Reset pos back to the beginning.
905 1         2 $ber->[ _POS() ] = 0;
906              
907             # print STDERR CORE::unpack("H*",$ber->[ _BUFFER() ]),"\n";
908 1         3 return $ber;
909              
910 0         0 READ_ERR:
911             $@ = "I/O Error $! " . CORE::unpack("H*",$ber->[ _BUFFER() ]);
912 0         0 return undef;
913             }
914              
915             sub write {
916 1     1 1 144 my $ber = shift;
917 1         3 my $io = shift;
918 1         5 local($SIG{'__DIE__'});
919              
920 1         11 my $togo = CORE::length($ber->[ _BUFFER() ]);
921 1         2 my $pos = 0;
922              
923 1         43 while($togo) {
924 1         2 my $len;
925              
926 1 50       62 unless ($len = syswrite($io, $ber->[ _BUFFER() ],$togo,$pos)) {
927 0         0 $@ = "I/O Error $!";
928 0         0 return;
929             }
930              
931 1         3 $togo -= $len;
932 1         6 $pos += $len;
933             }
934              
935 1         6 1;
936             }
937              
938             sub send {
939 1     1 1 640 my $ber = shift;
940 1         3 my $sock = shift;
941            
942 1         4 local($SIG{'__DIE__'});
943              
944 1 50       2 eval {
945             # Enable reporting a 'Broken pipe' error rather than dying.
946 1         32 local ($SIG{PIPE}) = "IGNORE";
947              
948 1 50       94 @_ ? send($sock,$ber->[ _BUFFER() ],0,$_[0])
949             : send($sock,$ber->[ _BUFFER() ],0);
950             } or die "I/O Error: $!";
951             }
952              
953             sub recv {
954 1     1 1 19 my $ber = shift;
955 1         2 my $sock = shift;
956              
957 1         9 require Socket; # for Socket::MSG_PEEK
958              
959 1         5 local $SIG{'__DIE__'};
960              
961 1 50       8 $ber = $ber->new unless ref($ber);
962 1         4 $ber->[ _BUFFER() ] = "";
963              
964             # We do not know the size of the datagram, so we have to PEEK --GMB
965             # is there an easier way to determine the packet size ??
966              
967 1         1 my $n = 128;
968 1 50 33     25 die "I/O Error: $!"
969             unless ((defined recv($sock,$ber->[ _BUFFER() ],$n,Socket::MSG_PEEK()))
970             and not $!);
971              
972             # PEEK until we have the complete tag and length of the BER
973             # packet. Use the length to determine how much data to read from
974             # the socket. This is an attempt to ensure that we read the
975             # entire packet and that we don't read into the next packet, if
976             # there is one.
977              
978 1         2 my $len;
979            
980             # Keep reading until we've read enough of the packet to unpack
981             # the BER length field.
982 1         1 for(;;) {
983              
984             # If we can decode a tag and length we can detemine the length
985            
986 1 50 33     3 if(defined($len = eval {
987 1         11 $ber->[ _POS() ] = 0;
988 1         3 unpack_tag($ber);
989 1         3 unpack_length($ber)
990             + $ber->[ _POS() ];
991             })
992             # unpack_length will return -1 for unknown length
993             && $len >= $ber->[ _POS() ]) {
994            
995 1         2 $n = $len;
996 1         2 last;
997             }
998              
999             # peek some more
1000 0         0 $n <<= 1;
1001 0 0 0     0 die "I/O Error: $!"
1002             unless ((defined recv($sock,$ber->[ _BUFFER() ],$n,Socket::MSG_PEEK()))
1003             and not $!);
1004             }
1005            
1006             # now we know the size, get it again but without MSG_PEEK
1007             # this will cause the kernel to remove the datagram from it's queue
1008              
1009             # If the data on the socket doesn't correspond to a valid BER
1010             # object, the loop above could have read something it thought was
1011             # the length and this loop could then block waiting for that many
1012             # bytes, which will never arrive. What do you do about something
1013             # like that?
1014            
1015 1         2 $ber->[ _POS() ] = 0;
1016 1         2 $ber->[ _BUFFER() ] = "";
1017 1         3 my ($read, $tmp);
1018 1         2 $read = 0;
1019 1         3 while ($read < $n) {
1020 1         10 $ber->[ _PEER() ] = recv($sock, $tmp, $n - $read, 0);
1021 1 50 33     9 die "I/O Error: $!"
1022             unless ((defined ( $ber->[ _PEER() ] ) and not $!));
1023            
1024 1         2 $read += CORE::length($tmp);
1025 1         4 $ber->[ _BUFFER() ] .= $tmp;
1026             }
1027 1         5 $ber;
1028             }
1029              
1030             ##
1031             ## The primitive packages
1032             ##
1033              
1034             package Convert::BER::BER;
1035              
1036             sub pack {
1037 0     0   0 my($self,$ber,$arg) = @_;
1038              
1039 0 0       0 $ber->[ Convert::BER::_BUFFER() ] .= $arg->[ Convert::BER::_BUFFER() ]
1040             if ref($arg);
1041              
1042 0         0 1;
1043             }
1044              
1045             sub unpack {
1046 0     0   0 my($self,$ber,$arg) = @_;
1047              
1048 0         0 my $len = CORE::length($ber->[ Convert::BER::_BUFFER() ]) - $ber->[ Convert::BER::_POS() ];
1049              
1050 0         0 $$arg = $ber->new(Convert::BER::unpack($ber,$len));
1051              
1052 0         0 1;
1053             }
1054              
1055             package Convert::BER::ANY;
1056              
1057             sub pack {
1058 0     0   0 my($self,$ber,$arg) = @_;
1059              
1060 0         0 $ber->[ Convert::BER::_BUFFER() ] .= $arg->[ Convert::BER::_BUFFER() ];
1061              
1062 0         0 1;
1063             }
1064              
1065             sub unpack {
1066 0     0   0 my($self,$ber,$arg) = @_;
1067              
1068 0         0 my $pos = $ber->[ Convert::BER::_POS() ];
1069 0         0 my $tag = Convert::BER::unpack_tag($ber);
1070 0         0 my $len = Convert::BER::unpack_length($ber) + $ber->[ Convert::BER::_POS() ] - $pos;
1071 0         0 $ber->[ Convert::BER::_POS() ] = $pos;
1072              
1073 0         0 $$arg = $ber->new(Convert::BER::unpack($ber,$len));
1074              
1075 0         0 1;
1076             }
1077              
1078             ##
1079             ##
1080             ##
1081              
1082             package Convert::BER::BOOLEAN;
1083              
1084             sub pack {
1085 21     21   39 my($self,$ber,$arg) = @_;
1086              
1087 21         47 Convert::BER::pack_length($ber,1);
1088 21 100       191 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("c", $arg ? 0xff : 0x00);
1089              
1090 21         77 1;
1091             }
1092              
1093             sub unpack {
1094 21     21   45 my($self,$ber,$arg) = @_;
1095              
1096 21         40 my $len = Convert::BER::unpack_length($ber);
1097              
1098 21 100       125 $$arg = CORE::unpack("c", Convert::BER::unpack($ber,$len)) ? 1 : 0;
1099              
1100 21         305 1;
1101             }
1102              
1103             ##
1104             ##
1105             ##
1106              
1107             package Convert::BER::INTEGER;
1108              
1109             ##
1110             ## Math::BigInt support
1111             ##
1112              
1113             sub pack_bigint {
1114 0     0   0 my($self,$ber,$arg) = @_;
1115              
1116 0         0 require Math::BigInt;
1117              
1118 0 0       0 my $neg = ($arg < 0) ? 1 : 0;
1119 0         0 my @octet = ();
1120 0         0 my $num = new Math::BigInt(abs($arg));
1121              
1122 0 0       0 $num -= 1 if $neg;
1123 0         0 while($num > 0) {
1124 0         0 my($i,$y) = $num->bdiv(256);
1125 0         0 $num = new Math::BigInt($i);
1126 0 0       0 $y = $y ^ 0xff if $neg;
1127 0         0 unshift(@octet,$y);
1128             }
1129 0 0       0 @octet = (0) unless @octet;
1130              
1131 0 0       0 my $msb = ($octet[0] & 0x80) ? 1 : 0;
1132              
1133 0 0       0 unshift(@octet,$neg ? 0xff : 0x00)
    0          
1134             if($neg != $msb);
1135              
1136 0         0 Convert::BER::pack_length($ber, scalar @octet);
1137              
1138 0         0 $ber->[ Convert::BER::_BUFFER() ] .= CORE::pack("C*",@octet);
1139              
1140 0         0 1;
1141             }
1142              
1143             sub unpack_bigint {
1144 0     0   0 my($self,$ber,$arg) = @_;
1145              
1146 0         0 require Math::BigInt;
1147              
1148 0         0 my $len = Convert::BER::unpack_length($ber);
1149 0         0 my @octet = CORE::unpack("C*",Convert::BER::unpack($ber,$len));
1150 0 0       0 my $neg = ($octet[0] & 0x80) ? 1 : 0;
1151 0         0 my $val = $$arg = 0;
1152              
1153 0         0 while(@octet) {
1154 0         0 my $oct = shift @octet;
1155 0 0       0 $oct = $oct ^ 0xff
1156             if $neg;
1157 0         0 $val *= (1<<8);
1158 0         0 $val += $oct;
1159             }
1160              
1161 0 0       0 $val = -1 - $val
1162             if $neg;
1163              
1164 0         0 1;
1165             }
1166              
1167             ##
1168             ## Math::BigInteger support
1169             ##
1170              
1171             sub pack_biginteger {
1172 0     0   0 my($self,$ber,$arg) = @_;
1173              
1174 0         0 my($len,$data);
1175 0         0 my $offset = 0;
1176              
1177 0         0 require Math::BigInteger;
1178             # save has no concept of +/-
1179 0         0 my $v = $arg->cmp(new Math::BigInteger(0));
1180              
1181 0 0       0 if($v) {
1182 0 0       0 if($v < 0) {
1183 0         0 my $b = $arg->bits + 8;
1184 0         0 $b -= $b % 8;
1185 0         0 my $tmp = new Math::BigInteger(1);
1186 0         0 $tmp->lshift(new Math::BigInteger(1), $b);
1187 0         0 $arg = $tmp + $arg;
1188             }
1189              
1190 0         0 $data = $arg->save;
1191 0         0 $len = CORE::length($data);
1192              
1193 0         0 my $c = ord(substr($data,0,1));
1194              
1195 0 0       0 if($c == 0) {
    0          
1196 0         0 for( ; $len > 1 ; $len--, $offset++) {
1197 0         0 my $ch = ord(substr($data,$offset,1));
1198 0 0       0 if($ch & 0xff) {
1199 0 0       0 if($ch & 0x80) {
1200 0         0 $len++;
1201 0         0 $offset--;
1202             }
1203 0         0 last;
1204             }
1205             }
1206             }
1207             elsif($c == 0xff) {
1208 0         0 for( ; $len > 1 ; $len--, $offset++) {
1209 0         0 my $ch = ord(substr($data,$offset,1));
1210 0 0       0 unless($ch == 0xff) {
1211 0 0       0 unless($ch & 0x80) {
1212 0         0 $len++;
1213 0         0 $offset--;
1214             }
1215 0         0 last;
1216             }
1217             }
1218             }
1219             }
1220             else {
1221 0         0 $len = 1;
1222 0         0 $data = CORE::pack("C",0);
1223             }
1224              
1225 0         0 Convert::BER::pack_length($ber,$len);
1226 0         0 $ber->[ Convert::BER::_BUFFER() ] .= substr($data,$offset);
1227              
1228 0         0 return 1;
1229             }
1230              
1231             sub unpack_biginteger {
1232 0     0   0 my($self,$ber,$arg) = @_;
1233              
1234 0         0 require Math::BigInteger;
1235              
1236 0         0 my $len = Convert::BER::unpack_length($ber);
1237 0         0 my $data = Convert::BER::unpack($ber,$len);
1238 0         0 my $int = restore Math::BigInteger $data;
1239              
1240             # restore has no concept of +/-
1241 0 0       0 if(ord(substr($data,0,1)) & 0x80) {
1242 0         0 my $tmp = new Math::BigInteger;
1243 0         0 $tmp->lshift(new Math::BigInteger(1), $len * 8);
1244 0         0 $tmp = new Math::BigInteger(0) - $tmp;
1245 0         0 $int = $tmp + $int;
1246             }
1247 0         0 $$arg = $int;
1248              
1249 0         0 return 1;
1250             }
1251              
1252             ##
1253             ##
1254             ##
1255              
1256             sub pack {
1257 51     51   87 my($self,$ber,$arg) = @_;
1258              
1259 51 50       129 if(ref $arg) {
1260 0 0       0 goto &pack_bigint
1261             if UNIVERSAL::isa($arg,'Math::BigInt');
1262              
1263 0 0       0 goto &pack_biginteger
1264             if UNIVERSAL::isa($arg,'Math::BigInteger');
1265             }
1266              
1267 51 100       107 my $neg = ($arg < 0) ? 1 : 0;
1268              
1269 51 100       147 my $len = Convert::BER::num_length($neg ? ~ $arg : $arg);
1270              
1271 51         109 my $msb = $arg & (0x80 << (($len - 1) * 8));
1272              
1273 51 100 100     583 $len++
      66        
      66        
1274             if(($msb && not($neg)) || ($neg && not($msb)));
1275 51         150 Convert::BER::pack_length($ber,$len);
1276 51         157 $ber->[ Convert::BER::_BUFFER() ] .= substr(CORE::pack("N",$arg), 0 - $len);
1277              
1278 51         205 1;
1279             }
1280              
1281             sub unpack {
1282 48     48   73 my($self,$ber,$arg) = @_;
1283              
1284 48 50 33     235 if( ref($arg) && ref($$arg) ) {
1285 0 0       0 goto &unpack_bigint
1286             if UNIVERSAL::isa($$arg,'Math::BigInt');
1287              
1288 0 0       0 goto &unpack_biginteger
1289             if UNIVERSAL::isa($$arg,'Math::BigInteger');
1290             }
1291              
1292 48         133 my $len = Convert::BER::unpack_length($ber);
1293 48         142 my $tmp = "\0" x (4 - $len) . Convert::BER::unpack($ber,$len);
1294 48         233 my $val = CORE::unpack("N",$tmp);
1295              
1296 48 100       147 $val -= 0x1 << ($len * 8)
1297             if($val & (0x1 << (($len * 8) - 1)));
1298              
1299 48         66 $$arg = $val;
1300              
1301 48         83 1;
1302             }
1303              
1304             ##
1305             ##
1306             ##
1307              
1308             package Convert::BER::NULL;
1309              
1310             sub pack {
1311 2     2   5 my($self,$ber,$arg) = @_;
1312              
1313 2         10 Convert::BER::pack_length($ber,0);
1314             }
1315              
1316             sub unpack {
1317 2     2   5 my($self,$ber,$arg) = @_;
1318              
1319 2         18 Convert::BER::unpack_length($ber);
1320              
1321 2         3 $$arg = 1;
1322             }
1323              
1324             ##
1325             ##
1326             ##
1327              
1328             package Convert::BER::STRING;
1329              
1330             sub pack {
1331 58     58   174 my($self,$ber,$arg) = @_;
1332              
1333 58         140 Convert::BER::pack_length($ber,CORE::length($arg));
1334 58         370 $ber->[ Convert::BER::_BUFFER() ] .= $arg;
1335             }
1336              
1337             sub unpack {
1338 53     53   92 my($self,$ber,$arg) = @_;
1339              
1340 53         94 my $len = Convert::BER::unpack_length($ber);
1341 53         534 $$arg = Convert::BER::unpack($ber,$len);
1342              
1343 53         863 1;
1344             }
1345              
1346             ##
1347             ##
1348             ##
1349              
1350             package Convert::BER::SEQUENCE;
1351              
1352             sub pack {
1353 0     0   0 my($self,$ber,$arg) = @_;
1354              
1355 0         0 Convert::BER::pack_length($ber,CORE::length($arg->[ Convert::BER::_BUFFER() ]));
1356 0         0 $ber->[ Convert::BER::_BUFFER() ] .= $arg->[ Convert::BER::_BUFFER() ];
1357              
1358 0         0 1;
1359             }
1360              
1361             sub unpack {
1362 33     33   47 my($self,$ber,$arg) = @_;
1363              
1364 33         69 my $len = Convert::BER::unpack_length($ber);
1365 33         341 $$arg = $ber->new(Convert::BER::unpack($ber,$len));
1366              
1367 33         62 1;
1368             }
1369              
1370             sub pack_array {
1371 33     33   59 my($self,$ber,$arg) = @_;
1372              
1373 33         73 my $ber2 = $ber->new;
1374              
1375             return undef
1376 33 50       118 unless defined($ber2->_encode($arg));
1377              
1378 33         112 Convert::BER::pack_length($ber,CORE::length($ber2->[ Convert::BER::_BUFFER() ]));
1379 33         67 $ber->[ Convert::BER::_BUFFER() ] .= $ber2->[ Convert::BER::_BUFFER() ];
1380              
1381 33         213 1;
1382             }
1383              
1384             sub unpack_array {
1385 30     30   53 my($self,$ber,$arg) = @_;
1386              
1387 30         37 my $ber2;
1388              
1389 30         100 $self->unpack($ber,\$ber2);
1390              
1391 30         121 $ber2->_decode($arg);
1392              
1393 30 50       95 die "Sequence buffer not empty"
1394             if CORE::length($ber2->[ Convert::BER::_BUFFER() ]) != $ber2->[ Convert::BER::_POS() ];
1395              
1396 30         137 1;
1397             }
1398              
1399             ##
1400             ##
1401             ##
1402              
1403             package Convert::BER::OBJECT_ID;
1404              
1405             sub pack {
1406 2     2   6 my($self,$ber,$arg) = @_;
1407 2         19 my @data = ($arg =~ /(\d+)/g);
1408              
1409 2 50       8 if(@data < 2) {
1410 0         0 @data = (0);
1411             }
1412             else {
1413 2         7 my $first = $data[1] + ($data[0] * 40);
1414 2         16 splice(@data,0,2,$first);
1415             }
1416              
1417 6         13 @data = map {
1418 2         5 my @d = ($_);
1419 6 100       17 if($_ >= 0x80) {
1420 1         3 @d = ();
1421 1         3 my $v = 0 | $_; # unsigned
1422 1         4 while($v) {
1423 2         4 unshift(@d, 0x80 | ($v & 0x7f));
1424 2         5 $v >>= 7;
1425             }
1426 1         3 $d[-1] &= 0x7f;
1427             }
1428 6         27 @d;
1429             } @data;
1430              
1431 2         7 my $data = CORE::pack("C*", @data);
1432              
1433 2         6 Convert::BER::pack_length($ber,CORE::length($data));
1434 2         4 $ber->[ Convert::BER::_BUFFER() ] .= $data;
1435              
1436 2         9 1;
1437             }
1438              
1439             sub unpack {
1440 2     2   5 my($self,$ber,$arg) = @_;
1441              
1442 2         3 my $len = Convert::BER::unpack_length($ber);
1443 2         5 my @ch = CORE::unpack("C*",Convert::BER::unpack($ber,$len));
1444 2         5 my @data = ();
1445 2         3 my $val = 0;
1446 2         6 while(@ch) {
1447 7         10 my $ch = shift @ch;
1448 7         13 $val = ($val << 7) | ($ch & 0x7f);
1449 7 100       18 unless($ch & 0x80) {
1450 6         7 push @data, $val;
1451 6         18 $val = 0;
1452             }
1453             }
1454 2 50       6 if(@data) {
1455 2         4 my $first = shift @data;
1456 2         5 unshift @data, $first % 40;
1457 2         8 unshift @data, int($first / 40);
1458             # unshift @data, "";
1459             }
1460 2         8 $$arg = join(".",@data);
1461 2         5 1;
1462             }
1463              
1464             ##
1465             ##
1466             ##
1467              
1468             package Convert::BER::CONSTRUCTED;
1469              
1470             BEGIN {
1471             # Cannot call import here as Convert::BER has not been initialized
1472 10     10   54124 *BER_CONSTRUCTOR = *Convert::BER::BER_CONSTRUCTOR
1473             }
1474              
1475             sub pack {
1476 0     0   0 my($self,$ber,$arg) = @_;
1477              
1478 0         0 Convert::BER::pack_tag($ber,$arg->tag | BER_CONSTRUCTOR);
1479 0         0 Convert::BER::pack_length($ber,CORE::length($arg->[ Convert::BER::_BUFFER() ]));
1480 0         0 $ber->[ Convert::BER::_BUFFER() ] .= $arg->[ Convert::BER::_BUFFER() ];
1481              
1482 0         0 1;
1483             }
1484              
1485             sub unpack {
1486 0     0   0 my($self,$ber,$arg) = @_;
1487 0         0 my $tag = Convert::BER::unpack_tag($ber);
1488              
1489 0 0       0 die "Not constructed"
1490             unless $tag & BER_CONSTRUCTOR;
1491              
1492 0         0 my $len = Convert::BER::unpack_length($ber);
1493 0         0 my $buf = $ber->new( Convert::BER::unpack($ber,$len));
1494              
1495 0 0       0 die &{$ber}(0,"Bad construction")
  0         0  
1496             unless( ($buf->tag | BER_CONSTRUCTOR) == $tag);
1497              
1498 0         0 $$arg = $buf;
1499              
1500 0         0 1;
1501             }
1502              
1503             sub pack_array {
1504 0     0   0 my($self,$ber,$arg) = @_;
1505              
1506 0         0 $self->_encode($arg);
1507             }
1508              
1509             sub unpack_array {
1510 0     0   0 my($self,$ber,$arg) = @_;
1511              
1512 0         0 my $ber2;
1513              
1514 0         0 $self->unpack($ber,\$ber2);
1515              
1516 0         0 $ber2->_decode($arg);
1517             }
1518              
1519             ##
1520             ##
1521             ##
1522              
1523             package Convert::BER::OPTIONAL;
1524              
1525             # optional elements
1526             # allows skipping in the encode if it comes across structures like
1527             # OPTIONAL => [ BOOLEAN => undef ]
1528             # or more realistically
1529             # my $foo = undef;
1530             # $foo = 1 if (arg->{'allowed'};
1531             # $ber->encode(SEQUENCE => [
1532             # STRING => $name,
1533             # OPTIONAL => [ BOOLEAN => $foo ]
1534             # ]);
1535              
1536             sub pack_array {
1537 2     2   6 my($self,$ber,$arg) = @_;
1538 2         3 my $a;
1539             my @newarg;
1540 2         4 foreach $a (@$arg) {
1541 4 100       14 return unless defined $a;
1542 0         0 my $c = ref($a) eq "CODE"
1543 3 50       18 ? &{$a}(@{$ber->[ Convert::BER::_INDEX() ]})
  0         0  
1544             : $a;
1545 3 50       13 return unless defined $c;
1546 3         8 push @newarg, $c;
1547             }
1548              
1549 1 50       5 shift @newarg if (@newarg & 1);
1550              
1551 1         8 Convert::BER::_encode($ber,\@newarg);
1552             }
1553              
1554             sub unpack_array {
1555 1     1   3 my($self,$ber,$arg) = @_;
1556 1         2 my($yes,$ref);
1557 1         2 my $pos = $ber->[ Convert::BER::_POS() ];
1558              
1559 1 50       4 if(@$arg & 1) {
1560 0         0 $ref = [ @$arg ];
1561 0         0 $yes = shift @$ref;
1562             }
1563             else {
1564 1         2 $ref = $arg;
1565             }
1566              
1567 1 50       2 if (eval { Convert::BER::_decode($ber,$ref) }) {
  1         6  
1568 1 50       3 $$yes = 1 if ref($yes);
1569             }
1570             else {
1571 0 0       0 $$yes = undef if ref($yes);
1572 0         0 $ber->[ Convert::BER::_POS() ] = $pos;
1573             }
1574              
1575 1         5 1;
1576             }
1577              
1578             ##
1579             ##
1580             ##
1581              
1582             package Convert::BER::SEQUENCE_OF;
1583              
1584             sub pack_array {
1585 8     8   16 my($self,$ber,$arg) = @_;
1586 8         23 my($n,@desc) = @$arg;
1587 8         12 my $i;
1588              
1589 8 100       41 $n = &{$n}(@{$ber->[ Convert::BER::_INDEX() ]})
  2         6  
  2         5  
1590             if ref($n) eq 'CODE';
1591              
1592 8         14 push(@{$ber->[ Convert::BER::_INDEX() ]},0);
  8         19  
1593              
1594 8         21 my $b = $ber->new;
1595              
1596 8 100       39 if(ref($n) eq 'HASH') {
    100          
1597 3         5 my $v;
1598 3         26 foreach $v (keys %$n) {
1599 4         11 $ber->[ Convert::BER::_INDEX() ][-1] = $v;
1600 4         14 $b->_encode(\@desc);
1601             }
1602             }
1603             elsif(ref($n) eq 'ARRAY') {
1604 2         5 my $v;
1605 2         5 foreach $v (@$n) {
1606 4         8 $ber->[ Convert::BER::_INDEX() ][-1] = $v;
1607 4         19 $b->_encode(\@desc);
1608             }
1609             }
1610             else {
1611 3         17 while($n--) {
1612 12         32 $b->_encode(\@desc);
1613 12         30 $ber->[ Convert::BER::_INDEX() ][-1] += 1;
1614             }
1615             }
1616              
1617 8         16 pop @{$ber->[ Convert::BER::_INDEX() ]};
  8         15  
1618              
1619 8         23 Convert::BER::pack_length($ber,CORE::length($b->[ Convert::BER::_BUFFER() ]));
1620 8         33 $ber->[ Convert::BER::_BUFFER() ] .= $b->[ Convert::BER::_BUFFER() ];
1621              
1622 8         59 1;
1623             }
1624              
1625             sub unpack_array {
1626 5     5   9 my($self,$ber,$arg) = @_;
1627 5         14 my($nref,@desc) = @$arg;
1628              
1629 5         7 push(@{$ber->[ Convert::BER::_INDEX() ]},0);
  5         10  
1630              
1631 5         21 my $len = Convert::BER::unpack_length($ber);
1632 5         13 my $b = $ber->new(Convert::BER::unpack($ber,$len));
1633 5         9 my $pos = $ber->[ Convert::BER::_POS() ];
1634 5         14 my $n;
1635              
1636 5         33 while(CORE::length($b->[ Convert::BER::_BUFFER() ]) > $b->[ Convert::BER::_POS() ]) {
1637 16         56 $b->_decode(\@desc);
1638 16         42 $ber->[ Convert::BER::_INDEX() ][-1] += 1;
1639             }
1640              
1641 5         8 $$nref = pop @{$ber->[ Convert::BER::_INDEX() ]};
  5         10  
1642 5         25 1;
1643             }
1644              
1645             ##
1646             ##
1647             ##
1648              
1649             package Convert::BER::BIT_STRING;
1650              
1651             sub pack {
1652 3     3   7 my($self,$ber,$arg) = @_;
1653              
1654 3         6 my $less = (8 - (CORE::length($arg) & 7)) & 7;
1655 3 100       8 $arg .= "0" x $less if $less;
1656 3         10 my $data = CORE::pack("B*",$arg);
1657 3         8 Convert::BER::pack_length($ber,CORE::length($data)+1);
1658 3         14 $ber->[ Convert::BER::_BUFFER() ] .= chr($less) . $data;
1659             }
1660              
1661             sub unpack {
1662 3     3   7 my($self,$ber,$arg) = @_;
1663              
1664 3         6 my $len = Convert::BER::unpack_length($ber);
1665 3         5 my $data = Convert::BER::unpack($ber,$len);
1666 3         4 my $less;
1667 3         11 ($less,$data) = CORE::unpack("C B*",$data,);
1668 3         8 $less = ord($less) & 7;
1669 3 100       8 substr($data,-$less) = '' if $less;
1670 3         4 $$arg = $data;
1671 3         6 1;
1672             }
1673              
1674             ##
1675             ##
1676             ##
1677              
1678             package Convert::BER::BIT_STRING8;
1679              
1680             sub pack {
1681 0     0     my($self,$ber,$arg) = @_;
1682              
1683 0           Convert::BER::pack_length($ber,CORE::length($arg)+1);
1684 0           $ber->[ Convert::BER::_BUFFER() ] .= chr(0) . $arg;
1685             }
1686              
1687             sub unpack {
1688 0     0     my($self,$ber,$arg) = @_;
1689              
1690 0           my $len = Convert::BER::unpack_length($ber);
1691 0           my $less = Convert::BER::unpack($ber,1);
1692 0 0         my $data = $len > 1 ? Convert::BER::unpack($ber,$len-1) : "";
1693 0           $$arg = $data;
1694 0           1;
1695             }
1696              
1697             ##
1698             ##
1699             ##
1700              
1701             package Convert::BER::REAL;
1702              
1703             sub pack {
1704 0     0     my($self,$ber,$arg) = @_;
1705 0           require POSIX;
1706 0           my $data = "";
1707              
1708 0 0         if($arg) {
1709 0           my $s = 128;
1710 0 0         if($arg < 0) {
1711 0           $s |= 64;
1712 0           $arg = -$arg;
1713             }
1714 0           my @e = ();
1715 0           my @m = ();
1716 0           my($v,$e) = POSIX::frexp($arg);
1717 0           $e -= 53;
1718 0           my $ae = abs($e);
1719              
1720 0 0         if($ae < 0x80) {
    0          
    0          
1721 0           @e = ($e & 0xff);
1722             }
1723             elsif($ae < 0x8000) {
1724 0           @e = map { $_ & 0xff } ($e>>8,$e);
  0            
1725 0           $s |= 1;
1726             }
1727             elsif($ae < 0x800000) {
1728 0           @e = map { $_ & 0xff } ($e>>16,$e>>8,$e);
  0            
1729 0           $s |= 2;
1730             }
1731             else {
1732 0           @e = (4, map { $_ & 0xff } ($e>>24,$e>>16,$e>>8,$e));
  0            
1733 0           $s |= 3;
1734             }
1735              
1736 0           $v = POSIX::ldexp($v,5);
1737 0           my $f = POSIX::floor($v);
1738 0           my $i = int($f);
1739 0           @m = ($i & 0xff);
1740 0           $v -= $f;
1741 0           for (1..2) {
1742 0           $v = POSIX::ldexp($v,24);
1743 0           $f = POSIX::floor($v);
1744 0           $i = int($f);
1745 0           push @m, ($i >> 16) & 0xff, ($i >> 8) & 0xff, $i & 0xff;
1746 0           $v -= $f;
1747             }
1748 0           $data = pack("C*",$s,@e,@m);
1749             }
1750 0           my $len = length($data);
1751 0           Convert::BER::pack_length($ber,$len);
1752 0 0         Convert::BER::pack($ber,$data) if $len;
1753             }
1754              
1755             my @base = (1,3,4,4);
1756              
1757             sub unpack {
1758 0     0     my($self,$ber,$arg) = @_;
1759              
1760 0           my $len = Convert::BER::unpack_length($ber);
1761 0 0         unless($len) {
1762 0           $$arg = undef;
1763 0           return 1;
1764             }
1765 0           my $data = Convert::BER::unpack($ber,$len);
1766 0           my $byte = unpack("C*",$data);
1767              
1768 0 0         if($byte & 0x80) {
    0          
    0          
1769 0           $data = reverse $data;
1770 0           chop($data);
1771 0           require POSIX; # The sins for using REAL
1772 0           my $base = $base[($byte & 0x30) >> 4];
1773 0           my $scale = $base & 0xC;
1774 0           my $elen = $byte & 0x3;
1775            
1776 0 0         $elen = ord(chop($data)) - 1 if $elen == 3;
1777              
1778 0 0 0       die "Bad REAL encoding" unless $elen >= 0 && $elen <= 3;
1779              
1780 0           my $exp = ord chop($data);
1781 0 0         $exp = -256 + $exp if $exp > 127;
1782              
1783 0           while ($elen--) {
1784 0           $exp *= 256;
1785 0           $exp += ord chop($data);
1786             }
1787              
1788 0           $exp = $exp * $base + $scale;
1789              
1790 0           my $v = 0;
1791 0           while(length($data)) {
1792 0           $v = POSIX::ldexp($v,8) + ord chop($data);
1793             }
1794              
1795 0 0         $v = POSIX::ldexp($v,$exp) if $exp;
1796 0 0         $v = -1 * $v if $byte & 0x40; # negative
1797              
1798 0           $$arg = $v;
1799             }
1800             elsif($byte & 0x40) {
1801 0           require POSIX;
1802 0 0         $$arg = POSIX::HUGE_VAL() * (($byte & 1) ? -1 : 1);
1803             }
1804             elsif(substr($data,1) =~ /^\s*([-+]?)0*(\d+(?:\.\d+(?:[Ee][-+]?\d+)?)?)\s*$/) {
1805 0           $$arg = eval "$1$2";
1806             }
1807             else {
1808 0           $$arg = undef;
1809             }
1810 0           1;
1811             }
1812              
1813             ##
1814             ##
1815             ##
1816              
1817             package Convert::BER::_Time_generic;
1818              
1819             sub pack {
1820 0     0     my($self,$ber,$arg) = @_;
1821              
1822 0   0       my $islocal = $self->isa('Convert::BER::TimeUL')
1823             || $self->isa('Convert::BER::TimeGL');
1824 0   0       my $isgen = $self->isa('Convert::BER::TimeGL')
1825             || $self->isa('Convert::BER::TimeGZ');
1826 0 0         my @time = $islocal ? localtime($arg) : gmtime($arg);
1827 0           my $off = 'Z';
1828              
1829 0 0         if($islocal) {
1830 0           my @g = gmtime($arg);
1831 0           my $v = ($time[1] - $g[1]) + ($time[2] - $g[2]) * 60;
1832 0           my $d = $time[7] - $g[7];
1833 0 0 0       if($d == 1 || $d < -1) {
    0          
1834 0           $v += 1440;
1835             }
1836             elsif($d > 1) {
1837 0           $v -= 1440;
1838             }
1839 0           $off = sprintf("%+03d%02d",$v / 60, abs($v % 60));
1840             }
1841            
1842 0           $time[4] += 1;
1843 0 0         $time[5] = $isgen ? $time[5] + 1900 : $time[5] % 100;
1844 0           my $str = sprintf("%02d"x6, @time[5,4,3,2,1,0]);
1845 0 0         if($isgen) {
1846 0           my $split = $arg - int($arg);
1847 0 0         $str .= sprintf(".%03d", int($split * 1000)) if($split);
1848             }
1849 0           Convert::BER::STRING::pack($self,$ber,$str . $off);
1850             }
1851              
1852             sub unpack {
1853 0     0     my($self,$ber,$arg) = @_;
1854 0           my $str;
1855 0 0         if(Convert::BER::STRING::unpack($self,$ber,\$str)) {
1856 0   0       my $isgen = $self->isa('Convert::BER::TimeGL')
1857             || $self->isa('Convert::BER::TimeGZ');
1858 0 0         my $n = $isgen ? 4 : 2;
1859 0 0         my ($Y,$M,$D,$h,$m,$s,$z) = $str =~ /^
1860             (\d{$n})
1861             (\d\d)
1862             (\d\d)
1863             (\d\d)
1864             (\d\d)
1865             ((?:\d\d(?:\.\d+)?)?)
1866             (Z|[-+]\d{4})
1867             $/x or die "Bad Time string '$str'";
1868 0           my $offset = 0;
1869 0 0         if($z ne 'Z') {
1870 10     10   15354 use integer;
  10         123  
  10         56  
1871 0           $offset = ((($z / 100) * 60) + ($z % 100)) * 60;
1872             }
1873 0 0         if($s > int($s)) { # fraction of a seccond
1874 0           $offset -= ($s - int($s));
1875             }
1876 0           $M -= 1;
1877 0 0         if($isgen) { # GeneralizedTime uses 4-digit years
    0          
1878 0           $Y -= 1900;
1879             }
1880             elsif($Y <= 50) { # ASN.1 UTCTime
1881 0           $Y += 100; # specifies <=50 = 2000..2050, >50 = 1951..1999
1882             }
1883 0           require Time::Local;
1884 0           $$arg = Time::Local::timegm(int($s),$m,$h,$D,$M,$Y) - $offset;
1885             }
1886             }
1887              
1888             package Convert::BER::CHOICE;
1889              
1890             sub pack_array {
1891 0     0     my($self,$ber,$arg) = @_;
1892 0           my $n = $arg->[0];
1893              
1894 0 0         if(defined($n)) {
1895 0           my $i = ($n * 2) + 2;
1896 0 0 0       die "Bad CHOICE index $n" if $n < 0 || $i > @$arg;
1897 0           $ber->_encode([$arg->[$i-1], $arg->[$i]]);
1898             }
1899 0           1;
1900             }
1901              
1902             sub unpack_array {
1903 0     0     my($self,$ber,$arg) = @_;
1904 0           my($i,$m,$err);
1905              
1906 0           $m = @$arg;
1907 0           my $want = Convert::BER::tag($ber);
1908              
1909 0           for($i = 1 ; $i < $m ; $i += 2) {
1910 0           my $tag;
1911 0           my $type = $arg->[$i];
1912              
1913 0 0         ($type,$tag) = @$type
1914             if(ref($type) eq 'ARRAY');
1915              
1916 0           my $can = UNIVERSAL::can($ber,'_' . $type);
1917              
1918 0 0         die "Unknown element '$type'"
1919             unless $can;
1920              
1921 0           my $data = &$can();
1922              
1923 0 0         $tag = $data->[ Convert::BER::_TAG() ]
1924             unless defined $tag;
1925              
1926 0 0         next unless $tag == $want;
1927            
1928 0 0         if ( eval { Convert::BER::_decode($ber,[@{$arg}[$i,$i+1]]) }) {
  0            
  0            
1929 0           my $choice = $arg->[0];
1930 0           $$choice = ($i - 1) >> 1;
1931 0           return 1;
1932             }
1933 0 0         $err = $@ if $@;
1934             }
1935 0   0       die ($err || sprintf("Cannot decode CHOICE, found tag 0x%X\n",$want));
1936             }
1937              
1938             1;