File Coverage

lib/Encoding/BER.pm
Criterion Covered Total %
statement 512 662 77.3
branch 160 248 64.5
condition 47 86 54.6
subroutine 54 63 85.7
pod 7 57 12.2
total 780 1116 69.8


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             # Copyright (c) 2007 by Jeff Weisberg
4             # Author: Jeff Weisberg
5             # Created: 2007-Jan-28 16:03 (EST)
6             # Function: BER encoding/decoding (also: CER and DER)
7             #
8             # $Id: BER.pm,v 1.11 2008/05/31 18:43:11 jaw Exp $
9              
10             # references: ITU-T x.680 07/2002 - ASN.1
11             # references: ITU-T x.690 07/2002 - BER
12              
13             package Encoding::BER;
14 3     3   1986 use vars qw($VERSION);
  3         5  
  3         182  
15             $VERSION = '1.02';
16 3     3   14 use Carp;
  3         4  
  3         210  
17 3     3   15 use strict;
  3         7  
  3         22517  
18             # loaded on demand if needed:
19             # POSIX
20             # used if already loaded:
21             # Math::BigInt
22              
23             =head1 NAME
24              
25             Encoding::BER - Perl module for encoding/decoding data using ASN.1 Basic Encoding Rules (BER)
26              
27             =head1 SYNOPSIS
28              
29             use Encoding::BER;
30             my $enc = Encoding::BER->new();
31             my $ber = $enc->encode( $data );
32             my $xyz = $enc->decode( $ber );
33              
34             =head1 DESCRIPTION
35              
36             Unlike many other BER encoder/decoders, this module uses tree structured data
37             as the interface to/from the encoder/decoder.
38              
39             The decoder does not require any form of template or description of the
40             data to be decoded. Given arbitrary BER encoded data, the decoder produces
41             a tree shaped perl data structure from it.
42              
43             The encoder takes a perl data structure and produces a BER encoding from it.
44            
45             =head1 METHODS
46              
47             =over 4
48              
49             =cut
50             ;
51              
52             ################################################################
53              
54             my %CLASS =
55             (
56             universal => { v => 0, },
57             application => { v => 0x40, },
58             context => { v => 0x80, },
59             private => { v => 0xC0, },
60             );
61              
62             my %TYPE =
63             (
64             primitive => { v => 0, },
65             constructed => { v => 0x20, },
66             );
67              
68             my %TAG =
69             (
70             universal => {
71             content_end => { v => 0, },
72             boolean => { v => 1, e => \&encode_bool, d => \&decode_bool },
73             integer => { v => 2, e => \&encode_int, d => \&decode_int },
74             bit_string => { v => 3, e => \&encode_bits, d => \&decode_bits, dc => \&reass_string, rule => 1 },
75             octet_string => { v => 4, e => \&encode_string, d => \&decode_string, dc => \&reass_string, rule => 1 },
76             null => { v => 5, e => \&encode_null, d => \&decode_null },
77             oid => { v => 6, e => \&encode_oid, d => \&decode_oid },
78             object_descriptor => { v => 7, implicit => 'octet_string' },
79             external => { v => 8, type => ['constructed'] },
80             real => { v => 9, e => \&encode_real, d => \&decode_real },
81             enumerated => { v => 0xA, implicit => 'integer' },
82             embedded_pdv => { v => 0xB, e => \&encode_string, d => \&decode_string, dc => \&reass_string },
83             utf8_string => { v => 0xC, implicit => 'octet_string' },
84             relative_oid => { v => 0xD, e => \&encode_roid, d => \&decode_roid },
85             # reserved
86             # reserved
87             sequence => { v => 0x10, type => ['constructed'] },
88             set => { v => 0x11, type => ['constructed'] },
89             numeric_string => { v => 0x12, implicit => 'octet_string' },
90             printable_string => { v => 0x13, implicit => 'octet_string' },
91             teletex_string => { v => 0x14, implicit => 'octet_string' },
92             videotex_string => { v => 0x15, implicit => 'octet_string' },
93             ia5_string => { v => 0x16, implicit => 'octet_string' },
94             universal_time => { v => 0x17, implicit => 'octet_string' },
95             generalized_time => { v => 0x18, implicit => 'octet_string' },
96             graphic_string => { v => 0x19, implicit => 'octet_string' },
97             visible_string => { v => 0x1a, implicit => 'octet_string' },
98             general_string => { v => 0x1b, implicit => 'octet_string' },
99             universal_string => { v => 0x1c, implicit => 'octet_string' },
100             character_string => { v => 0x1d, implicit => 'octet_string' },
101             bmp_string => { v => 0x1e, implicit => 'octet_string' },
102             },
103            
104             private => {
105             # extra.
106             # no, the encode/decode functions are not mixed up.
107             # yes, this module handles large tag-numbers.
108             integer32 => { v => 0xFFF0, type => ['private'], e => \&encode_uint32, d => \&decode_int },
109             unsigned_int => { v => 0xFFF1, type => ['private'], e => \&encode_uint, d => \&decode_uint },
110             unsigned_int32 => { v => 0xFFF2, type => ['private'], e => \&encode_uint32, d => \&decode_uint },
111             },
112             );
113              
114             # synonyms
115             my %AKATAG =
116             (
117             bool => 'boolean',
118             int => 'integer',
119             string => 'octet_string',
120             object_identifier => 'oid',
121             relative_object_identifier => 'relative_oid',
122             roid => 'relative_oid',
123             float => 'real',
124             enum => 'enumerated',
125             sequence_of => 'sequence',
126             set_of => 'set',
127             t61_string => 'teletex_string',
128             iso646_string => 'visible_string',
129             int32 => 'integer32',
130             unsigned_integer => 'unsigned_int',
131             uint => 'unsigned_int',
132             uint32 => 'unsigned_int32',
133             # ...
134             );
135              
136             # insert name into above data
137             my %ALLTAG;
138             my %REVTAG;
139              
140             # insert name + class into above data
141             # build reverse map, etc.
142             init_tag_lookups( \%TAG, \%ALLTAG, \%REVTAG );
143              
144             my %REVCLASS = map {
145             ( $CLASS{$_}{v} => $_ )
146             } keys %CLASS;
147              
148             my %REVTYPE = map {
149             ( $TYPE{$_}{v} => $_ )
150             } keys %TYPE;
151              
152             ################################################################
153              
154             =item new(option => value, ...)
155              
156             constructor.
157              
158             example:
159             my $enc = Encoding::BER->new( error => sub{ die "$_[1]\n" } );
160              
161             the following options are available:
162              
163             =over 4
164              
165             =item error
166              
167             coderef called if there is an error. will be called with 2 parameters,
168             the Encoding::BER object, and the error message.
169              
170             # example: die on error
171             error => sub{ die "oops! $_[1]\n" }
172            
173             =item warn
174              
175             coderef called if there is something to warn about. will be called with 2 parameters,
176             the Encoding::BER object, and the error message.
177              
178             # example: warn for warnings
179             warn => sub{ warn "how odd! $_[1]\n" }
180            
181              
182             =item decoded_callback
183              
184             coderef called for every element decoded. will be called with 2 parameters,
185             the Encoding::BER object, and the decoded data. [see DECODED DATA]
186              
187             # example: bless decoded results into a useful class
188             decoded_callback => sub{ bless $_[1], MyBER::Result }
189            
190             =item debug
191              
192             boolean. if true, large amounts of useless gibberish will be sent to stderr regarding
193             the encoding or decoding process.
194              
195             # example: enable gibberish output
196             debug => 1
197              
198             =back
199              
200             =cut
201             ;
202              
203             sub new {
204 94     94 1 92028 my $cl = shift;
205 94         202 my $me = bless { @_ }, $cl;
206              
207 94         211 $me;
208             }
209              
210             sub error {
211 0     0 1 0 my $me = shift;
212 0         0 my $msg = shift;
213              
214 0 0       0 if( my $f = $me->{error} ){
215 0         0 $f->($me, $msg);
216             }else{
217 0         0 croak ((ref $me) . ": $msg\n");
218             }
219 0         0 undef;
220             }
221              
222             sub warn {
223 1     1 1 6 my $me = shift;
224 1         2 my $msg = shift;
225              
226 1 50       5 if( my $f = $me->{warn} ){
227 1         4 $f->($me, $msg);
228             }else{
229 0         0 carp ((ref $me) . ": $msg\n");
230             }
231 1         3 undef;
232             }
233              
234             sub debug {
235 630     630 1 1656 my $me = shift;
236 630         704 my $msg = shift;
237              
238 630 50       1604 return unless $me->{debug};
239 0         0 print STDERR " " x $me->{level}, $msg, "\n";
240 0         0 undef;
241             }
242              
243             ################################################################
244              
245             sub add_tag_hash {
246 139     139 0 168 my $me = shift;
247 139         163 my $class = shift;
248 139         202 my $type = shift;
249 139         172 my $name = shift;
250 139         177 my $num = shift;
251 139         164 my $data = shift;
252              
253 139 50       288 return $me->error("invalid class: $class") unless $CLASS{$class};
254 139 50       275 return $me->error("invalid type: $type") unless $TYPE{$type};
255              
256 139         311 $data->{type} = [$class, $type];
257 139         199 $data->{v} = $num;
258 139         194 $data->{n} = $name;
259            
260             # install forward + reverse mappings
261 139         281 $me->{tags}{$name} = $data;
262 139         341 $me->{revtags}{$class}{$num} = $name;
263              
264 139         347 $me;
265             }
266              
267             =item add_implicit_tag(class, type, tag-name, tag-number, base-tag)
268              
269             add a new tag similar to another tag. class should be one of C,
270             C, C, or C. type should be either C
271             or C. tag-name should specify the name of the new tag.
272             tag-number should be the numeric tag number. base-tag should specify the
273             name of the tag this is equivalent to.
274              
275             example: add a tagged integer
276             in ASN.1: width-index ::= [context 42] implicit integer
277            
278             $ber->add_implicit_tag('context', 'primitive', 'width-index', 42, 'integer');
279              
280             =cut
281             ;
282              
283             sub add_implicit_tag {
284 138     138 1 404 my $me = shift;
285 138         168 my $class = shift;
286 138         155 my $type = shift;
287 138         167 my $name = shift;
288 138         156 my $num = shift;
289 138         155 my $base = shift;
290              
291 138 50       259 return $me->error("unknown base tag name: $base")
292             unless $me->tag_data_byname($base);
293              
294 138         399 $me->add_tag_hash($class, $type, $name, $num, {
295             implicit => $base,
296             });
297             }
298              
299             sub add_tag {
300 1     1 0 2 my $me = shift;
301 1         2 my $class = shift;
302 1         2 my $type = shift;
303 1         2 my $name = shift;
304 1         2 my $num = shift;
305             # possibly optional:
306 1         1 my $encf = shift;
307 1         2 my $decf = shift;
308 1         2 my $encfc = shift;
309 1         1 my $decfc = shift;
310            
311 1         8 $me->add_tag_hash($class, $type, $name, $num, {
312             e => $encf,
313             d => $decf,
314             ec => $encfc,
315             dc => $decfc,
316             });
317             }
318              
319             sub init_tag_lookups {
320 3     3 0 7 my $TAG = shift;
321 3         4 my $ALL = shift;
322 3         6 my $REV = shift;
323            
324 3         12 for my $class (keys %$TAG){
325 6         9 for my $name (keys %{$TAG->{$class}}){
  6         33  
326 96         152 $TAG->{$class}{$name}{n} = $name;
327 96         189 $ALL->{$name} = $TAG->{$class}{$name};
328             }
329             my %d = map {
330 96         255 ($TAG->{$class}{$_}{v} => $_)
331 6         14 } keys %{$TAG->{$class}};
  6         21  
332 6         39 $REV->{$class} = \%d;
333             }
334             }
335              
336             ################################################################
337              
338             =item encode( data )
339              
340             BER encode the provided data. [see: ENCODING DATA]
341              
342             example:
343             my $ber = $enc->encode( [0, 'public', [7.3, 0, 0, ['foo', 'bar']]] );
344              
345             =cut
346             ;
347              
348             sub encode {
349 113     113 1 318 my $me = shift;
350 113         129 my $data = shift;
351 113         161 my $levl = shift;
352            
353 113   100     401 $me->{level} = $levl || 0;
354 113 100 100     363 $data = $me->canonicalize($data) if $me->{acanonical} || !$me->behaves_like_a_hash($data);
355              
356             # include pre-encoded data as is
357 113 50       1240 if( $data->{type} eq 'BER_preencoded' ){
358 0         0 return $data->{value};
359             }
360            
361 113   33     215 $data = $me->rule_check_and_apply($data) || $data;
362 113         261 my($typeval, $tagnum, $encfnc) = $me->ident_data_and_efunc($data->{type});
363 113         149 my $value;
364              
365 113 100       201 if( $typeval & 0x20 ){
366 5         23 $me->debug( "encode constructed ($typeval/$tagnum) [" );
367             # constructed - recurse
368 5 50       14 my @vs = ref($data->{value}) ? @{$data->{value}} : $data->{value};
  5         14  
369 5         11 for my $e (@vs){
370 20         78 $value .= $me->encode( $e, $me->{level} + 1 );
371             }
372 5   100     24 $me->{level} = $levl || 0;
373 5         10 $me->debug("]");
374             }else{
375 108         302 $me->debug( "encode primitive ($typeval/$tagnum)" );
376            
377 108 100       227 unless( $encfnc ){
378             # try to guess encoding
379 1 50       4 my @t = ref($data->{type}) ? @{$data->{type}} : $data->{type};
  1         3  
380 1         16 $me->warn("do not know how to encode identifier [@t] ($typeval/$tagnum)");
381 1         3 $encfnc = \&encode_unknown;
382             }
383 108         201 $value = $encfnc->($me, $data);
384             }
385              
386 113         248 my $defp = $me->use_definite_form($typeval, $data);
387 113         241 my $leng = $me->encode_length(length($value));
388              
389 113         133 my $res;
390 113 100 66     426 if( $defp && defined($leng) ){
391 112         205 $me->debug("encode definite form");
392 112         206 $res = $me->encode_ident($typeval, $tagnum) . $leng . $value;
393             }else{
394 1         3 $me->debug("encode indefinite form");
395 1         2 $res = $me->encode_ident($typeval, $tagnum) . "\x80" . $value . "\x00\x00";
396             # x.690: 8.3.6.1 8.1.5
397             }
398            
399 113         210 $data->{dlen} = length($value);
400 113         161 $data->{tlen} = length($res);
401              
402 113         355 $res;
403             }
404              
405             sub encode_null {
406 2     2 0 5 my $me = shift;
407 2         7 $me->debug('encode null');
408 2         5 '';
409             }
410              
411             sub encode_unknown {
412 1     1 0 2 my $me = shift;
413 1         2 my $data = shift;
414              
415 1         2 $me->debug('encode unknown');
416 1         3 '' . $data->{value};
417             }
418              
419             sub encode_string {
420 9     9 0 15 my $me = shift;
421 9         12 my $data = shift;
422              
423             # CER splitting of long strings is handled in CER subclass
424 9         20 $me->debug('encode string');
425 9         25 '' . $data->{value};
426             }
427              
428             sub encode_bits {
429 1     1 0 2 my $me = shift;
430 1         2 my $data = shift;
431              
432             # x.690 8.6
433 1         3 $me->debug('encode bitstring');
434 1         3 "\0" . $data->{value};
435              
436             }
437              
438             sub encode_bool {
439 2     2 0 3 my $me = shift;
440 2         3 my $data = shift;
441              
442             # x.690 11.1
443 2         4 $me->debug('encode boolean');
444 2 100       12 $data->{value} ? "\xFF" : "\x0";
445             }
446              
447             sub encode_int {
448 48     48 0 56 my $me = shift;
449 48         55 my $data = shift;
450 48         64 my $val = $data->{value};
451              
452 48         49 my @i;
453             my $big;
454              
455 48 100       85 if( _have_math_bigint() ){
456             # value is a bigint or a long string
457 44 100 66     257 $big = 1 if (ref $val && $val->can('as_hex')) || length($val) > 8;
      100        
458             }
459            
460 48 100       85 if( $big ){
461 22         68 my $x = Math::BigInt->new($val);
462 22         504 $me->debug("bigint $val => $x");
463 22 100       72 my $sign = $x->is_neg() ? 0xff : 0;
464 22 100       146 if( $sign ){
465             # NB: in 2s comp: -X = ~(X-1) = ~X+1
466 9         23 $x = $x->bneg()->bsub(1)->as_hex();
467 9         1945 $x =~ s/^0x//;
468 9 100       28 $x = '0'.$x if length($x) & 1;
469 9         28 @i = map{ ~$_ & 0xff } unpack('C*', pack('H*', $x));
  31         52  
470 9 100       26 unshift @i, 0xff unless $i[0] & 0x80;
471             }else{
472 13         35 $x = $x->as_hex();
473 13         930 $x =~ s/^0x//;
474 13 100       37 $x = '0'.$x if length($x) & 1;
475 13         44 @i = unpack('C*', pack('H*', $x));
476 13 100       62 unshift @i, 0 if $i[0] & 0x80;
477             }
478 22         106 $me->debug("encode big int [@i]");
479             }else{
480 26 100       48 my $sign = ($val < 0) ? 0xff : 0;
481 26         32 while(1){
482 36         49 unshift @i, $val & 0xFF;
483 36 100 100     142 last if $val >= -128 && $val < 128;
484             # NB: >>= does not preserve sign.
485 10         19 $val = int(($val - $sign)/256);
486             }
487 26         88 $me->debug("encode int [@i]");
488             }
489 48         169 pack('C*', @i);
490             }
491              
492             sub encode_uint {
493 12     12 0 15 my $me = shift;
494 12         15 my $data = shift;
495 12         19 my $val = $data->{value};
496            
497 12         14 my @i;
498             my $big;
499              
500 12 50       19 if( _have_math_bigint() ){
501             # value is a bigint or a long string
502 12 100 66     74 $big = 1 if (ref $val && $val->can('bcmp')) || length($val) > 8;
      66        
503             }
504              
505 12 100       22 if( $big ){
506 6         19 my $x = Math::BigInt->new($val)->as_hex();
507 6         217 $x =~ s/^0x//;
508 6 100       20 $x = '0' . $x if length($x) & 1;
509 6         13 $me->debug("encode big unsigned int");
510 6         21 pack('H*', $x);
511             }else{
512 6         18 while($val){
513 7         13 unshift @i, $val & 0xFF;
514 7         17 $val >>= 8;
515             }
516 6         20 $me->debug("encode unsigned int [@i]");
517 6         19 pack('C*', @i);
518             }
519             }
520              
521              
522             sub encode_uint32 {
523 2     2 0 3 my $me = shift;
524 2         3 my $data = shift;
525 2         4 my $val = $data->{value};
526              
527             # signed or unsigned. -1 == 0xffffffff
528 2         5 $me->debug("encode unsigned int32");
529 2         8 pack('N', $val);
530             }
531              
532             sub encode_real {
533 29     29 0 38 my $me = shift;
534 29         40 my $data = shift;
535 29         39 my $val = $data->{value};
536              
537 29 50       59 return '' unless $val; # x.690 8.5.2
538 29 50       118 return "\x40" if $val eq 'inf'; # x.690 8.5.8
539 29 50       86 return "\x41" if $val eq '-inf'; # x.690 8.5.8
540              
541             # POSIX required. available?
542 29         34 eval {
543 29         916 require POSIX;
544             };
545 29 50       7073 return $me->error("POSIX not available. cannot encode type real")
546             unless defined &POSIX::frexp;
547              
548 29         38 my $sign = 0;
549 29         80 my($mant, $exp) = POSIX::frexp($val);
550 29 100       81 if( $mant < 0 ){
551 7         9 $sign = 1;
552 7         11 $mant = - $mant;
553             }
554              
555             #$me->debug("encode real: $mant ^ $exp");
556            
557             # go byte-by-byte
558 29         31 my @mant;
559 29         61 while($mant > 0){
560 107         253 my($frac, $int) = POSIX::modf(POSIX::ldexp($mant, 8));
561 107         139 push @mant, $int;
562 107         106 $mant = $frac;
563 107         228 $exp -= 8;
564             # $me->debug("encode real: [@mant] ^ $exp");
565             }
566             #$me->debug("encode real: [@mant] ^ $exp");
567              
568 29 50 33     111 if( $data->{flavor} || $me->{flavor} ){
569             # x.690 8.5.6.5, 11.3.1 - CER + DER require N has a 1 in the lsb
570             # normalize
571 29         74 while( ! ($mant[-1] & 1) ){
572             # shift right
573 154         163 my $c = 0;
574 154         251 for (@mant){
575 424         478 my $l = $_ & 1;
576 424 100       655 $_ = ($_>>1) | ($c?0x80:0);
577 424         541 $c = $l;
578             }
579 154         308 $exp ++;
580             }
581             #$me->debug("encode real normalized: [@mant] ^ $exp");
582             }
583              
584             # encode exp
585 29         31 my @exp;
586 29 100       53 my $exps = ($exp < 0) ? 0xff : 0;
587 29         31 while(1){
588 33         49 unshift @exp, $exp & 0xFF;
589 33 100 100     141 last if $exp >= -128 && $exp < 128;
590             # >>= does not preserve sign.
591 4         10 $exp = int(($exp - $exps)/256);
592             }
593            
594 29         147 $me->debug("encode real: [@mant] ^ [@exp]");
595              
596 29 100       61 my $first = 0x80 | ($sign ? 0x40 : 0);
597              
598 29 100       65 if(@exp == 2){
599 4         6 $first |= 1;
600             }
601 29 50       63 if(@exp == 3){
602 0         0 $first |= 2;
603             }
604 29 50       58 if(@exp > 3){
605             # should not happen using ieee-754 doubles
606 0         0 $first |= 3;
607 0         0 unshift @exp, scalar(@exp);
608             }
609            
610 29         142 pack('C*', $first, @exp, @mant);
611             }
612              
613             sub encode_oid {
614 1     1 0 2 my $me = shift;
615 1         2 my $data = shift;
616 1         2 my $val = $data->{value};
617             # "1.3.6.1.2.0" | [1, 3, 6, 1, 2, 0]
618              
619             # x.690 8.19
620 1 50       14 my @o = ref($val) ? @$val : (split /\./, $val);
621 1 50       4 shift @o if $o[0] eq ''; # remove empty in case specified with leading .
622              
623 1 50       4 if( @o > 1 ){
624             # x.690 8.19.4
625 1         2 my $o = shift @o;
626 1         3 $o[0] += $o * 40;
627             }
628              
629 1         5 $me->debug("encode oid [@o]");
630 1         8 pack('w*', @o);
631             }
632              
633             sub encode_roid {
634 1     1 0 3 my $me = shift;
635 1         2 my $data = shift;
636 1         2 my $val = $data->{value};
637             # "1.3.6.1.2.0" | [1, 3, 6, 1, 2, 0]
638              
639             # x.690 8.20
640 1 50       6 my @o = ref($val) ? @$val : (split /\./, $val);
641 1 50       4 shift @o if $o[0] eq ''; # remove empty in case specified with leading .
642             # no special encoding of 1st 2
643              
644 1         4 $me->debug("encode relative-oid [@o]");
645 1         7 pack('w*', @o);
646             }
647              
648              
649             ################################################################
650              
651             sub encode_ident {
652 113     113 0 141 my $me = shift;
653 113         134 my $type = shift;
654 113         132 my $tnum = shift;
655              
656 113 100       240 if( $tnum < 31 ){
657 112         314 return pack('C', $type|$tnum);
658             }
659 1         2 $type |= 0x1f;
660 1         4 pack('Cw', $type, $tnum);
661             }
662              
663             sub encode_length {
664 113     113 0 127 my $me = shift;
665 113         125 my $len = shift;
666              
667 113 50       406 return pack('C', $len) if $len < 128; # x.690 8.1.3.4
668 0 0       0 return pack('CC', 0x81, $len) if $len < 1<<8; # x.690 8.1.3.5
669 0 0       0 return pack('Cn', 0x82, $len) if $len < 1<<12;
670 0 0       0 return pack('CCn',0x83, ($len>>16), ($len&0xFFFF)) if $len < 1<<16;
671 0 0       0 return pack('CN', 0x84, $len) if $len <= 0xFFFFFFFF;
672            
673             # items larger than above will be encoded in indefinite form
674 0         0 return;
675             }
676              
677             # override me in subclass
678             sub rule_check_and_apply {
679 113     113 0 139 my $me = shift;
680 113         126 my $data = shift;
681              
682 113         435 undef;
683             }
684              
685             # convert DWIM values => canonical form
686             sub canonicalize {
687 64     64 0 74 my $me = shift;
688 64         74 my $data = shift;
689            
690             # arrayref | int | float | string | undef
691              
692 64 100       127 unless( defined $data ){
693             return {
694 2         7 type => 'null',
695             value => undef,
696             };
697             }
698            
699 62 100       125 if( $me->behaves_like_an_array($data) ){
700             return {
701 3         12 type => 'sequence',
702             value => $data,
703             };
704             }
705              
706 59 100       111 if( $me->behaves_like_a_hash($data) ){
707             return {
708 1         6 type => ['application', 'constructed', 3],
709             value => [ %$data ],
710             };
711             }
712            
713 58 100       108 if( $me->smells_like_a_number($data) ){
714             return {
715 51 100       168 type => ( int($data) == $data ? 'integer' : 'real'),
716             value => $data,
717             };
718             }
719              
720             # call it a string
721             return {
722 7         24 type => 'octet_string',
723             value => $data,
724             };
725             }
726              
727             # tags added via add_tag method
728             sub app_tag_data_byname {
729 264     264 0 311 my $me = shift;
730 264         307 my $name = shift;
731              
732 264         561 $me->{tags}{$name};
733             }
734              
735             # override me in subclass
736             sub subclass_tag_data_byname {
737 250     250 0 280 my $me = shift;
738 250         325 my $name = shift;
739              
740 250         391 undef;
741             }
742              
743             # from the table up top
744             sub univ_tag_data_byname {
745 250     250 0 291 my $me = shift;
746 250         283 my $name = shift;
747              
748 250 100 33     1197 $ALLTAG{$name} || ($AKATAG{$name} && $ALLTAG{$AKATAG{$name}});
749             }
750              
751             sub tag_data_byname {
752 264     264 0 313 my $me = shift;
753 264         321 my $name = shift;
754              
755 264         276 my $th;
756             # application specific tag name
757 264         550 $th = $me->app_tag_data_byname($name);
758            
759             # subclass specific tag name
760 264 100       761 $th = $me->subclass_tag_data_byname($name) unless $th;
761            
762             # universal tag name
763 264 100       702 $th = $me->univ_tag_data_byname($name) unless $th;
764              
765 264         602 $th;
766             }
767              
768             sub class_and_type_from_speclist {
769 130     130 0 150 my $me = shift;
770 130         137 my($class, $type);
771 130         235 for my $t (@_){
772 36 100       78 if( $CLASS{$t} ){ $class = $t; next }
  16         17  
  16         28  
773 20 50       44 if( $TYPE{$t} ){ $type = $t; next }
  20         19  
  20         43  
774 0         0 $me->error("unknown type specification [$t] not a class or type");
775             }
776 130         300 ($class, $type);
777             }
778              
779             sub ident_data_and_efunc {
780 113     113 0 135 my $me = shift;
781 113         134 my $typd = shift;
782 113         128 my $func = shift;
783              
784 113   50     339 $func ||= 'e';
785 113 100       283 my @t = ref($typd) ? @$typd : ($typd);
786            
787             # type: name | [class, type, name] | [class, type, num]
788             # if name resolves, specified class+type for validation only
789              
790 113         147 my $tname = pop @t;
791 113 100       247 if( $me->smells_like_a_number($tname) ){
792 2         6 my($class, $type) = $me->class_and_type_from_speclist( @t );
793 2   50     7 $class ||= 'universal';
794 2   50     12 $type ||= 'primitive';
795 2         5 my $tv = $CLASS{$class}{v} | $TYPE{$type}{v};
796 2         4 my $tm = $tname + 0;
797 2         14 $me->debug("numeric specification [@t $tname] resolved to [$class $type $tm]");
798 2         6 return ( $tv, $tm, undef );
799             }
800              
801 111         262 my $th = $me->tag_data_byname($tname);
802              
803 111 50       208 unless( $th ){
804 0         0 $me->error("unknown type [$tname]");
805             }
806 111 50       211 unless( ref $th ){
807 0         0 $me->error("programmer botch. tag data should be hashref: [$tname] => $th");
808 0         0 $th = undef;
809             }
810              
811 111         129 my( $class, $type, $rclass, $rtype, $tnum, $encf );
812              
813             # parse request
814 111         217 ($rclass, $rtype) = $me->class_and_type_from_speclist( @t );
815             # parse spec
816 111 100       282 if( my $ts = $th->{type} ){
817 17         36 ($class, $type) = $me->class_and_type_from_speclist( @$ts );
818             }
819              
820             # use these values for identifier-value
821 111   100     340 $class ||= 'universal';
822 111   100     497 $type = $rtype || $type || 'primitive';
823 111         168 $tnum = $th->{v};
824              
825 111         457 $me->debug("specificication [@t $tname] resolved to [$class $type $tname($tnum)]");
826             # warn if mismatched
827 111 50 33     306 $me->warn("specificication [$rclass $tname] resolved to [$class $tname]")
828             if $rclass && $rclass ne $class;
829            
830             # indirect via implicit to find encoding func
831 111         157 $encf = $th->{$func};
832 111 100       269 if( my $impl = $th->{implicit} ){
833             # only one level of indirection
834 15         32 $th = $me->tag_data_byname($impl);
835              
836 15 50       40 if( ref $th ){
837 15         55 $me->debug("specificication [$class $type $tname($tnum)] is implictly $impl ");
838 15   33     101 $encf ||= $th->{$func};
839             }else{
840 0         0 $me->error("programmer botch. implicit indirect not found: [$class $tname] => $impl");
841             }
842             }
843              
844 111         234 my $tv = $CLASS{$class}{v} | $TYPE{$type}{v};
845 111         364 return( $tv, $tnum, $encf );
846             }
847              
848             sub use_definite_form {
849 113     113 0 145 my $me = shift;
850 113         138 my $type = shift;
851 113         126 my $data = shift;
852            
853 113 100       316 return 1 unless $type & 0x20; # x.690 8.1.3.2 - primitive - always definite
854              
855 5   33     16 my $fl = $data->{flavor} || $me->{flavor};
856 5 100       16 return 1 unless $fl;
857 1 50       4 return 1 if $fl eq 'DER'; # x.690 10.1 - DER - always definite
858 1 50       4 return 0 if $fl eq 'CER'; # x.690 9.1 - CER + constructed - indefinite
859 0         0 1; # otherwise, prefer definite
860             }
861              
862             ################################################################
863              
864             sub behaves_like_an_array {
865 62     62 0 100 my $me = shift;
866 62         68 my $d = shift;
867              
868 62 100       171 return unless ref $d;
869 25         112 return UNIVERSAL::isa($d, 'ARRAY');
870             }
871              
872             sub behaves_like_a_hash {
873 163     163 0 201 my $me = shift;
874 163         181 my $d = shift;
875              
876 163 100       450 return unless ref $d;
877              
878             # treat as if it is a number
879 94 100       359 return if UNIVERSAL::isa($d, 'Math::BigInt');
880 52         234 return UNIVERSAL::isa($d, 'HASH');
881             }
882              
883             sub smells_like_a_number {
884 171     171 0 207 my $me = shift;
885 171         196 my $d = shift;
886              
887 171 100 66     458 return 1 if ref $d && UNIVERSAL::isa($d, 'Math::BigInt');
888             # NB: 5.00503 does not have 'no warnings';
889 150         365 local $^W = 0;
890 150         652 return ($d + 0 eq $d);
891             }
892              
893             ################################################################
894              
895             =item decode( ber )
896              
897             Decode the provided BER encoded data. returns a perl data structure.
898             [see: DECODED DATA]
899              
900             example:
901             my $data = $enc->decode( $ber );
902              
903             =cut
904             ;
905              
906             sub decode {
907 24     24 1 44 my $me = shift;
908 24         54 my $data = shift;
909              
910 24         33 $me->{level} = 0;
911 24         59 my($v, $l) = $me->decode_item($data, 0);
912 24         63 $v;
913             }
914              
915             sub decode_items {
916 2     2 0 4 my $me = shift;
917 2         3 my $data = shift;
918 2         3 my $eocp = shift;
919 2         4 my $levl = shift;
920 2         2 my @v;
921 2         3 my $tlen = 0;
922              
923 2         3 $me->{level} = $levl;
924 2         5 $me->debug("decode items[");
925 2         6 while($data){
926 8         46 my($val, $len) = $me->decode_item($data, $levl+1);
927 8         12 $tlen += $len;
928 8 50 33     45 unless( $val && defined $val->{type} ){
929             # end-of-content
930 0         0 $me->debug('end of content');
931 0 0       0 last if $eocp;
932             }
933              
934 8         12 push @v, $val;
935 8         22 $data = substr($data, $len);
936             }
937              
938 2         3 $me->{level} = $levl;
939 2         5 $me->debug(']');
940 2         4 return (\@v, $tlen);
941             }
942              
943             sub decode_item {
944 32     32 0 40 my $me = shift;
945 32         42 my $data = shift;
946 32         36 my $levl = shift;
947            
948             # hexdump($data, 'di:');
949 32         42 $me->{level} = $levl;
950 32         68 my($typval, $typlen, $typmore) = $me->decode_ident($data);
951 32         81 my($typdat, $decfnc, $pretty, $tagnum) = $me->ident_descr_and_dfuncs($typval, $typmore);
952 32         92 my($datlen, $lenlen) = $me->decode_length(substr($data,$typlen));
953 32         58 my $havlen = length($data);
954 32   100     77 my $tlen = $typlen + $lenlen + ($datlen || 0);
955 32         36 my $doff = $typlen + $lenlen;
956 32         30 my $result;
957            
958 32 50       63 $me->error("corrupt data? data appears truncated")
959             if $havlen < $tlen;
960              
961 32 100       59 if( $typval & 0x20 ){
962             # constructed
963 2         3 my $vals;
964            
965 2 50       4 if( defined $datlen ){
966             # definite
967 2         9 $me->debug("decode item: constructed definite [@$typdat($tagnum)]");
968 2         9 my($v, $t) = $me->decode_items( substr($data, $doff, $datlen), 0, $levl);
969 2         5 $me->{level} = $levl;
970 2 50       6 $me->warn("corrupt data? item len != data len ($t, $datlen)")
971             unless $t == $datlen;
972 2         2 $vals = $v;
973             }else{
974             # indefinite
975 0         0 $me->debug("decode item: constructed indefinite [@$typdat($tagnum)]");
976 0         0 my($v, $t) = $me->decode_items( substr($data, $doff), 1, $levl );
977 0         0 $me->{level} = $levl;
978 0         0 $tlen += $t;
979 0         0 $tlen += 2; # eoc
980 0         0 $vals = $v;
981             }
982 2 50       6 if( $decfnc ){
983             # constructed decode func: reassemble
984 0         0 $result = $decfnc->( $me, $vals, $typdat );
985             }else{
986 2         5 $result = {
987             value => $vals,
988             };
989             }
990             }else{
991             # primitive
992 30         30 my $ndat;
993 30 50       51 if( defined $datlen ){
994             # definite
995 30         126 $me->debug("decode item: primitive definite [@$typdat($tagnum)]");
996 30         62 $ndat = substr($data, $doff, $datlen);
997             }else{
998             # indefinite encoding of a primitive is a violation of x.690 8.1.3.2(a)
999             # warn + parse it anyway
1000 0         0 $me->debug("decode item: primitive indefinite [@$typdat($tagnum)]");
1001 0         0 $me->warn("protocol violation - indefinite encoding of primitive. see x.690 8.1.3.2(a)");
1002 0         0 my $i = index($data, "\0\0", $doff);
1003 0 0       0 if( $i == -1 ){
1004             # invalid encoding.
1005             # no eoc found.
1006             # go back to protocol school.
1007 0         0 $me->error("corrupt data - content terminator not found. see x.690 8.1.3.6, 8.1.5, et al. ");
1008 0         0 return (undef, $tlen);
1009             }
1010 0         0 my $dl = $i - $doff;
1011 0         0 $tlen += $dl;
1012 0         0 $tlen += 2; # eoc
1013 0         0 $ndat = substr($data, $doff, $dl);
1014             }
1015              
1016 30 50 33     69 unless( $typval || $typmore ){
1017             # universal-primitive-tag(0) => end-of-content
1018 0         0 return ( { }, $tlen );
1019             }
1020              
1021             # decode it
1022 30   50     58 $decfnc ||= \&decode_unknown;
1023 30         57 my $val = $decfnc->( $me, $ndat, $typdat );
1024            
1025             # format value in a special pretty way?
1026 30 50       66 if( $pretty ){
1027 0   0     0 $val = $pretty->( $me, $val ) || $val;
1028             }
1029 30         45 $result = $val;
1030             }
1031              
1032 32         49 $result->{type} = $typdat;
1033 32         53 $result->{tagnum} = $tagnum;
1034 32         48 $result->{identval} = $typval;
1035            
1036 32 50       69 if( my $c = $me->{decoded_callback} ){
1037 0   0     0 $result = $c->( $me, $result ) || $result; # make sure the brain hasn't fallen out
1038             }
1039 32         72 return( $result, $tlen );
1040             }
1041              
1042             sub app_tag_data_bynumber {
1043 32     32 0 40 my $me = shift;
1044 32         40 my $class = shift;
1045 32         38 my $tnum = shift;
1046            
1047 32         72 my $name = $me->{revtags}{$class}{$tnum};
1048 32 100       76 return unless $name;
1049              
1050 1         3 $me->{tags}{$name};
1051             }
1052              
1053             # override me in subclass
1054             sub subclass_tag_data_bynumber {
1055 31     31 0 34 my $me = shift;
1056 31         37 my $class = shift;
1057 31         32 my $tnum = shift;
1058              
1059 31         58 undef;
1060             }
1061              
1062             sub univ_tag_data_bynumber {
1063 31     31 0 33 my $me = shift;
1064 31         35 my $class = shift;
1065 31         33 my $tnum = shift;
1066              
1067 31         77 $TAG{$class}{ $REVTAG{$class}{$tnum} };
1068             }
1069              
1070             sub tag_data_bynumber {
1071 32     32 0 39 my $me = shift;
1072 32         41 my $class = shift;
1073 32         38 my $tnum = shift;
1074              
1075 32         31 my $th;
1076             # application specific tag name
1077 32         59 $th = $me->app_tag_data_bynumber($class, $tnum);
1078            
1079             # subclass specific tag name
1080 32 100       92 $th = $me->subclass_tag_data_bynumber($class, $tnum) unless $th;
1081              
1082             # from universal
1083 32 100       82 $th = $me->univ_tag_data_bynumber($class, $tnum) unless $th;
1084              
1085 32         58 $th;
1086             }
1087              
1088             sub ident_descr_and_dfuncs {
1089 32     32 0 44 my $me = shift;
1090 32         41 my $tval = shift;
1091 32         36 my $more = shift;
1092              
1093 32   50     142 my $tag = $more || ($tval & 0x1f) || 0;
1094 32         37 my $cl = $tval & 0xC0;
1095 32         34 my $ty = $tval & 0x20;
1096 32         57 my $class = $REVCLASS{$cl};
1097 32         42 my $pctyp = $REVTYPE{$ty};
1098              
1099 32         32 my( $th, $tn, $tf, $tp );
1100              
1101 32         65 $th = $me->tag_data_bynumber($class, $tag);
1102              
1103 32 50       68 if( ref $th ){
    0          
1104 32         46 $tn = $th->{n};
1105 32         45 $tp = $th->{pretty};
1106            
1107 32 50       68 if( my $impl = $th->{implicit} ){
1108             # indirect. we support only one level.
1109 0         0 my $h = $me->tag_data_byname($impl);
1110 0 0       0 if( ref $h ){
1111 0         0 $th = $h;
1112             }else{
1113 0         0 $me->error("programmer botch. implicit indirect not found: $class/$tn => $impl");
1114             }
1115             }
1116             # primitive decode func or constructed decode func?
1117 32   33     104 $tp ||= $th->{pretty};
1118 32 100       71 $tf = $ty ? $th->{dc} : $th->{d};
1119             }elsif( $th ){
1120 0         0 $me->error("programmer botch. tag data should be hashref: $class/$tag => $th");
1121             }else{
1122 0         0 $me->warn("unknown type [$class $tag]");
1123             }
1124              
1125 32 50       69 $tn = $tag unless defined $tn;
1126              
1127 32         123 $me->debug("identifier $tval/$tag resolved to [$class $pctyp $tn]");
1128             # [class, type, tagname], decodefunc, tagnumber
1129 32         126 ([$class, $pctyp, $tn], $tf, $tp, $tag);
1130             }
1131              
1132             sub decode_length {
1133 32     32 0 39 my $me = shift;
1134 32         88 my $data = shift;
1135              
1136 32         55 my($l1) = unpack('C', $data);
1137              
1138 32 50       77 unless( $l1 & 0x80 ){
1139             # x.690 8.1.3.4 - short form
1140 32         60 return ($l1, 1);
1141             }
1142 0 0       0 if( $l1 == 0x80 ){
1143             # x.690 8.1.3.6 - indefinite form
1144 0         0 return (undef, 1);
1145             }
1146              
1147             # x.690 8.1.3.5 - long form
1148 0         0 my $llen = $l1 & 0x7f;
1149 0         0 my @l = unpack("C$llen", substr($data, 1));
1150              
1151 0         0 my $len = 0;
1152 0         0 for my $l (@l){
1153 0         0 $len <<= 8;
1154 0         0 $len += $l;
1155             }
1156            
1157 0         0 ($len, $llen + 1);
1158             }
1159              
1160             sub decode_ident {
1161 32     32 0 46 my $me = shift;
1162 32         39 my $data = shift;
1163              
1164 32         66 my($tag) = unpack('C', $data);
1165 32 50       104 return ($tag, 1) unless ($tag & 0x1f) == 0x1f; # x.690 8.1.2.3
1166              
1167             # x.690 8.1.2.4 - tag numbers > 30
1168 0         0 my $i = 1;
1169 0         0 $tag &= ~0x1f;
1170 0         0 my $more = 0;
1171 0         0 while(1){
1172 0         0 my $c = unpack('C', substr($data,$i++,1));
1173 0         0 $more <<= 7;
1174 0         0 $more |= ($c & 0x7f);
1175 0 0       0 last unless $c & 0x80;
1176             }
1177              
1178 0         0 ($tag, $i, $more);
1179             }
1180              
1181             sub decode_bool {
1182 0     0 0 0 my $me = shift;
1183 0         0 my $data = shift;
1184 0         0 my $type = shift;
1185              
1186 0         0 my $v = unpack('C', $data);
1187            
1188             {
1189 0         0 value => $v,
1190             };
1191             }
1192              
1193             sub decode_null {
1194 1     1 0 2 my $me = shift;
1195 1         3 my $data = shift;
1196 1         2 my $type = shift;
1197              
1198             {
1199 1         3 value => undef,
1200             };
1201             }
1202              
1203             # reassemble constructed string
1204             sub reass_string {
1205 0     0 0 0 my $me = shift;
1206 0         0 my $vals = shift;
1207 0         0 my $type = shift;
1208              
1209 0         0 my $val = '';
1210 0         0 for my $v (@$vals){
1211 0         0 $val .= $v->{value};
1212             };
1213              
1214 0         0 $me->debug('reassemble constructed string');
1215             return {
1216 0         0 type => [ $type->[0], 'primitive', $type->[2] ],
1217             value => $val,
1218             };
1219            
1220             }
1221              
1222             sub decode_string {
1223 2     2 0 7 my $me = shift;
1224 2         3 my $data = shift;
1225 2         3 my $type = shift;
1226              
1227             {
1228 2         7 value => $data,
1229             };
1230             }
1231              
1232             sub decode_bits {
1233 0     0 0 0 my $me = shift;
1234 0         0 my $data = shift;
1235 0         0 my $type = shift;
1236              
1237 0         0 my $pad = unpack('C', $data);
1238             # QQQ - remove padding?
1239            
1240 0         0 $data = substr($data, 1);
1241            
1242             {
1243 0         0 value => $data,
1244             };
1245             }
1246              
1247             sub decode_int {
1248 4     4 0 5 my $me = shift;
1249 4         5 my $data = shift;
1250 4         5 my $type = shift;
1251              
1252 4         10 my $val = $me->part_decode_int($data, 1);
1253 4         12 $me->debug("decode integer: $val");
1254             {
1255 4         13 value => $val,
1256             };
1257             }
1258              
1259             sub decode_uint {
1260 0     0 0 0 my $me = shift;
1261 0         0 my $data = shift;
1262 0         0 my $type = shift;
1263              
1264 0         0 my $val = $me->part_decode_int($data, 0);
1265 0         0 $me->debug("decode unsigned integer: $val");
1266             {
1267 0         0 value => $val,
1268             };
1269             }
1270              
1271             sub part_decode_int {
1272 27     27 0 35 my $me = shift;
1273 27         45 my $data = shift;
1274 27         32 my $sgnd = shift;
1275              
1276 27         33 my $val;
1277             my $big;
1278 27 50 33     41 $big = 1 if _have_math_bigint() && length($data) > 4;
1279              
1280 27 50       47 if( $big ){
1281 0         0 my $sign = unpack('c', $data) < 0;
1282 0 0 0     0 if( $sgnd && $sign ){
1283             # make negative
1284 0         0 $val = Math::BigInt->new('0x' . unpack('H*', pack('C*', map {~$_ & 0xff} unpack('C*', $data))));
  0         0  
1285 0         0 $val->bneg()->bsub(1);
1286             }else{
1287 0         0 $val = Math::BigInt->new('0x' . unpack('H*', $data));
1288             }
1289            
1290             }else{
1291 27 50       62 $val = unpack(($sgnd ? 'c' : 'C'), $data);
1292 27         54 my @o = unpack('C*', $data);
1293 27         37 shift @o;
1294 27         55 for my $i (@o){
1295 4         5 $val *= 256;
1296 4         9 $val += $i;
1297             }
1298             }
1299              
1300 27         48 $val;
1301             }
1302              
1303             sub decode_real {
1304 23     23 0 27 my $me = shift;
1305 23         32 my $data = shift;
1306 23         26 my $type = shift;
1307              
1308 23         46 $me->debug('decode real');
1309 23 50       43 return { value => 0.0 } unless $data;
1310              
1311             # POSIX required. available?
1312 23         28 eval {
1313 23         91 require POSIX;
1314             };
1315 23 50       47 return $me->error("POSIX not available. cannot decode type real")
1316             unless defined &POSIX::frexp;
1317              
1318 23         38 my $first = unpack('C', $data);
1319 23 50       49 return { value => POSIX::HUGE_VAL() } if $first == 0x40;
1320 23 50       39 return { value => - POSIX::HUGE_VAL() } if $first == 0x41;
1321              
1322 23 50       44 if( $first & 0x80 ){
1323             # binary encoding
1324 23 100       38 my $sign = ($first & 0x40) ? -1 : 1;
1325 23         35 my $base = ($first & 0x30) >> 4;
1326 23         54 my $scal = [0, 1, -2, -1]->[($first & 0x0C) >> 2];
1327 23         46 my $expl = ($first & 0x03) + 1;
1328              
1329 23         28 $data = substr($data, 1);
1330              
1331 23 50       46 if( $expl == 4 ){
1332 0         0 $expl = unpack('C', $data);
1333 0         0 $data = substr($data, 1);
1334             }
1335              
1336 23         59 my $exp = $me->part_decode_int( substr($data, 0, $expl), 1 );
1337 23         46 $data = substr($data, $expl);
1338 23         45 my @mant = unpack('C*', $data);
1339 23         106 $me->debug("decode real: [@mant] $exp");
1340              
1341             # apply scale factor
1342 23 50       53 $exp *= 3 if $base == 1;
1343 23 50       39 $exp *= 4 if $base == 2;
1344 23 50       48 $me->error('corrupt data: invalid base for real') if $base == 3;
1345 23         26 $exp += $scal;
1346              
1347             # put it together
1348 23         25 my $val = 0;
1349 23         35 $exp += (@mant - 1) * 8;
1350 23         34 for my $m (@mant){
1351 101         209 $val += POSIX::ldexp($m, $exp);
1352             # $me->debug("decode real: $val ($m, $exp)");
1353 101         131 $exp -= 8;
1354             }
1355 23         29 $val *= $sign;
1356            
1357 23         99 $me->debug("decode real: => $val");
1358 23         84 return { value => $val };
1359             }else{
1360             # decimal encoding
1361             # x.690 8.5.7 - see iso-6093
1362 0         0 $me->debug('decode real decimal');
1363 0         0 $data = substr($data, 1);
1364 0         0 $data =~ s/^([+-]?)0+/$1/; # remove leading 0s
1365 0         0 $data =~ s/\s//g; # remove spaces
1366 0         0 $data += 0; # make number
1367            
1368 0         0 return { value => $data };
1369             }
1370            
1371             }
1372              
1373             sub decode_oid {
1374 0     0 0 0 my $me = shift;
1375 0         0 my $data = shift;
1376 0         0 my $type = shift;
1377              
1378 0         0 my @o = unpack('w*', $data);
1379            
1380 0 0       0 if( $o[0] < 40 ){
    0          
1381 0         0 unshift @o, 0;
1382             }elsif( $o[0] < 80 ){
1383 0         0 $o[0] -= 40;
1384 0         0 unshift @o, 1;
1385             }else{
1386 0         0 $o[0] -= 80;
1387 0         0 unshift @o, 2;
1388             }
1389              
1390 0         0 my $val = join('.', @o);
1391 0         0 $me->debug("decode oid: $val");
1392            
1393             {
1394 0         0 value => $val,
1395             };
1396             }
1397              
1398             sub decode_roid {
1399 0     0 0 0 my $me = shift;
1400 0         0 my $data = shift;
1401 0         0 my $type = shift;
1402              
1403 0         0 my @o = unpack('w*', $data);
1404            
1405 0         0 my $val = join('.', @o);
1406 0         0 $me->debug("decode relative-oid: $val");
1407            
1408             {
1409 0         0 value => $val,
1410             };
1411             }
1412              
1413             sub decode_unknown {
1414 0     0 0 0 my $me = shift;
1415 0         0 my $data = shift;
1416 0         0 my $type = shift;
1417              
1418 0         0 $me->debug("decode unknown");
1419             {
1420 0         0 value => $data,
1421             };
1422             }
1423              
1424             sub _have_math_bigint {
1425              
1426 87 100   87   230 return unless defined &Math::BigInt::new;
1427 56 50       100 return unless defined &Math::BigInt::is_neg;
1428              
1429 56         122 1;
1430             }
1431            
1432             ################################################################
1433              
1434             sub hexdump {
1435 0     0 0 0 my $b = shift;
1436 0         0 my $tag = shift;
1437 0         0 my( $l, $t );
1438              
1439 0 0       0 print STDERR "$tag:\n" if $tag;
1440 0         0 while( $b ){
1441 0         0 $t = $l = substr($b, 0, 16, '');
1442 0         0 $l =~ s/(.)/sprintf('%0.2X ',ord($1))/ges;
  0         0  
1443 0         0 $l =~ s/(.{24})/$1 /;
1444 0         0 $t =~ s/[[:^print:]]/./gs;
1445 0         0 my $p = ' ' x (49 - (length $l));
1446 0         0 print STDERR " $l $p$t\n";
1447             }
1448             }
1449              
1450             sub import {
1451 4     4   21 my $pkg = shift;
1452 4         9 my $caller = caller;
1453              
1454 4         4142 for my $f (@_){
1455 3     3   21 no strict;
  3         5  
  3         505  
1456 0           my $fnc = $pkg->can($f);
1457 0 0         next unless $fnc;
1458 0           *{$caller . '::' . $f} = $fnc;
  0            
1459             }
1460             }
1461              
1462             =back
1463              
1464             =head1 ENCODING DATA
1465              
1466             You can give data to the encoder in either of two ways (or mix and match).
1467              
1468             You can specify simple values directly, and the module will guess the
1469             correct tags to use. Things that look like integers will be encoded as
1470             C, things that look like floating-point numbers will be encoded
1471             as C, things that look like strings, will be encoded as C.
1472             Arrayrefs will be encoded as C.
1473              
1474             example:
1475             $enc->encode( [0, 1.2, "foobar", [ "baz", 37.94 ]] );
1476              
1477             Alternatively, you can explicity specify the type using a hashref
1478             containing C and C keys.
1479              
1480             example:
1481             $enc->encode( { type => 'sequence',
1482             value => [
1483             { type => 'integer',
1484             value => 37 } ] } );
1485              
1486             The type may be specfied as either a string containg the tag-name, or
1487             as an arryref containing the class, type, and tag-name.
1488              
1489             example:
1490             type => 'octet_string'
1491             type => ['universal', 'primitive', 'octet_string']
1492              
1493             Note: using the second form above, you can create wacky encodings
1494             that no one will be able to decode.
1495            
1496             The value should be a scalar value for primitive types, and an
1497             arrayref for constructed types.
1498              
1499             example:
1500             { type => 'octet_string', value => 'foobar' }
1501             { type => 'set', value => [ 1, 2, 3 ] }
1502              
1503             { type => ['universal', 'constructed', 'octet_string'],
1504             value => [ 'foo', 'bar' ] }
1505              
1506             =head1 DECODED DATA
1507              
1508             The values returned from decoding will be similar to the way data to
1509             be encoded is specified, in the full long form. Additionally, the hashref
1510             will contain: C the numeric value representing the class+type+tag
1511             and C the numeric tag number.
1512              
1513             example:
1514             a string might be returned as:
1515             { type => ['universal', 'primitive', 'octet_string'],
1516             identval => 4,
1517             tagnum => 4,
1518             value => 'foobar',
1519             }
1520              
1521              
1522             =head1 TAG NAMES
1523              
1524             The following are recognized as valid names of tags:
1525              
1526             bit_string bmp_string bool boolean character_string embedded_pdv
1527             enum enumerated external float general_string generalized_time
1528             graphic_string ia5_string int int32 integer integer32 iso646_string
1529             null numeric_string object_descriptor object_identifier octet_string
1530             oid printable_string real relative_object_identifier relative_oid
1531             roid sequence sequence_of set set_of string t61_string teletex_string
1532             uint uint32 universal_string universal_time unsigned_int unsigned_int32
1533             unsigned_integer utf8_string videotex_string visible_string
1534              
1535             =head1 Math::BigInt
1536              
1537             If you have Math::BigInt, it can be used for large integers. If you want it used,
1538             you must load it yourself:
1539              
1540             use Math::BigInt;
1541             use Encoding::BER;
1542              
1543             It can be used for both encoding and decoding. The encoder can be handed either
1544             a Math::BigInt object, or a "big string of digits" marked as an integer:
1545              
1546             use math::BigInt;
1547              
1548             my $x = Math::BigInt->new( '12345678901234567890' );
1549             $enc->encode( $x )
1550              
1551             $enc->encode( { type => 'integer', '12345678901234567890' } );
1552              
1553             During decoding, a Math::BigInt object will be created if the value "looks big".
1554              
1555            
1556             =head1 EXPORTS
1557              
1558             By default, this module exports nothing. This can be overridden by specifying
1559             something else:
1560              
1561             use Encoding::BER ('import', 'hexdump');
1562              
1563             =head1 LIMITATIONS
1564              
1565             If your application uses the same tag-number for more than one type of implicitly
1566             tagged primitive, the decoder will not be able to distinguish between them, and will
1567             not be able to decode them both correctly. eg:
1568              
1569             width ::= [context 12] implicit integer
1570             girth ::= [context 12] implicit real
1571              
1572             If you specify data to be encoded using the "short form", the module may
1573             guess the type differently than you expect. If it matters, be explicit.
1574              
1575             This module does not do data validation. It will happily let you encode
1576             a non-ascii string as a C, etc.
1577              
1578            
1579             =head1 PREREQUISITES
1580              
1581             If you wish to use Cs, the POSIX module is required. It will be loaded
1582             automatically, if needed.
1583              
1584             Familiarity with ASN.1 and BER encoding is probably required to take
1585             advantage of this module.
1586              
1587             =head1 SEE ALSO
1588            
1589             Yellowstone National Park
1590             Encoding::BER::CER, Encoding::BER::DER
1591             Encoding::BER::SNMP, Encoding::BER::Dumper
1592             ITU-T x.690
1593            
1594             =head1 AUTHOR
1595              
1596             Jeff Weisberg - http://www.tcp4me.com
1597              
1598             =cut
1599             ;
1600              
1601             ################################################################
1602             1;
1603