File Coverage

lib/Encoding/BER.pm
Criterion Covered Total %
statement 509 659 77.2
branch 157 244 64.3
condition 48 86 55.8
subroutine 53 62 85.4
pod 7 57 12.2
total 774 1108 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.9 2007/03/06 02:50:10 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   4204 use vars qw($VERSION);
  3         6  
  3         494  
15             $VERSION = '1.00';
16 3     3   18 use Carp;
  3         4  
  3         437  
17 3     3   17 use strict;
  3         11  
  3         35893  
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 51074 my $cl = shift;
205 94         249 my $me = bless { @_ }, $cl;
206              
207 94         192 $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 1 my $me = shift;
224 1         2 my $msg = shift;
225              
226 1 50       5 if( my $f = $me->{warn} ){
227 1         3 $f->($me, $msg);
228             }else{
229 0         0 carp ((ref $me) . ": $msg\n");
230             }
231 1         3 undef;
232             }
233              
234             sub debug {
235 642     642 1 1588 my $me = shift;
236 642         620 my $msg = shift;
237              
238 642 50       1679 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 141 my $me = shift;
247 139         148 my $class = shift;
248 139         132 my $type = shift;
249 139         121 my $name = shift;
250 139         499 my $num = shift;
251 139         128 my $data = shift;
252              
253 139 50       263 return $me->error("invalid class: $class") unless $CLASS{$class};
254 139 50       256 return $me->error("invalid type: $type") unless $TYPE{$type};
255              
256 139         630 $data->{type} = [$class, $type];
257 139         200 $data->{v} = $num;
258 139         191 $data->{n} = $name;
259            
260             # install forward + reverse mappings
261 139         416 $me->{tags}{$name} = $data;
262 139         333 $me->{revtags}{$class}{$num} = $name;
263              
264 139         369 $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 1070 my $me = shift;
285 138         142 my $class = shift;
286 138         119 my $type = shift;
287 138         136 my $name = shift;
288 138         314 my $num = shift;
289 138         118 my $base = shift;
290              
291 138 50       231 return $me->error("unknown base tag name: $base")
292             unless $me->tag_data_byname($base);
293              
294 138         595 $me->add_tag_hash($class, $type, $name, $num, {
295             implicit => $base,
296             });
297             }
298              
299             sub add_tag {
300 1     1 0 1 my $me = shift;
301 1         2 my $class = shift;
302 1         1 my $type = shift;
303 1         1 my $name = shift;
304 1         2 my $num = shift;
305             # possibly optional:
306 1         2 my $encf = shift;
307 1         1 my $decf = shift;
308 1         2 my $encfc = shift;
309 1         1 my $decfc = shift;
310            
311 1         6 $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 8 my $TAG = shift;
321 3         3 my $ALL = shift;
322 3         7 my $REV = shift;
323            
324 3         15 for my $class (keys %$TAG){
325 6         14 for my $name (keys %{$TAG->{$class}}){
  6         34  
326 96         143 $TAG->{$class}{$name}{n} = $name;
327 96         197 $ALL->{$name} = $TAG->{$class}{$name};
328             }
329 96         321 my %d = map {
330 6         33 ($TAG->{$class}{$_}{v} => $_)
331 6         21 } keys %{$TAG->{$class}};
332 6         42 $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 115     115 1 309 my $me = shift;
350 115         299 my $data = shift;
351 115         128 my $levl = shift;
352            
353 115   100     394 $me->{level} = $levl || 0;
354 115 100 100     538 $data = $me->canonicalize($data) if $me->{acanonical} || !$me->behaves_like_a_hash($data);
355              
356             # include pre-encoded data as is
357 115 50       1438 if( $data->{type} eq 'BER_preencoded' ){
358 0         0 return $data->{value};
359             }
360            
361 115   33     228 $data = $me->rule_check_and_apply($data) || $data;
362 115         262 my($typeval, $tagnum, $encfnc) = $me->ident_data_and_efunc($data->{type});
363 115         149 my $value;
364              
365 115 100       198 if( $typeval & 0x20 ){
366 5         24 $me->debug( "encode constructed ($typeval/$tagnum) [" );
367             # constructed - recurse
368 5 50       14 my @vs = ref($data->{value}) ? @{$data->{value}} : $data->{value};
  5         16  
369 5         9 for my $e (@vs){
370 22         76 $value .= $me->encode( $e, $me->{level} + 1 );
371             }
372 5   100     25 $me->{level} = $levl || 0;
373 5         15 $me->debug("]");
374             }else{
375 110         306 $me->debug( "encode primitive ($typeval/$tagnum)" );
376            
377 110 100       270 unless( $encfnc ){
378             # try to guess encoding
379 1 50       4 my @t = ref($data->{type}) ? @{$data->{type}} : $data->{type};
  1         3  
380 1         6 $me->warn("do not know how to encode identifier [@t] ($typeval/$tagnum)");
381 1         2 $encfnc = \&encode_unknown;
382             }
383 110         191 $value = $encfnc->($me, $data);
384             }
385              
386 115         290 my $defp = $me->use_definite_form($typeval, $data);
387 115         270 my $leng = $me->encode_length(length($value));
388              
389 115         124 my $res;
390 115 100 66     417 if( $defp && defined($leng) ){
391 114         189 $me->debug("encode definite form");
392 114         202 $res = $me->encode_ident($typeval, $tagnum) . $leng . $value;
393             }else{
394 1         2 $me->debug("encode indefinite form");
395 1         3 $res = $me->encode_ident($typeval, $tagnum) . "\x80" . $value . "\x00\x00";
396             # x.690: 8.3.6.1 8.1.5
397             }
398            
399 115         226 $data->{dlen} = length($value);
400 115         155 $data->{tlen} = length($res);
401              
402 115         382 $res;
403             }
404              
405             sub encode_null {
406 2     2 0 3 my $me = shift;
407 2         6 $me->debug('encode null');
408 2         11 '';
409             }
410              
411             sub encode_unknown {
412 1     1 0 1 my $me = shift;
413 1         1 my $data = shift;
414              
415 1         4 $me->debug('encode unknown');
416 1         9 '' . $data->{value};
417             }
418              
419             sub encode_string {
420 11     11 0 16 my $me = shift;
421 11         10 my $data = shift;
422              
423             # CER splitting of long strings is handled in CER subclass
424 11         20 $me->debug('encode string');
425 11         31 '' . $data->{value};
426             }
427              
428             sub encode_bits {
429 1     1 0 1 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         2 my $data = shift;
441              
442             # x.690 11.1
443 2         5 $me->debug('encode boolean');
444 2 100       5 $data->{value} ? "\xFF" : "\x0";
445             }
446              
447             sub encode_int {
448 48     48 0 55 my $me = shift;
449 48         45 my $data = shift;
450 48         68 my $val = $data->{value};
451              
452 48         49 my @i;
453             my $big;
454              
455 48 100       126 if( defined &Math::BigInt::new ){
456             # value is a bigint or a long string
457 44 100 66     272 $big = 1 if (ref $val && $val->can('as_hex')) || length($val) > 8;
      100        
458             }
459            
460 48 100       87 if( $big ){
461 22         72 my $x = Math::BigInt->new($val);
462 22         537 $me->debug("bigint $val => $x");
463 22 100       84 my $sign = $x->is_neg() ? 0xff : 0;
464 22 100       151 if( $sign ){
465             # NB: in 2s comp: -X = ~(X-1) = ~X+1
466 9         26 $x = $x->bneg()->bsub(1)->as_hex();
467 9         2773 $x =~ s/^0x//;
468 9 100       26 $x = '0'.$x if length($x) & 1;
469 9         37 @i = map{ ~$_ & 0xff } unpack('C*', pack('H*', $x));
  31         68  
470 9 100       40 unshift @i, 0xff unless $i[0] & 0x80;
471             }else{
472 13         42 $x = $x->as_hex();
473 13         819 $x =~ s/^0x//;
474 13 100       37 $x = '0'.$x if length($x) & 1;
475 13         57 @i = unpack('C*', pack('H*', $x));
476 13 100       39 unshift @i, 0 if $i[0] & 0x80;
477             }
478 22         106 $me->debug("encode big int [@i]");
479             }else{
480 26 100       37 my $sign = ($val < 0) ? 0xff : 0;
481 26         27 while(1){
482 36         48 unshift @i, $val & 0xFF;
483 36 100 100     131 last if $val >= -128 && $val < 128;
484             # NB: >>= does not preserve sign.
485 10         16 $val = int(($val - $sign)/256);
486             }
487 26         91 $me->debug("encode int [@i]");
488             }
489 48         171 pack('C*', @i);
490             }
491              
492             sub encode_uint {
493 12     12 0 13 my $me = shift;
494 12         15 my $data = shift;
495 12         18 my $val = $data->{value};
496            
497 12         13 my @i;
498             my $big;
499            
500 12 50       28 if( defined &Math::BigInt::new ){
501             # value is a bigint or a long string
502 12 100 66     67 $big = 1 if (ref $val && $val->can('bcmp')) || length($val) > 8;
      66        
503             }
504              
505 12 100       28 if( $big ){
506 6         30 my $x = Math::BigInt->new($val)->as_hex();
507 6         238 $x =~ s/^0x//;
508 6 100       18 $x = '0' . $x if length($x) & 1;
509 6         11 $me->debug("encode big unsigned int");
510 6         19 pack('H*', $x);
511             }else{
512 6         14 while($val){
513 7         11 unshift @i, $val & 0xFF;
514 7         262 $val >>= 8;
515             }
516 6         25 $me->debug("encode unsigned int [@i]");
517 6         25 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         7 pack('N', $val);
530             }
531              
532             sub encode_real {
533 29     29 0 33 my $me = shift;
534 29         32 my $data = shift;
535 29         42 my $val = $data->{value};
536              
537 29 50       56 return '' unless $val; # x.690 8.5.2
538 29 50       128 return "\x40" if $val eq 'inf'; # x.690 8.5.8
539 29 50       95 return "\x41" if $val eq '-inf'; # x.690 8.5.8
540              
541             # POSIX required. available?
542 29         35 eval {
543 29         1104 require POSIX;
544             };
545 29 50       8388 return $me->error("POSIX not available. cannot encode type real")
546             unless defined &POSIX::frexp;
547              
548 29         31 my $sign = 0;
549 29         89 my($mant, $exp) = POSIX::frexp($val);
550 29 100       78 if( $mant < 0 ){
551 7         10 $sign = 1;
552 7         8 $mant = - $mant;
553             }
554              
555             #$me->debug("encode real: $mant ^ $exp");
556            
557             # go byte-by-byte
558 29         29 my @mant;
559 29         62 while($mant > 0){
560 107         268 my($frac, $int) = POSIX::modf(POSIX::ldexp($mant, 8));
561 107         152 push @mant, $int;
562 107         106 $mant = $frac;
563 107         219 $exp -= 8;
564             # $me->debug("encode real: [@mant] ^ $exp");
565             }
566             #$me->debug("encode real: [@mant] ^ $exp");
567              
568 29 50 33     144 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         72 while( ! ($mant[-1] & 1) ){
572             # shift right
573 154         151 my $c = 0;
574 154         209 for (@mant){
575 424         421 my $l = $_ & 1;
576 424 100       629 $_ = ($_>>1) | ($c?0x80:0);
577 424         564 $c = $l;
578             }
579 154         336 $exp ++;
580             }
581             #$me->debug("encode real normalized: [@mant] ^ $exp");
582             }
583              
584             # encode exp
585 29         29 my @exp;
586 29 100       47 my $exps = ($exp < 0) ? 0xff : 0;
587 29         39 while(1){
588 33         49 unshift @exp, $exp & 0xFF;
589 33 100 100     149 last if $exp >= -128 && $exp < 128;
590             # >>= does not preserve sign.
591 4         7 $exp = int(($exp - $exps)/256);
592             }
593            
594 29         159 $me->debug("encode real: [@mant] ^ [@exp]");
595              
596 29 100       57 my $first = 0x80 | ($sign ? 0x40 : 0);
597              
598 29 100       60 if(@exp == 2){
599 4         5 $first |= 1;
600             }
601 29 50       139 if(@exp == 3){
602 0         0 $first |= 2;
603             }
604 29 50       48 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         155 pack('C*', $first, @exp, @mant);
611             }
612              
613             sub encode_oid {
614 1     1 0 3 my $me = shift;
615 1         2 my $data = shift;
616 1         3 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       17 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       9 if( @o > 1 ){
624             # x.690 8.19.4
625 1         2 my $o = shift @o;
626 1         9 $o[0] += $o * 40;
627             }
628              
629 1         8 $me->debug("encode oid [@o]");
630 1         18 pack('w*', @o);
631             }
632              
633             sub encode_roid {
634 1     1 0 2 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         5 $me->debug("encode relative-oid [@o]");
645 1         14 pack('w*', @o);
646             }
647              
648              
649             ################################################################
650              
651             sub encode_ident {
652 115     115 0 106 my $me = shift;
653 115         116 my $type = shift;
654 115         109 my $tnum = shift;
655              
656 115 100       674 if( $tnum < 31 ){
657 114         364 return pack('C', $type|$tnum);
658             }
659 1         1 $type |= 0x1f;
660 1         3 pack('Cw', $type, $tnum);
661             }
662              
663             sub encode_length {
664 115     115 0 115 my $me = shift;
665 115         105 my $len = shift;
666              
667 115 50       379 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 115     115 0 125 my $me = shift;
680 115         129 my $data = shift;
681              
682 115         974 undef;
683             }
684              
685             # convert DWIM values => canonical form
686             sub canonicalize {
687 66     66 0 68 my $me = shift;
688 66         228 my $data = shift;
689            
690             # arrayref | int | float | string | undef
691              
692 66 100       116 unless( defined $data ){
693             return {
694 2         8 type => 'null',
695             value => undef,
696             };
697             }
698            
699 64 100       109 if( $me->behaves_like_an_array($data) ){
700             return {
701 3         15 type => 'sequence',
702             value => $data,
703             };
704             }
705              
706 61 100       102 if( $me->behaves_like_a_hash($data) ){
707             return {
708 1         6 type => ['application', 'constructed', 3],
709             value => [ %$data ],
710             };
711             }
712            
713 60 100       99 if( $me->smells_like_a_number($data) ){
714             return {
715 51 100       270 type => ( int($data) == $data ? 'integer' : 'real'),
716             value => $data,
717             };
718             }
719              
720             # call it a string
721             return {
722 9         29 type => 'octet_string',
723             value => $data,
724             };
725             }
726              
727             # tags added via add_tag method
728             sub app_tag_data_byname {
729 266     266 0 1720 my $me = shift;
730 266         372 my $name = shift;
731              
732 266         773 $me->{tags}{$name};
733             }
734              
735             # override me in subclass
736             sub subclass_tag_data_byname {
737 252     252 0 428 my $me = shift;
738 252         272 my $name = shift;
739              
740 252         474 undef;
741             }
742              
743             # from the table up top
744             sub univ_tag_data_byname {
745 252     252 0 234 my $me = shift;
746 252         399 my $name = shift;
747              
748 252 100 33     3178 $ALLTAG{$name} || ($AKATAG{$name} && $ALLTAG{$AKATAG{$name}});
749             }
750              
751             sub tag_data_byname {
752 266     266 0 285 my $me = shift;
753 266         245 my $name = shift;
754              
755 266         238 my $th;
756             # application specific tag name
757 266         417 $th = $me->app_tag_data_byname($name);
758            
759             # subclass specific tag name
760 266 100       663 $th = $me->subclass_tag_data_byname($name) unless $th;
761            
762             # universal tag name
763 266 100       665 $th = $me->univ_tag_data_byname($name) unless $th;
764              
765 266         647 $th;
766             }
767              
768             sub class_and_type_from_speclist {
769 132     132 0 187 my $me = shift;
770 132         114 my($class, $type);
771 132         229 for my $t (@_){
772 36 100       83 if( $CLASS{$t} ){ $class = $t; next }
  16         15  
  16         34  
773 20 50       42 if( $TYPE{$t} ){ $type = $t; next }
  20         18  
  20         40  
774 0         0 $me->error("unknown type specification [$t] not a class or type");
775             }
776 132         311 ($class, $type);
777             }
778              
779             sub ident_data_and_efunc {
780 115     115 0 116 my $me = shift;
781 115         117 my $typd = shift;
782 115         131 my $func = shift;
783              
784 115   50     706 $func ||= 'e';
785 115 100       269 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 115         139 my $tname = pop @t;
791 115 100       219 if( $me->smells_like_a_number($tname) ){
792 2         6 my($class, $type) = $me->class_and_type_from_speclist( @t );
793 2   50     6 $class ||= 'universal';
794 2   50     13 $type ||= 'primitive';
795 2         7 my $tv = $CLASS{$class}{v} | $TYPE{$type}{v};
796 2         4 my $tm = $tname + 0;
797 2         21 $me->debug("numeric specification [@t $tname] resolved to [$class $type $tm]");
798 2         7 return ( $tv, $tm, undef );
799             }
800              
801 113         243 my $th = $me->tag_data_byname($tname);
802              
803 113 50       207 unless( $th ){
804 0         0 $me->error("unknown type [$tname]");
805             }
806 113 50       214 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 113         112 my( $class, $type, $rclass, $rtype, $tnum, $encf );
812              
813             # parse request
814 113         197 ($rclass, $rtype) = $me->class_and_type_from_speclist( @t );
815             # parse spec
816 113 100       281 if( my $ts = $th->{type} ){
817 17         35 ($class, $type) = $me->class_and_type_from_speclist( @$ts );
818             }
819              
820             # use these values for identifier-value
821 113   100     330 $class ||= 'universal';
822 113   100     483 $type = $rtype || $type || 'primitive';
823 113         157 $tnum = $th->{v};
824              
825 113         484 $me->debug("specificication [@t $tname] resolved to [$class $type $tname($tnum)]");
826             # warn if mismatched
827 113 50 33     247 $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 113         158 $encf = $th->{$func};
832 113 100       225 if( my $impl = $th->{implicit} ){
833             # only one level of indirection
834 15         25 $th = $me->tag_data_byname($impl);
835              
836 15 50       29 if( ref $th ){
837 15         48 $me->debug("specificication [$class $type $tname($tnum)] is implictly $impl ");
838 15   33     55 $encf ||= $th->{$func};
839             }else{
840 0         0 $me->error("programmer botch. implicit indirect not found: [$class $tname] => $impl");
841             }
842             }
843              
844 113         267 my $tv = $CLASS{$class}{v} | $TYPE{$type}{v};
845 113         463 return( $tv, $tnum, $encf );
846             }
847              
848             sub use_definite_form {
849 115     115 0 128 my $me = shift;
850 115         119 my $type = shift;
851 115         120 my $data = shift;
852            
853 115 100       293 return 1 unless $type & 0x20; # x.690 8.1.3.2 - primitive - always definite
854              
855 5   66     22 my $fl = $data->{flavor} || $me->{flavor};
856 5 100       31 return 1 unless $fl;
857 1 50       5 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 64     64 0 60 my $me = shift;
866 64         66 my $d = shift;
867              
868 64 100       322 return unless ref $d;
869 25         687 return UNIVERSAL::isa($d, 'ARRAY');
870             }
871              
872             sub behaves_like_a_hash {
873 165     165 0 175 my $me = shift;
874 165         341 my $d = shift;
875              
876 165 100       628 return unless ref $d;
877              
878             # treat as if it is a number
879 94 100       379 return if UNIVERSAL::isa($d, 'Math::BigInt');
880 52         269 return UNIVERSAL::isa($d, 'HASH');
881             }
882              
883             sub smells_like_a_number {
884 175     175 0 552 my $me = shift;
885 175         384 my $d = shift;
886              
887 175 100 66     1992 return 1 if ref $d && UNIVERSAL::isa($d, 'Math::BigInt');
888             # NB: 5.00503 does not have 'no warnings';
889 154         418 local $^W = 0;
890 154         1530 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 27 my $me = shift;
908 24         66 my $data = shift;
909              
910 24         31 $me->{level} = 0;
911 24         52 my($v, $l) = $me->decode_item($data, 0);
912 24         72 $v;
913             }
914              
915             sub decode_items {
916 2     2 0 2 my $me = shift;
917 2         6 my $data = shift;
918 2         4 my $eocp = shift;
919 2         2 my $levl = shift;
920 2         3 my @v;
921 2         2 my $tlen = 0;
922              
923 2         3 $me->{level} = $levl;
924 2         11 $me->debug("decode items[");
925 2         5 while($data){
926 10         39 my($val, $len) = $me->decode_item($data, $levl+1);
927 10         11 $tlen += $len;
928 10 50 33     49 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 10         12 push @v, $val;
935 10         27 $data = substr($data, $len);
936             }
937              
938 2         3 $me->{level} = $levl;
939 2         5 $me->debug(']');
940 2         11 return (\@v, $tlen);
941             }
942              
943             sub decode_item {
944 34     34 0 40 my $me = shift;
945 34         39 my $data = shift;
946 34         30 my $levl = shift;
947            
948             # hexdump($data, 'di:');
949 34         77 $me->{level} = $levl;
950 34         66 my($typval, $typlen, $typmore) = $me->decode_ident($data);
951 34         74 my($typdat, $decfnc, $pretty, $tagnum) = $me->ident_descr_and_dfuncs($typval, $typmore);
952 34         104 my($datlen, $lenlen) = $me->decode_length(substr($data,$typlen));
953 34         55 my $havlen = length($data);
954 34   100     79 my $tlen = $typlen + $lenlen + ($datlen || 0);
955 34         35 my $doff = $typlen + $lenlen;
956 34         32 my $result;
957            
958 34 50       63 $me->error("corrupt data? data appears truncated")
959             if $havlen < $tlen;
960              
961 34 100       65 if( $typval & 0x20 ){
962             # constructed
963 2         3 my $vals;
964            
965 2 50       4 if( defined $datlen ){
966             # definite
967 2         16 $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         4 $me->{level} = $levl;
970 2 50       5 $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       7 if( $decfnc ){
983             # constructed decode func: reassemble
984 0         0 $result = $decfnc->( $me, $vals, $typdat );
985             }else{
986 2         4 $result = {
987             value => $vals,
988             };
989             }
990             }else{
991             # primitive
992 32         30 my $ndat;
993 32 50       48 if( defined $datlen ){
994             # definite
995 32         117 $me->debug("decode item: primitive definite [@$typdat($tagnum)]");
996 32         54 $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 32 50 33     67 unless( $typval || $typmore ){
1017             # universal-primitive-tag(0) => end-of-content
1018 0         0 return ( { }, $tlen );
1019             }
1020              
1021             # decode it
1022 32   50     60 $decfnc ||= \&decode_unknown;
1023 32         62 my $val = $decfnc->( $me, $ndat, $typdat );
1024            
1025             # format value in a special pretty way?
1026 32 50       77 if( $pretty ){
1027 0   0     0 $val = $pretty->( $me, $val ) || $val;
1028             }
1029 32         37 $result = $val;
1030             }
1031              
1032 34         54 $result->{type} = $typdat;
1033 34         53 $result->{tagnum} = $tagnum;
1034 34         46 $result->{identval} = $typval;
1035            
1036 34 50       73 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 34         81 return( $result, $tlen );
1040             }
1041              
1042             sub app_tag_data_bynumber {
1043 34     34 0 30 my $me = shift;
1044 34         34 my $class = shift;
1045 34         41 my $tnum = shift;
1046            
1047 34         83 my $name = $me->{revtags}{$class}{$tnum};
1048 34 100       92 return unless $name;
1049              
1050 1         3 $me->{tags}{$name};
1051             }
1052              
1053             # override me in subclass
1054             sub subclass_tag_data_bynumber {
1055 33     33 0 36 my $me = shift;
1056 33         41 my $class = shift;
1057 33         30 my $tnum = shift;
1058              
1059 33         47 undef;
1060             }
1061              
1062             sub univ_tag_data_bynumber {
1063 33     33 0 35 my $me = shift;
1064 33         39 my $class = shift;
1065 33         31 my $tnum = shift;
1066              
1067 33         97 $TAG{$class}{ $REVTAG{$class}{$tnum} };
1068             }
1069              
1070             sub tag_data_bynumber {
1071 34     34 0 35 my $me = shift;
1072 34         41 my $class = shift;
1073 34         31 my $tnum = shift;
1074              
1075 34         28 my $th;
1076             # application specific tag name
1077 34         65 $th = $me->app_tag_data_bynumber($class, $tnum);
1078            
1079             # subclass specific tag name
1080 34 100       96 $th = $me->subclass_tag_data_bynumber($class, $tnum) unless $th;
1081              
1082             # from universal
1083 34 100       97 $th = $me->univ_tag_data_bynumber($class, $tnum) unless $th;
1084              
1085 34         55 $th;
1086             }
1087              
1088             sub ident_descr_and_dfuncs {
1089 34     34 0 41 my $me = shift;
1090 34         35 my $tval = shift;
1091 34         34 my $more = shift;
1092              
1093 34   50     143 my $tag = $more || ($tval & 0x1f) || 0;
1094 34         38 my $cl = $tval & 0xC0;
1095 34         34 my $ty = $tval & 0x20;
1096 34         50 my $class = $REVCLASS{$cl};
1097 34         50 my $pctyp = $REVTYPE{$ty};
1098              
1099 34         29 my( $th, $tn, $tf, $tp );
1100              
1101 34         75 $th = $me->tag_data_bynumber($class, $tag);
1102              
1103 34 50       68 if( ref $th ){
    0          
1104 34         52 $tn = $th->{n};
1105 34         42 $tp = $th->{pretty};
1106            
1107 34 50       66 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 34   33     121 $tp ||= $th->{pretty};
1118 34 100       74 $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 34 50       63 $tn = $tag unless defined $tn;
1126              
1127 34         125 $me->debug("identifier $tval/$tag resolved to [$class $pctyp $tn]");
1128             # [class, type, tagname], decodefunc, tagnumber
1129 34         159 ([$class, $pctyp, $tn], $tf, $tp, $tag);
1130             }
1131              
1132             sub decode_length {
1133 34     34 0 40 my $me = shift;
1134 34         68 my $data = shift;
1135              
1136 34         57 my($l1) = unpack('C', $data);
1137              
1138 34 50       71 unless( $l1 & 0x80 ){
1139             # x.690 8.1.3.4 - short form
1140 34         66 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 34     34 0 35 my $me = shift;
1162 34         43 my $data = shift;
1163              
1164 34         63 my($tag) = unpack('C', $data);
1165 34 50       110 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 1 my $me = shift;
1195 1         2 my $data = shift;
1196 1         2 my $type = shift;
1197              
1198             {
1199 1         2 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 4     4 0 5 my $me = shift;
1224 4         5 my $data = shift;
1225 4         3 my $type = shift;
1226              
1227             {
1228 4         12 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         4 my $data = shift;
1250 4         4 my $type = shift;
1251              
1252 4         9 my $val = $me->part_decode_int($data, 1);
1253 4         12 $me->debug("decode integer: $val");
1254             {
1255 4         11 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 30 my $me = shift;
1273 27         50 my $data = shift;
1274 27         32 my $sgnd = shift;
1275              
1276 27         34 my $val;
1277             my $big;
1278 27 50 33     79 $big = 1 if defined &Math::BigInt::new && length($data) > 4;
1279              
1280 27 50       36 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       58 $val = unpack(($sgnd ? 'c' : 'C'), $data);
1292 27         58 my @o = unpack('C*', $data);
1293 27         28 shift @o;
1294 27         58 for my $i (@o){
1295 4         5 $val *= 256;
1296 4         10 $val += $i;
1297             }
1298             }
1299              
1300 27         48 $val;
1301             }
1302              
1303             sub decode_real {
1304 23     23 0 28 my $me = shift;
1305 23         24 my $data = shift;
1306 23         24 my $type = shift;
1307              
1308 23         43 $me->debug('decode real');
1309 23 50       37 return { value => 0.0 } unless $data;
1310              
1311             # POSIX required. available?
1312 23         25 eval {
1313 23         105 require POSIX;
1314             };
1315 23 50       61 return $me->error("POSIX not available. cannot decode type real")
1316             unless defined &POSIX::frexp;
1317              
1318 23         42 my $first = unpack('C', $data);
1319 23 50       41 return { value => POSIX::HUGE_VAL() } if $first == 0x40;
1320 23 50       45 return { value => - POSIX::HUGE_VAL() } if $first == 0x41;
1321              
1322 23 50       38 if( $first & 0x80 ){
1323             # binary encoding
1324 23 100       39 my $sign = ($first & 0x40) ? -1 : 1;
1325 23         42 my $base = ($first & 0x30) >> 4;
1326 23         67 my $scal = [0, 1, -2, -1]->[($first & 0x0C) >> 2];
1327 23         42 my $expl = ($first & 0x03) + 1;
1328              
1329 23         35 $data = substr($data, 1);
1330              
1331 23 50       45 if( $expl == 4 ){
1332 0         0 $expl = unpack('C', $data);
1333 0         0 $data = substr($data, 1);
1334             }
1335              
1336 23         60 my $exp = $me->part_decode_int( substr($data, 0, $expl), 1 );
1337 23         44 $data = substr($data, $expl);
1338 23         52 my @mant = unpack('C*', $data);
1339 23         108 $me->debug("decode real: [@mant] $exp");
1340              
1341             # apply scale factor
1342 23 50       514 $exp *= 3 if $base == 1;
1343 23 50       45 $exp *= 4 if $base == 2;
1344 23 50       40 $me->error('corrupt data: invalid base for real') if $base == 3;
1345 23         26 $exp += $scal;
1346              
1347             # put it together
1348 23         23 my $val = 0;
1349 23         35 $exp += (@mant - 1) * 8;
1350 23         35 for my $m (@mant){
1351 101         168 $val += POSIX::ldexp($m, $exp);
1352             # $me->debug("decode real: $val ($m, $exp)");
1353 101         130 $exp -= 8;
1354             }
1355 23         34 $val *= $sign;
1356            
1357 23         104 $me->debug("decode real: => $val");
1358 23         91 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             ################################################################
1425              
1426             sub hexdump {
1427 0     0 0 0 my $b = shift;
1428 0         0 my $tag = shift;
1429 0         0 my( $l, $t );
1430              
1431 0 0       0 print STDERR "$tag:\n" if $tag;
1432 0         0 while( $b ){
1433 0         0 $t = $l = substr($b, 0, 16, '');
1434 0         0 $l =~ s/(.)/sprintf('%0.2X ',ord($1))/ges;
  0         0  
1435 0         0 $l =~ s/(.{24})/$1 /;
1436 0         0 $t =~ s/[[:^print:]]/./gs;
1437 0         0 my $p = ' ' x (49 - (length $l));
1438 0         0 print STDERR " $l $p$t\n";
1439             }
1440             }
1441              
1442             sub import {
1443 4     4   40 my $pkg = shift;
1444 4         12 my $caller = caller;
1445              
1446 4         6822 for my $f (@_){
1447 3     3   150 no strict;
  3         8  
  3         1087  
1448 0           my $fnc = $pkg->can($f);
1449 0 0         next unless $fnc;
1450 0           *{$caller . '::' . $f} = $fnc;
  0            
1451             }
1452             }
1453              
1454             =back
1455              
1456             =head1 ENCODING DATA
1457              
1458             You can give data to the encoder in either of two ways (or mix and match).
1459              
1460             You can specify simple values directly, and the module will guess the
1461             correct tags to use. Things that look like integers will be encoded as
1462             C, things that look like floating-point numbers will be encoded
1463             as C, things that look like strings, will be encoded as C.
1464             Arrayrefs will be encoded as C.
1465              
1466             example:
1467             $enc->encode( [0, 1.2, "foobar", [ "baz", 37.94 ]] );
1468              
1469             Alternatively, you can explicity specify the type using a hashref
1470             containing C and C keys.
1471              
1472             example:
1473             $enc->encode( { type => 'sequence',
1474             value => [
1475             { type => 'integer',
1476             value => 37 } ] } );
1477              
1478             The type may be specfied as either a string containg the tag-name, or
1479             as an arryref containing the class, type, and tag-name.
1480              
1481             example:
1482             type => 'octet_string'
1483             type => ['universal', 'primitive', 'octet_string']
1484              
1485             Note: using the second form above, you can create wacky encodings
1486             that no one will be able to decode.
1487            
1488             The value should be a scalar value for primitive types, and an
1489             arrayref for constructed types.
1490              
1491             example:
1492             { type => 'octet_string', value => 'foobar' }
1493             { type => 'set', value => [ 1, 2, 3 ] }
1494              
1495             { type => ['universal', 'constructed', 'octet_string'],
1496             value => [ 'foo', 'bar' ] }
1497              
1498             =head1 DECODED DATA
1499              
1500             The values returned from decoding will be similar to the way data to
1501             be encoded is specified, in the full long form. Additionally, the hashref
1502             will contain: C the numeric value representing the class+type+tag
1503             and C the numeric tag number.
1504              
1505             example:
1506             a string might be returned as:
1507             { type => ['universal', 'primitive', 'octet_string'],
1508             identval => 4,
1509             tagnum => 4,
1510             value => 'foobar',
1511             }
1512              
1513              
1514             =head1 TAG NAMES
1515              
1516             The following are recognized as valid names of tags:
1517              
1518             bit_string bmp_string bool boolean character_string embedded_pdv
1519             enum enumerated external float general_string generalized_time
1520             graphic_string ia5_string int int32 integer integer32 iso646_string
1521             null numeric_string object_descriptor object_identifier octet_string
1522             oid printable_string real relative_object_identifier relative_oid
1523             roid sequence sequence_of set set_of string t61_string teletex_string
1524             uint uint32 universal_string universal_time unsigned_int unsigned_int32
1525             unsigned_integer utf8_string videotex_string visible_string
1526              
1527             =head1 Math::BigInt
1528              
1529             If you have Math::BigInt, it can be used for large integers. If you want it used,
1530             you must load it yourself:
1531              
1532             use Math::BigInt;
1533             use Encoding::BER;
1534              
1535             It can be used for both encoding and decoding. The encoder can be handed either
1536             a Math::BigInt object, or a "big string of digits" marked as an integer:
1537              
1538             use math::BigInt;
1539              
1540             my $x = Math::BigInt->new( '12345678901234567890' );
1541             $enc->encode( $x )
1542              
1543             $enc->encode( { type => 'integer', '12345678901234567890' } );
1544              
1545             During decoding, a Math::BigInt object will be created if the value "looks big".
1546              
1547            
1548             =head1 EXPORTS
1549              
1550             By default, this module exports nothing. This can be overridden by specifying
1551             something else:
1552              
1553             use Encoding::BER ('import', 'hexdump');
1554              
1555             =head1 LIMITATIONS
1556              
1557             If your application uses the same tag-number for more than one type of implicitly
1558             tagged primitive, the decoder will not be able to distinguish between them, and will
1559             not be able to decode them both correctly. eg:
1560              
1561             width ::= [context 12] implicit integer
1562             girth ::= [context 12] implicit real
1563              
1564             If you specify data to be encoded using the "short form", the module may
1565             guess the type differently than you expect. If it matters, be explicit.
1566              
1567             This module does not do data validation. It will happily let you encode
1568             a non-ascii string as a C, etc.
1569              
1570            
1571             =head1 PREREQUISITES
1572              
1573             If you wish to use Cs, the POSIX module is required. It will be loaded
1574             automatically, if needed.
1575              
1576             Familiarity with ASN.1 and BER encoding is probably required to take
1577             advantage of this module.
1578              
1579             =head1 SEE ALSO
1580            
1581             Yellowstone National Park
1582             Encoding::BER::CER, Encoding::BER::DER
1583             Encoding::BER::SNMP, Encoding::BER::Dumper
1584             ITU-T x.690
1585            
1586             =head1 AUTHOR
1587              
1588             Jeff Weisberg - http://www.tcp4me.com
1589              
1590             =cut
1591             ;
1592              
1593             ################################################################
1594             1;
1595