File Coverage

blib/lib/VOMS/Lite/ASN1Helper.pm
Criterion Covered Total %
statement 118 138 85.5
branch 41 68 60.2
condition 6 9 66.6
subroutine 14 15 93.3
pod 0 10 0.0
total 179 240 74.5


line stmt bran cond sub pod time code
1             package VOMS::Lite::ASN1Helper;
2              
3 1     1   19 use 5.004;
  1         4  
  1         49  
4 1     1   5 use strict;
  1         2  
  1         31  
5 1     1   6 use warnings;
  1         1  
  1         49  
6 1     1   1809 use Math::BigInt lib => 'GMP';
  1         25973  
  1         7  
7              
8             require Exporter;
9 1     1   28520 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         1976  
10             @ISA = qw(Exporter);
11             %EXPORT_TAGS = ( );
12             @EXPORT_OK = qw( Hex DecToHex ASN1BitStr ASN1Wrap ASN1UnwrapHex ASNLenStr ASN1Index ASN1Unwrap ASN1OIDtoOID OIDtoASN1OID);
13             @EXPORT = ( );
14             $VERSION = '0.20';
15              
16             ##################################################
17              
18             sub Hex { #Converts a binary string to a padded string of hex values
19 89     89 0 164 my $data=shift;
20 89 50       169 return undef if ( ! defined $data );
21 89         231 my $str=unpack('H*',$data);
22 89 50       431 return ((length($str) & 1)?"0":"").$str;
23             }
24              
25             ##################################################
26              
27             sub DecToHex { #Converts an 'integer' to a padded string of hex values
28 39     39 0 70 my $data=shift;
29 39 50       105 return undef if ( ! defined $data );
30 39 50       203 return "NaN" if ( $data !~ /^-?[0-9]+$/ );
31 39         1456 my $num=Math::BigInt->new("$data");
32 39 50       2576 $num->binc() if ( $data =~ /^-/ );
33 39         1137 my $str=$num->as_hex();
34 39         30011 $str =~ s/^-?0x//; # strip 0x and negative sign
35 39         241 $str =~ s/^.(..)*$/0$&/; # even up str
36 39         178 $str =~ s/^[89a-f]/00$&/; # convert to 2's complement as if positive
37 39 50       129 $str =~ y/0-9a-f/fedcba9876543210/ if ( $data =~ /^-/ ); # if negative, negate
38 39         1468 return $str;
39             }
40              
41             ##################################################
42              
43             sub ASN1OIDtoOID {
44 75     75 0 124 my ($val,$OIDstr)=(0,"");
45 75         215 foreach (split(//,shift)) { # Tackle OID one byte at a time
46 225         311 $val=($val*128)+ord($_&"\x7f"); # effectively shift $val 7 bits left add last 7 bits of $_
47 225 50       440 if (($_&"\x80") ne "\x80") { # If bit 8 is 0 then write append the value to the oid string
48 225 100       523 $OIDstr .= ( ( length($OIDstr) ) ? ".$val" : (int($val/40).".".$val%40) );
49 225         360 $val=0;
50             }
51             }
52 75         267 return $OIDstr;
53             }
54              
55             sub OIDtoASN1OID {
56 9 50   9 0 43 if ( $_[0] !~ /^[0-9]+(?:\.[0-9]+)*$/ ) { return undef; }
  0         0  
57 9         30 my @nums=split /\./,$_[0];
58 9         24 unshift(@nums,shift(@nums)*40+shift(@nums));
59 9         14 my $OIDASN1str="";
60 9         15 foreach (@nums) {
61 27         31 my $str="";
62 27         49 while ( $_ > 0 ) {
63 27         29 my $m = $_ % 128;
64 27         26 $_ -= $m;
65 27 50       44 $m += 128 if ($str ne "");
66 27         46 $str = pack('C',$m).$str;
67 27         64 $_/=128;
68             }
69 27         41 $OIDASN1str .= $str;
70             }
71 9         30 return $OIDASN1str;
72             }
73              
74             ##################################################
75              
76             sub ASN1BitStr { #Converts a hex representation of a bit string to an ASN1 bitstring primitive
77 9     9 0 26 my ($data,$length)=@_;
78 9 50       38 return undef if ( ! defined $data );
79 9 50       27 if ( defined $length ) {
80 0         0 my $wholebytes = int($length/8);
81 0         0 my $mask = Hex(pack('C',(2**((8-$length)%8)-1)));
82 0         0 return $mask.substr($data,0,$wholebytes*2);
83             }
84 9         56 return "00".$data;
85             }
86              
87             ##################################################
88              
89             sub ASN1Wrap { #wraps an ASN1 structure with its ASN1 headers
90 220     220 0 293 my $Header=shift;
91 220         294 my $Data=shift;
92 220 50 33     783 return undef if (! defined $Header || ! defined $Data);
93 220         379 return $Header.ASNLenStr($Data).$Data;
94             }
95              
96             ##################################################
97              
98             sub ASN1UnwrapHex {
99 0     0 0 0 my $data=shift;
100 0 0       0 return undef if ( ! defined $data );
101 0         0 $data=~ s/(..)/pack("C",hex($&))/ge;
  0         0  
102 0         0 return Hex(scalar ASN1Unwrap($data));
103             }
104              
105             sub ASN1Unwrap {
106 1109     1109 0 2251 my $BER=shift;
107 1109 0       2019 return (wantarray ? (0,0,undef,undef,undef,"") : "") if (! defined $BER );
    50          
108 1109         1117 my $inheader=1;
109 1109         1302 my ($Class,$Constructed,$Tag)=(0,0,0);
110 1109         1160 my ($headlen,$reallen,$lenlen)=(0,0,0);
111             # my $i;
112 1109         2381 for ( my $i = 0 ; $i <= length($BER) ; $i++ ) {
113 3453         4709 my $C=substr $BER,$i,1;
114 3453         14071 my @B=split(//, unpack("B*", $C));
115 3453 100       10631 if ( $inheader==1 ) { # ID First Byte
    50          
    100          
    100          
116 1109         1439 $headlen=1;
117 1109         2278 $Class= (shift @B)*2 + shift @B;
118 1109         1550 $Constructed=shift @B;
119 1109         3585 $Tag=unpack("N", pack("B32",substr("0" x 32 . join("",@B), -32 )));
120 1109 50       2499 if ($Tag==31) { $inheader=2; $Tag=0; }
  0         0  
  0         0  
121 1109         3553 else { $inheader=3; }
122             } elsif ( $inheader==2 ) { # ID Subsequent Bytes
123 0         0 $headlen++;
124 0         0 $inheader+=shift @B;
125 0         0 $Tag <<= 7;
126 0         0 $Tag=unpack("N", pack("B32",substr("0" x 32 . join("",@B), -32 )));
127             } elsif ( $inheader==3 ) { # Length First Byte
128 1109         1095 $headlen++;
129 1109 100       1682 if ( shift @B ) { $inheader=4; $reallen=0; $lenlen = unpack("N", pack("B32",substr("0" x 32 . join("",@B), -32 )));}
  88         102  
  88         85  
  88         457  
130 1021         1046 else { $inheader=-1; $lenlen=0; $reallen = unpack("N", pack("B32",substr("0" x 32 . join("",@B), -32 )));}
  1021         1064  
  1021         5498  
131             } elsif ( $inheader==4 ) { # Length Subsequent Bytes
132 126         127 $headlen++; $lenlen--;
  126         135  
133 126         624 $reallen+=(unpack("N", pack("B32",substr("0" x 32 . join("",@B), -32 ))))*(256**$lenlen);
134 126 100       339 if ( $lenlen == 0 ) { $inheader=-1;}
  88         277  
135             } else { #What's left: Primative or Construction
136 1109 100       6073 return wantarray ? ($headlen,$reallen,$Class,$Constructed,$Tag,substr($BER,$i,$reallen)) : substr($BER,$i,$reallen);
137             }
138             }
139 0 0       0 return wantarray ? (0,0,undef,undef,undef,"") : "";
140             }
141              
142             ##################################################
143              
144             sub ASNLenStr { #expects Hex String returns ASN1 length header
145 220     220 0 242 my $data=shift;
146 220 50       357 return undef if ( ! defined $data );
147              
148 220         271 my $len=length($data)/2;
149 220 100       381 if ($len <= 127) {
150 195         1085 return unpack("H2",pack("i",$len));
151             } else {
152 25         98 my $lenlen=sprintf "%0x",$len;
153 25 100       72 if ( length($lenlen) & 1 ) { $lenlen='0'.$lenlen; }
  19         35  
154 25         246 return sprintf("%0x%s",((length($lenlen)/2)+128),$lenlen);
155             }
156             }
157              
158             ##################################################
159             # Subroutine to find structure of DER ASN.1
160             ##################################################
161              
162             sub ASN1Index {
163 38     38 0 61 my $data=shift;
164 38 50       84 return () if ( ! defined $data );
165              
166 38         72 my @ContentStart=(0);
167 38         51 my $datalength=length($data);
168 38         79 my @ContentStop=($datalength);
169 38         46 my $pointer=0;
170              
171 38         130 my @total=ASN1Unwrap(substr($data,$pointer,($ContentStop[-1]-$ContentStart[-1])));
172 38 50       128 if ( $total[0]+$total[1] != $datalength ) { return (); }
  0         0  
173              
174 38         46 my @data;
175 38   66     233 while ( defined $ContentStop[-1] && $pointer < $ContentStop[0] ) {
176              
177 996         2621 my ($headlen,$reallen,$Class,$Constructed,$Tag,$Data) = ASN1Unwrap(substr($data,$pointer,($ContentStop[-1]-$ContentStart[-1])));
178              
179 996 50       2831 if ( ! defined $Class ) { return (); }
  0         0  
180 996         1483 push @ContentStart,($pointer+$headlen);
181 996         1522 push @ContentStop,($pointer+$headlen+$reallen);
182              
183 996         2205 push @data, [$Class,$Constructed,$Tag,$pointer,$headlen,$reallen];
184              
185 996         1159 $pointer+=$headlen;
186 996 100       2163 $pointer+=$reallen if ($Constructed==0);
187 996   100     5098 while ( defined $ContentStop[-1] && $pointer == $ContentStop[-1] ) {
188 1034 50       1915 if ( ! defined pop @ContentStop) { return (); }
  0         0  
189 1034 50       5838 if ( ! defined pop @ContentStart) { return (); }
  0         0  
190             }
191             }
192 38         403 return @data;
193             }
194              
195             1;
196             __END__