sl@0: # sl@0: # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: # All rights reserved. sl@0: # This component and the accompanying materials are made available sl@0: # under the terms of the License "Eclipse Public License v1.0" sl@0: # which accompanies this distribution, and is available sl@0: # at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: # sl@0: # Initial Contributors: sl@0: # Nokia Corporation - initial contribution. sl@0: # sl@0: # Contributors: sl@0: # sl@0: # Description: sl@0: # sl@0: #!/bin/perl -w sl@0: sl@0: use strict; sl@0: use Getopt::Long; sl@0: sl@0: my $OID_DEVICE_ID_LIST="1.2.826.0.1.1796587.1.1.1.1"; sl@0: my $OID_SID_LIST="1.2.826.0.1.1796587.1.1.1.4"; sl@0: my $OID_VID_LIST="1.2.826.0.1.1796587.1.1.1.5"; sl@0: my $OID_CAPABILITIES="1.2.826.0.1.1796587.1.1.1.6"; sl@0: sl@0: # Reserved for future use sl@0: #my $OID_SUBSCRIBER_ID_LIST="1.2.826.0.1.1796587.1.1.2.2"; sl@0: #my $OID_ICC_ID_LIST="1.2.826.0.1.1796587.1.1.1.3"; sl@0: sl@0: my $DER_UTF8STRING_TAG="0C"; sl@0: my $DER_PRINTABLESTRING_TAG="13"; sl@0: my $DER_SEQUENCE_TAG="30"; sl@0: my $DER_INTEGER_TAG="02"; sl@0: my $DER_BITSTRING_TAG="03"; sl@0: my $DER_OCTET_TAG="04"; sl@0: sl@0: my %CAPABILITY_SET = ( sl@0: "TCB" => 0, sl@0: "COMMDD" => 1, sl@0: "POWERMGMT" => 2, sl@0: "MULTIMEDIADD" => 3, sl@0: "READDEVICEDATA" => 4, sl@0: "WRITEDEVICEDATA" => 5, sl@0: "DRM" => 6, sl@0: "TRUSTEDUI" => 7, sl@0: "PROTSERV" => 8, sl@0: "DISKADMIN" => 9, sl@0: "NETWORKCONTROL" => 10, sl@0: "ALLFILES" => 11, sl@0: "SWEVENT" => 12, sl@0: "NETWORKSERVICES" => 13, sl@0: "LOCALSERVICES" => 14, sl@0: "READUSERDATA" => 15, sl@0: "WRITEUSERDATA" => 16, sl@0: "LOCATION" => 17, sl@0: "SURROUNDINGSDD" => 18, sl@0: "USERENVIRONMENT" => 19 sl@0: ); sl@0: sl@0: # Sections encountered sl@0: my %sections; sl@0: sl@0: # Fields within sectins sl@0: my @device_id_list = (); sl@0: my @sid_list = (); sl@0: my @vid_list = (); sl@0: my $capabilities = ''; sl@0: sl@0: main(); sl@0: exit(0); sl@0: sl@0: # Main function. sl@0: # Loops through the input file and creates lists of items for each section. sl@0: sub main { sl@0: my $section = ""; sl@0: my $output_binary = 0; sl@0: sl@0: GetOptions('binary' => \$output_binary); sl@0: my ($infile, $outfile) = @ARGV; sl@0: sl@0: if (defined $infile) { sl@0: open IN, "$infile" || die "Cannot open input file $infile"; sl@0: } sl@0: else { sl@0: *IN = *STDIN; sl@0: } sl@0: sl@0: if (defined $outfile) { sl@0: open OUT, ">$outfile" || die "Cannot open input file $outfile"; sl@0: } sl@0: else { sl@0: *OUT = *STDOUT; sl@0: } sl@0: sl@0: while () { sl@0: chomp; sl@0: if (/^\s*\[\w*\]/) { sl@0: # Process the new section name sl@0: sl@0: s/.*\[\s*(.*)\s*].*/$1/g; sl@0: $section = lc($_); sl@0: $sections{$section} = 1; sl@0: } sl@0: elsif (/^\s*\#/) { sl@0: # Comment - ignore sl@0: } sl@0: elsif (/.*=.*/ ) { sl@0: # Process element within section sl@0: s/.*=//g; sl@0: if ($section eq "device_id_list") { sl@0: push @device_id_list, $_; sl@0: } sl@0: elsif ($section eq "sid_list") { sl@0: push @sid_list, $_; sl@0: } sl@0: elsif ($section eq "vid_list") { sl@0: push @vid_list, $_; sl@0: } sl@0: elsif ($section eq "cert_capabilities") { sl@0: sl@0: if (! $capabilities eq "") { sl@0: die "Error: Multiple capability constraints defined."; sl@0: } sl@0: sl@0: $capabilities = $_; sl@0: if ($capabilities eq "" || $capabilities =~ /^\s*[01]+\s*$/) { sl@0: # Handle explicit bit strings sl@0: $capabilities =~ s/\s//g; sl@0: } sl@0: else { sl@0: # Convert MMP syntax into an explicit bit string sl@0: $capabilities = &encode_capabilities($capabilities); sl@0: } sl@0: } sl@0: else { sl@0: # Not in a section so ignore text sl@0: } sl@0: } sl@0: } sl@0: sl@0: if ($output_binary) { sl@0: print_to_der(); sl@0: } sl@0: else { sl@0: print_to_cnf(); sl@0: } sl@0: close IN; sl@0: close OUT; sl@0: } sl@0: sl@0: # Test function which outputs the binary DER encoding. This can be vieweed using an sl@0: # ASN.1 viewer. sl@0: # This is really a debug function. sl@0: sub print_to_der { sl@0: my $seq_octets = 0; sl@0: my $seq_content = ""; sl@0: sl@0: if (defined $sections{"device_id_list"}) { sl@0: $seq_content .= ":" . &encode_string_list(\@device_id_list, \$seq_octets); sl@0: } sl@0: sl@0: if (defined $sections{"sid_list"}) { sl@0: $seq_content .= ":" . &encode_integer_list(\@sid_list, \$seq_octets); sl@0: } sl@0: sl@0: if (defined $sections{"vid_list"}) { sl@0: $seq_content .= ":" . &encode_integer_list(\@vid_list, \$seq_octets); sl@0: } sl@0: sl@0: if (defined $sections{"cert_capabilities"}) { sl@0: $seq_content .= ":" . &encode_bit_string($capabilities, \$seq_octets); sl@0: } sl@0: # Tidy up repreated colons sl@0: $seq_content =~ s/::/:/; sl@0: $seq_content =~ s/^://g; sl@0: sl@0: my $seq_length_octets; sl@0: my $seq_length = &encode_length($seq_octets, \$seq_length_octets); sl@0: my $seq ="$DER_SEQUENCE_TAG:$seq_length:$seq_content"; sl@0: sl@0: $seq =~ s/::/:/g; sl@0: $seq = uc($seq); sl@0: sl@0: binmode(OUT); sl@0: foreach (split(/:/, $seq)){ sl@0: print OUT pack('C', hex); sl@0: } sl@0: } sl@0: sl@0: # Output to a format that can be read by Open SSL using the -extfile parameter sl@0: sub print_to_cnf { sl@0: print OUT "extensions = extend\n"; sl@0: print OUT "[extend]\n"; sl@0: sl@0: my $octets = 0; sl@0: my $output = ""; sl@0: sl@0: if (defined $sections{"device_id_list"}) { sl@0: $output .= "# Device ID List\n" . sl@0: $OID_DEVICE_ID_LIST . "= critical, " . "DER:" . sl@0: uc(&encode_string_list(\@device_id_list, \$octets)) . "\n"; sl@0: } sl@0: sl@0: if (defined $sections{"sid_list"}) { sl@0: $output .= "# SID List\n" . sl@0: $OID_SID_LIST . "= critical, " . "DER:" . sl@0: uc(&encode_integer_list(\@sid_list, \$octets)) . "\n"; sl@0: } sl@0: sl@0: if (defined $sections{"vid_list"}) { sl@0: $output .= "# VID List\n" . sl@0: $OID_VID_LIST . "= critical, " . "DER:" . uc(&encode_integer_list(\@vid_list, \$octets)) . "\n"; sl@0: } sl@0: sl@0: if (defined $sections{"cert_capabilities"}) { sl@0: $output .= "# Capabilities\n" . sl@0: $OID_CAPABILITIES . "= critical, " . "DER:" . uc(&encode_bit_string($capabilities, \$octets)) . "\n"; sl@0: } sl@0: sl@0: # Remove trailing colons sl@0: $output=~ s/\:*$//mg; sl@0: print OUT $output; sl@0: } sl@0: sl@0: # Creates a hex representation of the DER encoding of a sequence of strings. sl@0: sub encode_string_list($$) { sl@0: my ($list, $octets) = @_; sl@0: sl@0: my $sequence_body = ""; sl@0: sl@0: my $sequence_octets = 0; sl@0: foreach (@$list) { sl@0: my $hex_string = &encode_utf8_string($_, \$sequence_octets); sl@0: sl@0: # Add to string sequence body sl@0: if ($sequence_body ne "") { sl@0: $sequence_body .= ":"; sl@0: } sl@0: $sequence_body .= $hex_string; sl@0: } sl@0: my $seq_length_octets = 0; sl@0: my $seq_length = &encode_length($sequence_octets, \$seq_length_octets); sl@0: sl@0: $$octets += 1 + $seq_length_octets + $sequence_octets; sl@0: sl@0: return "$DER_SEQUENCE_TAG:$seq_length:$sequence_body"; sl@0: } sl@0: sl@0: # Creates a hex represenation of the DER encoding of a sequence of integers. sl@0: sub encode_integer_list($$) { sl@0: my ($list, $octets) = @_; sl@0: sl@0: my $sequence_body = ""; sl@0: my $sequence_octets = 0; sl@0: foreach (@$list) { sl@0: # Increment for integer tag value sl@0: # Increment for integer length < 127 octets assumed ! sl@0: $sequence_octets+=2; sl@0: sl@0: if (s/^0x//) { sl@0: $_ = hex; sl@0: } sl@0: sl@0: # Convert the integer to base 256 hex and find out how sl@0: # many octets were required sl@0: my $hex_octets; sl@0: my $hex_integer; sl@0: if (//) { sl@0: $hex_octets = 0; sl@0: $hex_integer = ""; sl@0: } sl@0: else { sl@0: $hex_integer = &to_hex_base256($_, \$hex_octets); sl@0: $sequence_octets += $hex_octets; sl@0: } sl@0: sl@0: # Add to integer sequence body sl@0: if ($sequence_body ne "") { sl@0: $sequence_body .= ":"; sl@0: } sl@0: sl@0: # No need to store length in long form because in base256 sl@0: # we never need more than 7 octets to store the largest number sl@0: # that we need. sl@0: my $int_header = sprintf("%2.2x", $hex_octets); sl@0: $sequence_body .= "$DER_INTEGER_TAG:$int_header:$hex_integer"; sl@0: } sl@0: sl@0: # Get the number of octets of the entire sequence. This could require sl@0: # encoding in long form. sl@0: my $seq_length_octets = 0; sl@0: my $seq_length = &encode_length($sequence_octets, \$seq_length_octets); sl@0: sl@0: $$octets += 1 + $seq_length_octets + $sequence_octets; sl@0: sl@0: return "$DER_SEQUENCE_TAG:$seq_length:$sequence_body"; sl@0: } sl@0: sl@0: # Creates a hex represenation of the DER encoding of a UTF-8 string. sl@0: sub encode_utf8_string($$) { sl@0: my ($input, $octets) = @_; sl@0: sl@0: # Hex encoded string sl@0: my $output = ""; sl@0: my $input_len = length($input); sl@0: my $i = 0; sl@0: while ($i < $input_len) { sl@0: my $hex_val = ord(substr($input, $i, 1)); sl@0: if ($output ne "") { sl@0: $output .= ":"; sl@0: } sl@0: $output .= sprintf("%2.2x", $hex_val); sl@0: $i++; sl@0: } sl@0: sl@0: # Build string header sl@0: my $output_length_octets = 0; sl@0: my $output_length = &encode_length($input_len, \$output_length_octets); sl@0: sl@0: # Track number of octets added for string header sl@0: $$octets += 1 + $output_length_octets + $input_len; sl@0: return "$DER_UTF8STRING_TAG:$output_length:$output"; sl@0: } sl@0: sl@0: # Converts the text description of capabilities into an ASCII string of 0s and 1s; sl@0: sub encode_capabilities($) { sl@0: my ($value) = @_; sl@0: my $output = ""; sl@0: my @caps = (0); sl@0: sl@0: $value = uc($value); sl@0: foreach (split(/[\s,]/, $value)) { sl@0: my $set_val = 1; sl@0: sl@0: if (s/^-//g) { sl@0: $set_val = 0; sl@0: } sl@0: sl@0: if (/^ALL$/) { sl@0: foreach (keys %CAPABILITY_SET) { sl@0: @caps[$CAPABILITY_SET{$_}] = 1; sl@0: } sl@0: } sl@0: elsif (/^NONE$/) { sl@0: @caps = (); sl@0: } sl@0: if (defined $CAPABILITY_SET{$_}) { sl@0: $caps[$CAPABILITY_SET{$_}] = $set_val; sl@0: } sl@0: } sl@0: sl@0: # Build the ascii bit string. Bit 0 is the left most bit sl@0: for (my $i = 0; $i <= $#caps; $i++) { sl@0: $output .= (defined $caps[$i] && $caps[$i] ? "1" : "0"); sl@0: } sl@0: sl@0: return $output; sl@0: } sl@0: sl@0: # Creates a hex representation of the DER encoding of an arbitrary length bit string sl@0: sub encode_bit_string($$) { sl@0: my ($text, $octets) = @_; sl@0: sl@0: # Bit string in hex including padding length octet sl@0: my $bit_str = ""; sl@0: my $bit_str_octets = 1; # one octet for padding sl@0: sl@0: # Current byte sl@0: my $byte = 0; sl@0: my $len = length($text); sl@0: sl@0: if ($len == 0) { sl@0: $$octets+=2; sl@0: return "03:00"; sl@0: } sl@0: sl@0: my $i = 0; sl@0: while ($i < $len) { sl@0: sl@0: # Read the ith character and insert it in the correct place in the byte sl@0: # (fill from the left) sl@0: my $c = substr($text, $i, 1); sl@0: if ($c eq "1") { sl@0: $byte |= (1 << (7 - ($i % 8))); sl@0: } sl@0: elsif ($c ne "0") { sl@0: die "Invalid character $c in bit string $text"; sl@0: } sl@0: sl@0: if (++$i % 8 == 0) { sl@0: # Received 8 bits so output byte in hex sl@0: if ($bit_str ne "") { sl@0: $bit_str .= ":"; sl@0: } sl@0: $bit_str .= sprintf("%2.2x", $byte); sl@0: $bit_str_octets++; sl@0: $byte = 0; sl@0: } sl@0: } sl@0: # Pad any remaining bits / make sure 0 is output for empty string sl@0: if ($byte != 0 || $bit_str_octets == 1) { sl@0: if ($bit_str ne "") { sl@0: $bit_str .= ":"; sl@0: } sl@0: $bit_str .= sprintf("%2.2x", $byte); sl@0: $bit_str_octets++; sl@0: } sl@0: sl@0: my $pad_length = "00"; sl@0: if ($len % 8 > 0) { sl@0: # If this isn't a multiple of 8 bits then calculated sl@0: # the number of padding bits added. sl@0: $pad_length = sprintf("%2.2x", 8 - ($len % 8)); sl@0: } sl@0: sl@0: # Octets used to store the length sl@0: my $bit_str_length_octets = 0; sl@0: my $bit_str_length = &encode_length($bit_str_octets, \$bit_str_length_octets); sl@0: $$octets += 1 + $bit_str_length_octets + $bit_str_octets; sl@0: sl@0: return "$DER_BITSTRING_TAG:$bit_str_length:$pad_length:$bit_str"; sl@0: } sl@0: sl@0: # Return a hex represenation of the length using DER primitive (definate length encoding) sl@0: sub encode_length($$) { sl@0: my ($num, $octets) = @_; sl@0: sl@0: if ($num < 128) { sl@0: # Number is < 128 so encode in short form sl@0: $$octets++; sl@0: return sprintf("%2.2x", $num); sl@0: } sl@0: else { sl@0: # Number >= 128 so encode in long form sl@0: my $length_octets = 0; sl@0: my $base256 = &to_hex_base256($num, \$length_octets); sl@0: if ($length_octets > 127) {die "Encoding overflow.";} sl@0: sl@0: $$octets += 1 + $length_octets; sl@0: sl@0: # Set the top bit of the length octet to indicate long form sl@0: return "" . sprintf("%2.2x", ($length_octets | 0x80)) . ":$base256"; sl@0: } sl@0: } sl@0: sl@0: # Convert an integer into an ascii hex representation in base 256 sl@0: # $num - the number to encode sl@0: # $octets - refernce to the octet count to increment sl@0: sub to_hex_base256($$) { sl@0: my ($num, $octets) = @_; sl@0: sl@0: my $base256 = ""; sl@0: $num = int($num); sl@0: while ($num > 0) { sl@0: my $hexoctet = sprintf("%2.2x", $num & 0xFF); sl@0: if ($base256 ne "") { sl@0: $base256 = "$hexoctet:$base256"; sl@0: } sl@0: else { sl@0: $base256 = $hexoctet; sl@0: } sl@0: $num >>= 8; sl@0: $$octets++; sl@0: } sl@0: if ($base256 eq "") { sl@0: $base256 = "0"; sl@0: $$octets++; sl@0: } sl@0: return $base256; sl@0: }