File Coverage

blib/lib/Convert/ASN1/_encode.pm
Criterion Covered Total %
statement 162 186 87.1
branch 85 98 86.7
condition 10 17 58.8
subroutine 17 17 100.0
pod n/a
total 274 318 86.1


line stmt bran cond sub pod time code
1             # Copyright (c) 2000-2005 Graham Barr . All rights reserved.
2             # This program is free software; you can redistribute it and/or
3             # modify it under the same terms as Perl itself.
4              
5             package Convert::ASN1;
6             $Convert::ASN1::VERSION = '0.33';
7 23     23   157 use strict;
  23         46  
  23         687  
8 23     23   118 use warnings;
  23         45  
  23         1643  
9              
10             BEGIN {
11 23     23   62763 unless (CHECK_UTF8) {
12             local $SIG{__DIE__};
13             eval { require bytes } and 'bytes'->import
14             }
15             }
16              
17             # These are the subs which do the encoding, they are called with
18             # 0 1 2 3 4 5
19             # $opt, $op, $stash, $var, $buf, $loop
20             # The order in the array must match the op definitions above
21              
22             my @encode = (
23             sub { die "internal error\n" },
24             \&_enc_boolean,
25             \&_enc_integer,
26             \&_enc_bitstring,
27             \&_enc_string,
28             \&_enc_null,
29             \&_enc_object_id,
30             \&_enc_real,
31             \&_enc_sequence,
32             \&_enc_sequence, # EXPLICIT is the same encoding as sequence
33             \&_enc_sequence, # SET is the same encoding as sequence
34             \&_enc_time,
35             \&_enc_time,
36             \&_enc_utf8,
37             \&_enc_any,
38             \&_enc_choice,
39             \&_enc_object_id,
40             \&_enc_bcd,
41             );
42              
43              
44             sub _encode {
45 150     150   350 my ($optn, $ops, $stash, $path) = @_;
46 150         214 my $var;
47              
48 150         213 foreach my $op (@{$ops}) {
  150         335  
49 197 100       479 next if $op->[cTYPE] == opEXTENSIONS;
50 192 100       449 if (defined(my $opt = $op->[cOPT])) {
51 5 100       14 next unless defined $stash->{$opt};
52             }
53 190 100       433 if (defined($var = $op->[cVAR])) {
54 153         327 push @$path, $var;
55 153 100       1072 require Carp, Carp::croak(join(".", @$path)," is undefined") unless defined $stash->{$var};
56             }
57 187         344 $_[4] .= $op->[cTAG];
58              
59 187         626 &{$encode[$op->[cTYPE]]}(
60             $optn,
61             $op,
62             (UNIVERSAL::isa($stash, 'HASH')
63 187 100       927 ? ($stash, defined($var) ? $stash->{$var} : undef)
    100          
64             : ({}, $stash)),
65             $_[4],
66             $op->[cLOOP],
67             $path,
68             );
69              
70 185 100       620 pop @$path if defined $var;
71             }
72              
73 145         905 $_[4];
74             }
75              
76              
77             sub _enc_boolean {
78             # 0 1 2 3 4 5 6
79             # $optn, $op, $stash, $var, $buf, $loop, $path
80              
81 15 100   15   66 $_[4] .= pack("CC",1, $_[3] ? 0xff : 0);
82             }
83              
84              
85             sub _enc_integer {
86             # 0 1 2 3 4 5 6
87             # $optn, $op, $stash, $var, $buf, $loop, $path
88 72 100   72   242 if (abs($_[3]) >= 2**31) {
89 12   50     909 my $os = i2osp($_[3], ref($_[3]) || $_[0]->{encode_bigint} || 'Math::BigInt');
90 12         31 my $len = length $os;
91 12 100       51 my $msb = (vec($os, 0, 8) & 0x80) ? 0 : 255;
92 12 100 100     64 $len++, $os = pack("C",$msb) . $os if $msb xor $_[3] > 0;
93 12         684 $_[4] .= asn_encode_length($len);
94 12         34 $_[4] .= $os;
95             }
96             else {
97 60         176 my $val = int($_[3]);
98 60         150 my $neg = ($val < 0);
99 60 100       233 my $len = num_length($neg ? ~$val : $val);
100 60         167 my $msb = $val & (0x80 << (($len - 1) * 8));
101              
102 60 100       190 $len++ if $neg ? !$msb : $msb;
    100          
103              
104 60         154 $_[4] .= asn_encode_length($len);
105 60         239 $_[4] .= substr(pack("N",$val), -$len);
106             }
107             }
108              
109              
110             sub _enc_bitstring {
111             # 0 1 2 3 4 5 6
112             # $optn, $op, $stash, $var, $buf, $loop, $path
113 4 100   4   22 my $vref = ref($_[3]) ? \($_[3]->[0]) : \$_[3];
114              
115 4 50       17 if (CHECK_UTF8 and Encode::is_utf8($$vref)) {
116 0         0 utf8::encode(my $tmp = $$vref);
117 0         0 $vref = \$tmp;
118             }
119              
120 4 100       11 if (ref($_[3])) {
121 3         9 my $less = (8 - ($_[3]->[1] & 7)) & 7;
122 3         9 my $len = ($_[3]->[1] + 7) >> 3;
123 3         8 $_[4] .= asn_encode_length(1+$len);
124 3         7 $_[4] .= pack("C",$less);
125 3         16 $_[4] .= substr($$vref, 0, $len);
126 3 50 33     16 if ($less && $len) {
127 3         22 substr($_[4],-1) &= pack("C",(0xff << $less) & 0xff);
128             }
129             }
130             else {
131 1         15 $_[4] .= asn_encode_length(1+length $$vref);
132 1         3 $_[4] .= pack("C",0);
133 1         3 $_[4] .= $$vref;
134             }
135             }
136              
137              
138             sub _enc_string {
139             # 0 1 2 3 4 5 6
140             # $optn, $op, $stash, $var, $buf, $loop, $path
141              
142 41 100   41   164 if (CHECK_UTF8 and Encode::is_utf8($_[3])) {
143 1         3 utf8::encode(my $tmp = $_[3]);
144 1         5 $_[4] .= asn_encode_length(length $tmp);
145 1         10 $_[4] .= $tmp;
146             }
147             else {
148 40         115 $_[4] .= asn_encode_length(length $_[3]);
149 40         103 $_[4] .= $_[3];
150             }
151             }
152              
153              
154             sub _enc_null {
155             # 0 1 2 3 4 5 6
156             # $optn, $op, $stash, $var, $buf, $loop, $path
157              
158 2     2   6 $_[4] .= pack("C",0);
159             }
160              
161              
162             sub _enc_object_id {
163             # 0 1 2 3 4 5 6
164             # $optn, $op, $stash, $var, $buf, $loop, $path
165              
166 9     9   87 my @data = ($_[3] =~ /(\d+)/g);
167              
168 9 100       51 if ($_[1]->[cTYPE] == opOBJID) {
169 6 50       19 if(@data < 2) {
170 0         0 @data = (0);
171             }
172             else {
173 6         22 my $first = $data[1] + ($data[0] * 40);
174 6         20 splice(@data,0,2,$first);
175             }
176             }
177              
178 9         20 my $l = length $_[4];
179 9         74 $_[4] .= pack("cw*", 0, @data);
180 9         33 substr($_[4],$l,1) = asn_encode_length(length($_[4]) - $l - 1);
181             }
182              
183              
184             sub _enc_real {
185             # 0 1 2 3 4 5 6
186             # $optn, $op, $stash, $var, $buf, $loop, $path
187              
188             # Zero
189 7 100   7   17 unless ($_[3]) {
190 1         2 $_[4] .= pack("C",0);
191 1         2 return;
192             }
193              
194 6         547 require POSIX;
195              
196             # +oo (well we use HUGE_VAL as Infinity is not available to perl)
197 6 100       6100 if ($_[3] >= POSIX::HUGE_VAL()) {
198 1         2 $_[4] .= pack("C*",0x01,0x40);
199 1         2 return;
200             }
201              
202             # -oo (well we use HUGE_VAL as Infinity is not available to perl)
203 5 100       20 if ($_[3] <= - POSIX::HUGE_VAL()) {
204 1         3 $_[4] .= pack("C*",0x01,0x41);
205 1         2 return;
206             }
207              
208 4 50 33     26 if (exists $_[0]->{'encode_real'} && $_[0]->{'encode_real'} ne 'binary') {
209 0         0 my $tmp = sprintf("%g",$_[3]);
210 0         0 $_[4] .= asn_encode_length(1+length $tmp);
211 0         0 $_[4] .= pack("C",1); # NR1?
212 0         0 $_[4] .= $tmp;
213 0         0 return;
214             }
215              
216             # We have a real number.
217 4         10 my $first = 0x80;
218 4         28 my($mantissa, $exponent) = POSIX::frexp($_[3]);
219              
220 4 100       14 if ($mantissa < 0.0) {
221 1         3 $mantissa = -$mantissa;
222 1         3 $first |= 0x40;
223             }
224 4         8 my($eMant,$eExp);
225              
226 4         13 while($mantissa > 0.0) {
227 5         23 ($mantissa, my $int) = POSIX::modf($mantissa * (1<<8));
228 5         24 $eMant .= pack("C",$int);
229             }
230 4         10 $exponent -= 8 * length $eMant;
231              
232 4         17 _enc_integer(undef, undef, undef, $exponent, $eExp);
233              
234             # $eExp will be prefixed by a length byte
235            
236 4 50       16 if (5 > length $eExp) {
237 4         21 $eExp =~ s/\A.//s;
238 4         11 $first |= length($eExp)-1;
239             }
240             else {
241 0         0 $first |= 0x3;
242             }
243              
244 4         20 $_[4] .= asn_encode_length(1 + length($eMant) + length($eExp));
245 4         9 $_[4] .= pack("C",$first);
246 4         8 $_[4] .= $eExp;
247 4         8 $_[4] .= $eMant;
248             }
249              
250              
251             sub _enc_sequence {
252             # 0 1 2 3 4 5 6
253             # $optn, $op, $stash, $var, $buf, $loop, $path
254              
255 45 50   45   187 if (my $ops = $_[1]->[cCHILD]) {
256 45         78 my $l = length $_[4];
257 45         75 $_[4] .= "\0\0"; # guess
258 45 100       117 if (defined $_[5]) {
259 11         22 my $op = $ops->[0]; # there should only be one
260 11         23 my $enc = $encode[$op->[cTYPE]];
261 11         26 my $tag = $op->[cTAG];
262 11         24 my $loop = $op->[cLOOP];
263              
264 11         24 push @{$_[6]}, -1;
  11         23  
265              
266 11         17 foreach my $var (@{$_[3]}) {
  11         26  
267 32         46 $_[6]->[-1]++;
268 32         75 $_[4] .= $tag;
269              
270 32         89 &{$enc}(
  32         103  
271             $_[0], # $optn
272             $op, # $op
273             $_[2], # $stash
274             $var, # $var
275             $_[4], # $buf
276             $loop, # $loop
277             $_[6], # $path
278             );
279             }
280 11         19 pop @{$_[6]};
  11         32  
281             }
282             else {
283 34 100       197 _encode($_[0],$_[1]->[cCHILD], defined($_[3]) ? $_[3] : $_[2], $_[6], $_[4]);
284             }
285 43         131 substr($_[4],$l,2) = asn_encode_length(length($_[4]) - $l - 2);
286             }
287             else {
288 0         0 $_[4] .= asn_encode_length(length $_[3]);
289 0         0 $_[4] .= $_[3];
290             }
291             }
292              
293              
294             my %_enc_time_opt = ( utctime => 1, withzone => 0, raw => 2);
295              
296             sub _enc_time {
297             # 0 1 2 3 4 5 6
298             # $optn, $op, $stash, $var, $buf, $loop, $path
299              
300 6   100 6   29 my $mode = $_enc_time_opt{$_[0]->{'encode_time'} || ''} || 0;
301              
302 6 50       14 if ($mode == 2) {
303 0         0 $_[4] .= asn_encode_length(length $_[3]);
304 0         0 $_[4] .= $_[3];
305 0         0 return;
306             }
307              
308 6         20 my $time;
309             my @time;
310 6         0 my $offset;
311 6         14 my $isgen = $_[1]->[cTYPE] == opGTIME;
312              
313 6 50       14 if (ref($_[3])) {
    100          
314 0         0 $offset = int($_[3]->[1] / 60);
315 0         0 $time = $_[3]->[0] + $_[3]->[1];
316             }
317             elsif ($mode == 0) {
318 5 50       10 if (exists $_[0]->{'encode_timezone'}) {
319 5         21 $offset = int($_[0]->{'encode_timezone'} / 60);
320 5         10 $time = $_[3] + $_[0]->{'encode_timezone'};
321             }
322             else {
323 0         0 @time = localtime($_[3]);
324 0         0 my @g = gmtime($_[3]);
325            
326 0         0 $offset = ($time[1] - $g[1]) + ($time[2] - $g[2]) * 60;
327 0         0 $time = $_[3] + $offset*60;
328             }
329             }
330             else {
331 1         2 $time = $_[3];
332             }
333 6         45 @time = gmtime($time);
334 6         12 $time[4] += 1;
335 6 100       16 $time[5] = $isgen ? ($time[5] + 1900) : ($time[5] % 100);
336              
337 6         31 my $tmp = sprintf("%02d"x6, @time[5,4,3,2,1,0]);
338 6 100       14 if ($isgen) {
339 3         26 my $sp = sprintf("%.03f",$time);
340 3 100       17 $tmp .= substr($sp,-4) unless $sp =~ /\.000$/;
341             }
342 6 100       25 $tmp .= $offset ? sprintf("%+03d%02d",$offset / 60, abs($offset % 60)) : 'Z';
343 6         20 $_[4] .= asn_encode_length(length $tmp);
344 6         19 $_[4] .= $tmp;
345             }
346              
347              
348             sub _enc_utf8 {
349             # 0 1 2 3 4 5 6
350             # $optn, $op, $stash, $var, $buf, $loop, $path
351              
352 2     2   2 if (CHECK_UTF8) {
353 2         5 my $tmp = $_[3];
354 2 100       9 utf8::upgrade($tmp) unless Encode::is_utf8($tmp);
355 2         5 utf8::encode($tmp);
356 2         5 $_[4] .= asn_encode_length(length $tmp);
357 2         5 $_[4] .= $tmp;
358             }
359             else {
360             $_[4] .= asn_encode_length(length $_[3]);
361             $_[4] .= $_[3];
362             }
363             }
364              
365              
366             sub _enc_any {
367             # 0 1 2 3 4 5 6
368             # $optn, $op, $stash, $var, $buf, $loop, $path
369              
370 2     2   3 my $handler;
371 2 50 33     15 if ($_[1]->[cDEFINE] && $_[2]->{$_[1]->[cDEFINE]}) {
372 2         7 $handler=$_[0]->{oidtable}{$_[2]->{$_[1]->[cDEFINE]}};
373 2 50       12 $handler=$_[0]->{handlers}{$_[1]->[cVAR]}{$_[2]->{$_[1]->[cDEFINE]}} unless $handler;
374             }
375 2 50       6 if ($handler) {
376 2         12 $_[4] .= $handler->encode($_[3]);
377             } else {
378 0         0 $_[4] .= $_[3];
379             }
380             }
381              
382              
383             sub _enc_choice {
384             # 0 1 2 3 4 5 6
385             # $optn, $op, $stash, $var, $buf, $loop, $path
386              
387 10 100   10   25 my $stash = defined($_[3]) ? $_[3] : $_[2];
388 10         19 for my $op (@{$_[1]->[cCHILD]}) {
  10         30  
389 16 100       35 next if $op->[cTYPE] == opEXTENSIONS;
390 15 50       56 my $var = defined $op->[cVAR] ? $op->[cVAR] : $op->[cCHILD]->[0]->[cVAR];
391              
392 15 100       42 if (exists $stash->{$var}) {
393 10         15 push @{$_[6]}, $var;
  10         23  
394 10         43 _encode($_[0],[$op], $stash, $_[6], $_[4]);
395 10         17 pop @{$_[6]};
  10         21  
396 10         21 return;
397             }
398             }
399 0         0 require Carp;
400 0         0 Carp::croak("No value found for CHOICE " . join(".", @{$_[6]}));
  0         0  
401             }
402              
403              
404             sub _enc_bcd {
405             # 0 1 2 3 4 5 6
406             # $optn, $op, $stash, $var, $buf, $loop, $path
407 8 100   8   70 my $str = ("$_[3]" =~ /^(\d+)/) ? $1 : "";
408 8 100       35 $str .= "F" if length($str) & 1;
409 8         32 $_[4] .= asn_encode_length(length($str) / 2);
410 8         32 $_[4] .= pack("H*", $str);
411             }
412             1;
413