|
1 # |
|
2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of the License "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # |
|
16 #!/bin/perl -w |
|
17 |
|
18 # Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
19 # All rights reserved. |
|
20 # This component and the accompanying materials are made available |
|
21 # under the terms of the License "Symbian Foundation License v1.0" |
|
22 # which accompanies this distribution, and is available |
|
23 # at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html". |
|
24 # |
|
25 # Initial Contributors: |
|
26 # Nokia Corporation - initial contribution. |
|
27 # |
|
28 # Contributors: |
|
29 # |
|
30 # Description: |
|
31 # This script converts certificate constraint extensions specified in an INI |
|
32 # format into a hex representation of the DER encoding that OpenSSL can |
|
33 # add to a certificate. |
|
34 # This script allows corrupt certificate extensions to be generated for unit |
|
35 # testing and therefore using this script to create production certificates is |
|
36 # NOT supported. |
|
37 # See SGL.GT0220.255_Developer_Certificates.doc for usage instructions. |
|
38 # |
|
39 # |
|
40 |
|
41 use strict; |
|
42 use Getopt::Long; |
|
43 |
|
44 my $OID_DEVICE_ID_LIST="1.2.826.0.1.1796587.1.1.1.1"; |
|
45 my $OID_SID_LIST="1.2.826.0.1.1796587.1.1.1.4"; |
|
46 my $OID_VID_LIST="1.2.826.0.1.1796587.1.1.1.5"; |
|
47 my $OID_CAPABILITIES="1.2.826.0.1.1796587.1.1.1.6"; |
|
48 |
|
49 # Reserved for future use |
|
50 #my $OID_SUBSCRIBER_ID_LIST="1.2.826.0.1.1796587.1.1.2.2"; |
|
51 #my $OID_ICC_ID_LIST="1.2.826.0.1.1796587.1.1.1.3"; |
|
52 |
|
53 my $DER_UTF8STRING_TAG="0C"; |
|
54 my $DER_PRINTABLESTRING_TAG="13"; |
|
55 my $DER_SEQUENCE_TAG="30"; |
|
56 my $DER_INTEGER_TAG="02"; |
|
57 my $DER_BITSTRING_TAG="03"; |
|
58 my $DER_OCTET_TAG="04"; |
|
59 |
|
60 my %CAPABILITY_SET = ( |
|
61 "TCB" => 0, |
|
62 "COMMDD" => 1, |
|
63 "POWERMGMT" => 2, |
|
64 "MULTIMEDIADD" => 3, |
|
65 "READDEVICEDATA" => 4, |
|
66 "WRITEDEVICEDATA" => 5, |
|
67 "DRM" => 6, |
|
68 "TRUSTEDUI" => 7, |
|
69 "PROTSERV" => 8, |
|
70 "DISKADMIN" => 9, |
|
71 "NETWORKCONTROL" => 10, |
|
72 "ALLFILES" => 11, |
|
73 "SWEVENT" => 12, |
|
74 "NETWORKSERVICES" => 13, |
|
75 "LOCALSERVICES" => 14, |
|
76 "READUSERDATA" => 15, |
|
77 "WRITEUSERDATA" => 16, |
|
78 "LOCATION" => 17, |
|
79 "SURROUNDINGSDD" => 18, |
|
80 "USERENVIRONMENT" => 19 |
|
81 ); |
|
82 |
|
83 # Sections encountered |
|
84 my %sections; |
|
85 |
|
86 # Fields within sectins |
|
87 my @device_id_list = (); |
|
88 my @sid_list = (); |
|
89 my @vid_list = (); |
|
90 my $capabilities = ''; |
|
91 |
|
92 main(); |
|
93 exit(0); |
|
94 |
|
95 # Main function. |
|
96 # Loops through the input file and creates lists of items for each section. |
|
97 sub main { |
|
98 my $section = ""; |
|
99 my $output_binary = 0; |
|
100 |
|
101 GetOptions('binary' => \$output_binary); |
|
102 my ($infile, $outfile) = @ARGV; |
|
103 |
|
104 if (defined $infile) { |
|
105 open IN, "$infile" || die "Cannot open input file $infile"; |
|
106 } |
|
107 else { |
|
108 *IN = *STDIN; |
|
109 } |
|
110 |
|
111 if (defined $outfile) { |
|
112 open OUT, ">$outfile" || die "Cannot open input file $outfile"; |
|
113 } |
|
114 else { |
|
115 *OUT = *STDOUT; |
|
116 } |
|
117 |
|
118 while (<IN>) { |
|
119 chomp; |
|
120 if (/^\s*\[\w*\]/) { |
|
121 # Process the new section name |
|
122 |
|
123 s/.*\[\s*(.*)\s*].*/$1/g; |
|
124 $section = lc($_); |
|
125 $sections{$section} = 1; |
|
126 } |
|
127 elsif (/^\s*\#/) { |
|
128 # Comment - ignore |
|
129 } |
|
130 elsif (/.*=.*/ ) { |
|
131 # Process element within section |
|
132 s/.*=//g; |
|
133 if ($section eq "device_id_list") { |
|
134 push @device_id_list, $_; |
|
135 } |
|
136 elsif ($section eq "sid_list") { |
|
137 push @sid_list, $_; |
|
138 } |
|
139 elsif ($section eq "vid_list") { |
|
140 push @vid_list, $_; |
|
141 } |
|
142 elsif ($section eq "cert_capabilities") { |
|
143 |
|
144 if (! $capabilities eq "") { |
|
145 die "Error: Multiple capability constraints defined."; |
|
146 } |
|
147 |
|
148 $capabilities = $_; |
|
149 if ($capabilities eq "" || $capabilities =~ /^\s*[01]+\s*$/) { |
|
150 # Handle explicit bit strings |
|
151 $capabilities =~ s/\s//g; |
|
152 } |
|
153 else { |
|
154 # Convert MMP syntax into an explicit bit string |
|
155 $capabilities = &encode_capabilities($capabilities); |
|
156 } |
|
157 } |
|
158 else { |
|
159 # Not in a section so ignore text |
|
160 } |
|
161 } |
|
162 } |
|
163 |
|
164 if ($output_binary) { |
|
165 print_to_der(); |
|
166 } |
|
167 else { |
|
168 print_to_cnf(); |
|
169 } |
|
170 close IN; |
|
171 close OUT; |
|
172 } |
|
173 |
|
174 # Test function which outputs the binary DER encoding. This can be vieweed using an |
|
175 # ASN.1 viewer. |
|
176 # This is really a debug function. |
|
177 sub print_to_der { |
|
178 my $seq_octets = 0; |
|
179 my $seq_content = ""; |
|
180 |
|
181 if (defined $sections{"device_id_list"}) { |
|
182 $seq_content .= ":" . &encode_string_list(\@device_id_list, \$seq_octets); |
|
183 } |
|
184 |
|
185 if (defined $sections{"sid_list"}) { |
|
186 $seq_content .= ":" . &encode_integer_list(\@sid_list, \$seq_octets); |
|
187 } |
|
188 |
|
189 if (defined $sections{"vid_list"}) { |
|
190 $seq_content .= ":" . &encode_integer_list(\@vid_list, \$seq_octets); |
|
191 } |
|
192 |
|
193 if (defined $sections{"cert_capabilities"}) { |
|
194 $seq_content .= ":" . &encode_bit_string($capabilities, \$seq_octets); |
|
195 } |
|
196 # Tidy up repreated colons |
|
197 $seq_content =~ s/::/:/; |
|
198 $seq_content =~ s/^://g; |
|
199 |
|
200 my $seq_length_octets; |
|
201 my $seq_length = &encode_length($seq_octets, \$seq_length_octets); |
|
202 my $seq ="$DER_SEQUENCE_TAG:$seq_length:$seq_content"; |
|
203 |
|
204 $seq =~ s/::/:/g; |
|
205 $seq = uc($seq); |
|
206 |
|
207 binmode(OUT); |
|
208 foreach (split(/:/, $seq)){ |
|
209 print OUT pack('C', hex); |
|
210 } |
|
211 } |
|
212 |
|
213 # Output to a format that can be read by Open SSL using the -extfile parameter |
|
214 sub print_to_cnf { |
|
215 print OUT "extensions = extend\n"; |
|
216 print OUT "[extend]\n"; |
|
217 |
|
218 my $octets = 0; |
|
219 my $output = ""; |
|
220 |
|
221 if (defined $sections{"device_id_list"}) { |
|
222 $output .= "# Device ID List\n" . |
|
223 $OID_DEVICE_ID_LIST . "= critical, " . "DER:" . |
|
224 uc(&encode_string_list(\@device_id_list, \$octets)) . "\n"; |
|
225 } |
|
226 |
|
227 if (defined $sections{"sid_list"}) { |
|
228 $output .= "# SID List\n" . |
|
229 $OID_SID_LIST . "= critical, " . "DER:" . |
|
230 uc(&encode_integer_list(\@sid_list, \$octets)) . "\n"; |
|
231 } |
|
232 |
|
233 if (defined $sections{"vid_list"}) { |
|
234 $output .= "# VID List\n" . |
|
235 $OID_VID_LIST . "= critical, " . "DER:" . uc(&encode_integer_list(\@vid_list, \$octets)) . "\n"; |
|
236 } |
|
237 |
|
238 if (defined $sections{"cert_capabilities"}) { |
|
239 $output .= "# Capabilities\n" . |
|
240 $OID_CAPABILITIES . "= critical, " . "DER:" . uc(&encode_bit_string($capabilities, \$octets)) . "\n"; |
|
241 } |
|
242 |
|
243 # Remove trailing colons |
|
244 $output=~ s/\:*$//mg; |
|
245 print OUT $output; |
|
246 } |
|
247 |
|
248 # Creates a hex representation of the DER encoding of a sequence of strings. |
|
249 sub encode_string_list($$) { |
|
250 my ($list, $octets) = @_; |
|
251 |
|
252 my $sequence_body = ""; |
|
253 |
|
254 my $sequence_octets = 0; |
|
255 foreach (@$list) { |
|
256 my $hex_string = &encode_utf8_string($_, \$sequence_octets); |
|
257 |
|
258 # Add to string sequence body |
|
259 if ($sequence_body ne "") { |
|
260 $sequence_body .= ":"; |
|
261 } |
|
262 $sequence_body .= $hex_string; |
|
263 } |
|
264 my $seq_length_octets = 0; |
|
265 my $seq_length = &encode_length($sequence_octets, \$seq_length_octets); |
|
266 |
|
267 $$octets += 1 + $seq_length_octets + $sequence_octets; |
|
268 |
|
269 return "$DER_SEQUENCE_TAG:$seq_length:$sequence_body"; |
|
270 } |
|
271 |
|
272 # Creates a hex represenation of the DER encoding of a sequence of integers. |
|
273 sub encode_integer_list($$) { |
|
274 my ($list, $octets) = @_; |
|
275 |
|
276 my $sequence_body = ""; |
|
277 my $sequence_octets = 0; |
|
278 foreach (@$list) { |
|
279 # Increment for integer tag value |
|
280 # Increment for integer length < 127 octets assumed ! |
|
281 $sequence_octets+=2; |
|
282 |
|
283 if (s/^0x//) { |
|
284 $_ = hex; |
|
285 } |
|
286 |
|
287 # Convert the integer to base 256 hex and find out how |
|
288 # many octets were required |
|
289 my $hex_octets; |
|
290 my $hex_integer; |
|
291 if (//) { |
|
292 $hex_octets = 0; |
|
293 $hex_integer = ""; |
|
294 } |
|
295 else { |
|
296 $hex_integer = &to_hex_base256($_, \$hex_octets); |
|
297 $sequence_octets += $hex_octets; |
|
298 } |
|
299 |
|
300 # Add to integer sequence body |
|
301 if ($sequence_body ne "") { |
|
302 $sequence_body .= ":"; |
|
303 } |
|
304 |
|
305 # No need to store length in long form because in base256 |
|
306 # we never need more than 7 octets to store the largest number |
|
307 # that we need. |
|
308 my $int_header = sprintf("%2.2x", $hex_octets); |
|
309 $sequence_body .= "$DER_INTEGER_TAG:$int_header:$hex_integer"; |
|
310 } |
|
311 |
|
312 # Get the number of octets of the entire sequence. This could require |
|
313 # encoding in long form. |
|
314 my $seq_length_octets = 0; |
|
315 my $seq_length = &encode_length($sequence_octets, \$seq_length_octets); |
|
316 |
|
317 $$octets += 1 + $seq_length_octets + $sequence_octets; |
|
318 |
|
319 return "$DER_SEQUENCE_TAG:$seq_length:$sequence_body"; |
|
320 } |
|
321 |
|
322 # Creates a hex represenation of the DER encoding of a UTF-8 string. |
|
323 sub encode_utf8_string($$) { |
|
324 my ($input, $octets) = @_; |
|
325 |
|
326 # Hex encoded string |
|
327 my $output = ""; |
|
328 my $input_len = length($input); |
|
329 my $i = 0; |
|
330 while ($i < $input_len) { |
|
331 my $hex_val = ord(substr($input, $i, 1)); |
|
332 if ($output ne "") { |
|
333 $output .= ":"; |
|
334 } |
|
335 $output .= sprintf("%2.2x", $hex_val); |
|
336 $i++; |
|
337 } |
|
338 |
|
339 # Build string header |
|
340 my $output_length_octets = 0; |
|
341 my $output_length = &encode_length($input_len, \$output_length_octets); |
|
342 |
|
343 # Track number of octets added for string header |
|
344 $$octets += 1 + $output_length_octets + $input_len; |
|
345 return "$DER_UTF8STRING_TAG:$output_length:$output"; |
|
346 } |
|
347 |
|
348 # Converts the text description of capabilities into an ASCII string of 0s and 1s; |
|
349 sub encode_capabilities($) { |
|
350 my ($value) = @_; |
|
351 my $output = ""; |
|
352 my @caps = (0); |
|
353 |
|
354 $value = uc($value); |
|
355 foreach (split(/[\s,]/, $value)) { |
|
356 my $set_val = 1; |
|
357 |
|
358 if (s/^-//g) { |
|
359 $set_val = 0; |
|
360 } |
|
361 |
|
362 if (/^ALL$/) { |
|
363 foreach (keys %CAPABILITY_SET) { |
|
364 @caps[$CAPABILITY_SET{$_}] = 1; |
|
365 } |
|
366 } |
|
367 elsif (/^NONE$/) { |
|
368 @caps = (); |
|
369 } |
|
370 if (defined $CAPABILITY_SET{$_}) { |
|
371 $caps[$CAPABILITY_SET{$_}] = $set_val; |
|
372 } |
|
373 } |
|
374 |
|
375 # Build the ascii bit string. Bit 0 is the left most bit |
|
376 for (my $i = 0; $i <= $#caps; $i++) { |
|
377 $output .= (defined $caps[$i] && $caps[$i] ? "1" : "0"); |
|
378 } |
|
379 |
|
380 return $output; |
|
381 } |
|
382 |
|
383 # Creates a hex representation of the DER encoding of an arbitrary length bit string |
|
384 sub encode_bit_string($$) { |
|
385 my ($text, $octets) = @_; |
|
386 |
|
387 # Bit string in hex including padding length octet |
|
388 my $bit_str = ""; |
|
389 my $bit_str_octets = 1; # one octet for padding |
|
390 |
|
391 # Current byte |
|
392 my $byte = 0; |
|
393 my $len = length($text); |
|
394 |
|
395 if ($len == 0) { |
|
396 $$octets+=2; |
|
397 return "03:00"; |
|
398 } |
|
399 |
|
400 my $i = 0; |
|
401 while ($i < $len) { |
|
402 |
|
403 # Read the ith character and insert it in the correct place in the byte |
|
404 # (fill from the left) |
|
405 my $c = substr($text, $i, 1); |
|
406 if ($c eq "1") { |
|
407 $byte |= (1 << (7 - ($i % 8))); |
|
408 } |
|
409 elsif ($c ne "0") { |
|
410 die "Invalid character $c in bit string $text"; |
|
411 } |
|
412 |
|
413 if (++$i % 8 == 0) { |
|
414 # Received 8 bits so output byte in hex |
|
415 if ($bit_str ne "") { |
|
416 $bit_str .= ":"; |
|
417 } |
|
418 $bit_str .= sprintf("%2.2x", $byte); |
|
419 $bit_str_octets++; |
|
420 $byte = 0; |
|
421 } |
|
422 } |
|
423 # Pad any remaining bits / make sure 0 is output for empty string |
|
424 if ($byte != 0 || $bit_str_octets == 1) { |
|
425 if ($bit_str ne "") { |
|
426 $bit_str .= ":"; |
|
427 } |
|
428 $bit_str .= sprintf("%2.2x", $byte); |
|
429 $bit_str_octets++; |
|
430 } |
|
431 |
|
432 my $pad_length = "00"; |
|
433 if ($len % 8 > 0) { |
|
434 # If this isn't a multiple of 8 bits then calculated |
|
435 # the number of padding bits added. |
|
436 $pad_length = sprintf("%2.2x", 8 - ($len % 8)); |
|
437 } |
|
438 |
|
439 # Octets used to store the length |
|
440 my $bit_str_length_octets = 0; |
|
441 my $bit_str_length = &encode_length($bit_str_octets, \$bit_str_length_octets); |
|
442 $$octets += 1 + $bit_str_length_octets + $bit_str_octets; |
|
443 |
|
444 return "$DER_BITSTRING_TAG:$bit_str_length:$pad_length:$bit_str"; |
|
445 } |
|
446 |
|
447 # Return a hex represenation of the length using DER primitive (definate length encoding) |
|
448 sub encode_length($$) { |
|
449 my ($num, $octets) = @_; |
|
450 |
|
451 if ($num < 128) { |
|
452 # Number is < 128 so encode in short form |
|
453 $$octets++; |
|
454 return sprintf("%2.2x", $num); |
|
455 } |
|
456 else { |
|
457 # Number >= 128 so encode in long form |
|
458 my $length_octets = 0; |
|
459 my $base256 = &to_hex_base256($num, \$length_octets); |
|
460 if ($length_octets > 127) {die "Encoding overflow.";} |
|
461 |
|
462 $$octets += 1 + $length_octets; |
|
463 |
|
464 # Set the top bit of the length octet to indicate long form |
|
465 return "" . sprintf("%2.2x", ($length_octets | 0x80)) . ":$base256"; |
|
466 } |
|
467 } |
|
468 |
|
469 # Convert an integer into an ascii hex representation in base 256 |
|
470 # $num - the number to encode |
|
471 # $octets - refernce to the octet count to increment |
|
472 sub to_hex_base256($$) { |
|
473 my ($num, $octets) = @_; |
|
474 |
|
475 my $base256 = ""; |
|
476 $num = int($num); |
|
477 while ($num > 0) { |
|
478 my $hexoctet = sprintf("%2.2x", $num & 0xFF); |
|
479 if ($base256 ne "") { |
|
480 $base256 = "$hexoctet:$base256"; |
|
481 } |
|
482 else { |
|
483 $base256 = $hexoctet; |
|
484 } |
|
485 $num >>= 8; |
|
486 $$octets++; |
|
487 } |
|
488 if ($base256 eq "") { |
|
489 $base256 = "0"; |
|
490 $$octets++; |
|
491 } |
|
492 return $base256; |
|
493 } |