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
|
|
|
|
|
|
|
|