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