os/security/cryptoservices/certificateandkeymgmt/tder/dergen.pl
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     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 Digest::HMAC_MD5;
    20 use Digest::HMAC_SHA1;
    21 use Getopt::Long;
    22 
    23 # 0 = off
    24 # 1 = log parsing
    25 # 2 = log parsing + encoding
    26 # 3 = really verbose stuff
    27 my $DEBUG=0;
    28 
    29 # Turn on validation checks that attempt to only generate
    30 # valid DER encodings.
    31 my $VALIDATE=0;
    32 
    33 my $OID_PKCS = "1.2.840.113549.1";
    34 my $OID_PKCS7 ="${OID_PKCS}.7";
    35 my $OID_PKCS9 = "${OID_PKCS}.9";
    36 my $OID_PKCS9_CERTTYPES = "${OID_PKCS9}.22"; 
    37 my $OID_PKCS12 = "${OID_PKCS}.12";
    38 my $OID_PKCS12_BAGTYPES = "${OID_PKCS12}.10.1";
    39 my $OID_PKCS12_PBEIDS = "${OID_PKCS12}.1";
    40 
    41 my %OIDS = 
    42 	(
    43 	 "MD5"  => "1.2.840.113549.2.5",
    44 	 "SHA1" => "1.3.14.3.2.26",
    45 	 "X509CRL" => "1.3.6.1.4.1.3627.4",
    46 
    47 	 "PKCS7_DATA" => "${OID_PKCS7}.1",
    48 	 "PKCS7_SIGNEDDATA" => "${OID_PKCS7}.2",
    49 	 "PKCS7_ENVELOPEDDATA" => "${OID_PKCS7}.3",
    50 	 "PKCS7_SIGNEDANDENVELOPEDDATA" => "${OID_PKCS7}.4",
    51 	 "PKCS7_DIGESTEDDATA" => "${OID_PKCS7}.5",
    52 	 "PKCS7_ENCRYPTEDDATA" => "${OID_PKCS7}.6",	
    53 	 
    54 	 "PKCS9_CERTTYPES_PKCS12_X509" => "${OID_PKCS9_CERTTYPES}.1",
    55 	 "PKCS9_FRIENDLY_NAME" => "${OID_PKCS9}.20",
    56 	 "PKCS9_LOCAL_KEYID" => "${OID_PKCS9}.21",
    57 	 
    58 	 "PKCS12_BAGTYPES_KEYBAG" => "${OID_PKCS12_BAGTYPES}.1",
    59 	 "PKCS12_BAGTYPES_PKCS8SHROUDEDKEYBAG" => "${OID_PKCS12_BAGTYPES}.2",
    60 	 "PKCS12_BAGTYPES_CERTBAG" => "${OID_PKCS12_BAGTYPES}.3",
    61 	 "PKCS12_BAGTYPES_CRLBAG" => "${OID_PKCS12_BAGTYPES}.4",
    62 	 "PKCS12_BAGTYPES_SECRETBAG" => "${OID_PKCS12_BAGTYPES}.5",
    63 	 "PKCS12_BAGTYPES_SAFECONTENTSBAG" => "${OID_PKCS12_BAGTYPES}.6",
    64 
    65 	 "PKCS12_PBEIDS_SHAAND128BITRC4" => "${OID_PKCS12_PBEIDS}.1",
    66 	 "PKCS12_PBEIDS_SHAAND40BITRC4" => "${OID_PKCS12_PBEIDS}.2",
    67 	 "PKCS12_PBEIDS_SHAAND3KEYTRIPLEDESCBC" => "${OID_PKCS12_PBEIDS}.3",
    68 	 "PKCS12_PBEIDS_SHAAND2KEYTRIPLEDESCBC" => "${OID_PKCS12_PBEIDS}.4",
    69 	 "PKCS12_PBEIDS_SHAAND128BITRC2CBC" => "${OID_PKCS12_PBEIDS}.5", 
    70 	 "PKCS12_PBEIDS_SHAAND40BITRC2CBC" => "${OID_PKCS12_PBEIDS}.6",
    71 
    72 	 # Symbian dev cert extensions
    73 	 "SYMBIAN_DEVICE_ID_LIST" => "1.2.826.0.1.1796587.1.1.1.1",
    74 	 "SYMBIAN_SID_LIST" => "1.2.826.0.1.1796587.1.1.1.4",
    75 	 "SYMBIAN_VID_LIST" => "1.2.826.0.1.1796587.1.1.1.5",
    76 	 "SYMBIAN_CAPABILITIES" => "1.2.826.0.1.1796587.1.1.1.6"
    77 
    78 );
    79 
    80 my $DER_BOOLEAN_TAG="01";
    81 my $DER_INTEGER_TAG="02";
    82 my $DER_BITSTRING_TAG="03";
    83 my $DER_OCTETSTRING_TAG="04";
    84 my $DER_NULL_TAG="05";
    85 my $DER_OID_TAG="06";
    86 my $DER_ENUMERATED_TAG="0A";
    87 my $DER_SEQUENCE_TAG="10";
    88 my $DER_SET_TAG="11";
    89 my $DER_UTF8STRING_TAG="0C";
    90 my $DER_PRINTABLESTRING_TAG="13";
    91 my $DER_IA5STRING_TAG="16";
    92 my $DER_UTCTIME_TAG="17";
    93 my $DER_BMPSTRING_TAG="1E";
    94 
    95 my $UNIVERSAL_CLASS="UNIVERSAL";
    96 my $APPLICATION_CLASS="APPLICATION";
    97 my $CONTEXT_SPECIFIC_CLASS="CONTEXT-SPECIFIC";
    98 my $PRIVATE_CLASS="PRIVATE";
    99 
   100 my %PARSE = 
   101 	(
   102 	 "BOOL" => \&parseBoolean,
   103 	 "BOOLEAN" => \&parseBoolean,
   104 	 "BIGINTEGER" => \&parseBigInteger,
   105 	 "BITSTRING" => \&parseBitString,
   106 	 "BITSTRING_WRAPPER" => \&parseBitStringWrapper,
   107 	 "BMPSTRING" => \&parseBmpString,
   108 	 "BMPSTRING_FILE" => \&parseBmpStringFile,
   109 	 "ENUMERATED" => \&parseEnumerated,
   110 	 "IA5STRING" => \&parseIA5String,
   111 	 "IA5STRING_FILE" => \&parseIA5StringFile,
   112 	 "INCLUDE" => \&parseInclude,
   113 	 "INCLUDE_BINARY_FILE" => \&parseIncludeBinaryFile,
   114 	 "INTEGER" => \&parseInteger,
   115 	 "INT" => \&parseInteger,
   116 	 "IMPLICIT" => \&parseImplicit,
   117 	 "ENCRYPT" => \&parseEncrypt,
   118 	 "EXPLICIT" => \&parseExplicit,
   119 	 "HASH" => \&parseHash,
   120 	 "HMAC" => \&parseHmac,
   121 	 "NULL" => \&parseNull,
   122 	 "OCTETSTRING" => \&parseOctetString,
   123 	 "OUTPUT_BINARY_FILE" => \&parseOutputFile,
   124 	 "OID" => \&parseOid,
   125 	 "PRINTABLESTRING" => \&parsePrintableString,
   126 	 "PRINTABLESTRING_FILE" => \&parsePrintableStringFile,
   127 	 "RAW" => \&parseRaw,
   128 	 "SEQUENCE" => \&parseSequence,
   129 	 "SEQ" => \&parseSequence,
   130 	 "SET" => \&parseSet,
   131 	 "SHELL" => \&parseShell,
   132 	 "SIGN" => \&parseSign,
   133 	 "UTCTIME" => \&parseUtcTime,
   134 	 "UTF8STRING" => \&parseUtf8String,
   135 	 "UTF8STRING_FILE" => \&parseUtf8StringFile,
   136 	 );
   137 
   138 my $TABS = "";
   139 
   140 &main;
   141 exit(0);
   142 
   143 sub main() {
   144 	my $hex;
   145 	my $out;
   146 	my $in;	
   147 	my @lines;
   148 
   149 	GetOptions('debug=i' => \$DEBUG,
   150 			   'hex' => \$hex, 
   151 			   'in=s' => \$in,
   152 			   'out=s' => \$out);
   153 
   154 	if (! defined $in) {
   155 		$in = $ARGV[0];
   156 	}
   157 
   158 	if (! defined $out) {
   159 		$out = $ARGV[1];
   160 	}
   161 
   162 	if (defined $in) {
   163 		@lines = readFile($in);
   164 	}
   165 	else {
   166 		die "No input file specified.\n";
   167 	}
   168 
   169 	if (defined $out) {
   170 		open OUT, ">$out" || die "Cannot open output file $out";
   171 	}
   172 	else {
   173 		*OUT = *STDOUT;
   174 	}
   175 
   176 	my $oc = 0;
   177 	my $asnHex = parseScript(\@lines, \$oc);
   178 	$asnHex = tidyHex($asnHex);
   179 
   180 	if ((!defined $hex) && (defined $out)) {
   181 		binmode(OUT);
   182 		print OUT toBin($asnHex);
   183 	}
   184 	elsif (defined $out) {
   185 		print OUT $asnHex;
   186 	}
   187 	else {
   188 		print $asnHex;
   189 	}
   190 
   191 	close OUT;
   192 }
   193 
   194 sub tidyHex($) {
   195 	my ($input) = @_;	
   196 	$input =~ s/:+/:/g;
   197 	$input =~ s/(^:|:$)//g;
   198 	return uc($input);
   199 }
   200 
   201 sub toBin($) {
   202 	my ($asnHex) = @_;
   203 
   204 	$asnHex =~ s/[\s:]//g;
   205 	$asnHex = uc($asnHex);
   206 	
   207 	my $len = length($asnHex);
   208 	if ($len % 2 != 0) {
   209 		die "toBin: hex string contains an odd number ($len) of octets.\n$asnHex\n";
   210 	}
   211 
   212 	my $binary;
   213 	$binary .= pack("H${len}", $asnHex);
   214 #	for (my $i = 0; $i < length($asnHex); $i+=2) {
   215 #		$binary .= pack('C', substr($asnHex, $i, 2));
   216 #	}
   217 	return $binary;
   218 }
   219 
   220 sub parseScript($$;$) {
   221 	my ($lines, $oc, $params) = @_;
   222 	my $derHex = "";
   223 
   224 	nest();
   225 	substVars($lines, $params);
   226 
   227 	while (my $line = shift @$lines) {
   228 		chomp($line);
   229 
   230 		# Remove leading spaces
   231 		$line =~ s/^\s*//g;
   232   
   233 		# skip comments 
   234 		next if ($line =~ /^\/\//);
   235 
   236 		if ($DEBUG == 3) {
   237 			print "${TABS}:PARSE parseScript: $line\n";
   238 		}
   239 
   240 		my $argString;
   241 		my $cmd;
   242 		if ($line =~ /(\w+)\s*\{/ ) {
   243 			# parse block commands e.g. large integer
   244 			$cmd = uc($1);
   245 			
   246 			$line =~ s/.*\{//g;
   247 			while (defined $line && !($line =~ /(^|[^\\]+)\}/) ) {
   248 				$argString .= $line;
   249 				$line = shift(@$lines);				
   250 			}
   251 			if (defined $line) {
   252 				# append everything up to the closing curly bracket
   253 				$line =~ s/(^|[^\\])\}.*/$1/g;
   254 				$argString .= $line;
   255 			}
   256 		}	
   257 		elsif ($line =~ /(\w+)\s*=*(.*)/) {
   258 			# parse commands of the form key = value
   259 			$cmd = uc($1);
   260 			$argString = defined $2 ? $2 : "";			
   261 		}
   262 
   263 		if (defined $cmd) {
   264 			if ($cmd =~ /^END/) {
   265 				leaveNest();
   266 				if ($DEBUG) {
   267 					print "${TABS}:PARSE END\n";
   268 				}
   269 				return $derHex;
   270 			}
   271 			elsif (! defined $PARSE{$cmd}) {
   272 				die "parseScript: Unknown command: $cmd\n";
   273 			}
   274 			else {
   275 				if ($DEBUG) {
   276 					print "${TABS}:PARSE CMD=$cmd";					
   277 					if ($argString ne "") {print " ARG: $argString";}
   278 					print "\n";
   279 				}
   280 				
   281 				# Substitue variables in argString
   282 				$derHex .= ":" . &{$PARSE{$cmd}}($argString, $oc, $lines);
   283 			}
   284 		}
   285 
   286 	}
   287 	leaveNest();
   288 	return $derHex;
   289 }
   290 
   291 sub substVars($$) {
   292 	my ($lines, $params) = @_;
   293 
   294 	if (! defined $params) {
   295 		@$params = ();
   296 	}
   297 
   298 	for (my $i = 0; $i < scalar(@$lines); $i++) {
   299 		my $line = @$lines[$i];
   300 		my $paramIndex = 1;
   301 
   302 		# For each parameter search for the a use of $N where
   303 		# N is the index of the parameter and replace $N with the
   304 		# value of the parameter
   305 		foreach (@$params) {
   306 			$line =~ s/\$${paramIndex}(\D|$)/$_$1/g;	
   307 			++$paramIndex;
   308 		}
   309 		
   310 		# Remove any unused parameters
   311 		$line =~ s/\$\d+//g;
   312 		@$lines[$i] = $line;
   313 	}
   314 }
   315 
   316 sub readFile($) {
   317 	my ($fileName) = @_;
   318 	my $inFile;
   319 
   320 	if ($DEBUG) {
   321 		print "readFile, $fileName\n";
   322 	}
   323 
   324 	open($inFile, $fileName) || die "readFile: cannot open $fileName\n";	
   325 	my @lines = <$inFile>;
   326 	close $inFile;
   327 
   328 	return @lines;
   329 }
   330 
   331 sub parseBitString($$;$) {
   332 	my ($argString, $oc, $lines) = @_;	
   333 	return encodeBitString($argString, $oc);
   334 }
   335 
   336 sub parseBitStringWrapper($$;$) {
   337 	my ($argString, $oc, $lines) = @_;	
   338 
   339 	my $contents_oc = 0;
   340 	my $contents = parseScript($lines, \$contents_oc);
   341 
   342 	my $binary = toBin($contents);
   343 	my $bitCount = $contents_oc * 8;
   344 	my $bitStr = unpack("B${bitCount}", $binary);
   345 
   346 	# remove trailing zeros - breaks signatures so disable for the moment
   347 	# $bitStr =~ s/0*$//g;
   348 	
   349 	return encodeBitString($bitStr, $oc);
   350 }
   351 
   352 sub parseBmpString($$;$) {
   353 	my ($argString, $oc, $lines) = @_;	
   354 	
   355 	my $bmpString_oc = 0;
   356 	my $bmpString = asciiToBmpString($argString, \$bmpString_oc);
   357 	return encodeBmpString($bmpString, $bmpString_oc, $oc);
   358 }
   359 
   360 sub parseBmpStringFile($$;$) {
   361 	my ($binFName, $oc, $lines) = @_;
   362 	$binFName =~ s/\s*//g;
   363 	
   364 	my $bmpString_oc = 0;
   365 	my $bmpString = encodeBinaryFile($binFName, \$bmpString_oc);	
   366 	
   367 	return encodeBmpString($bmpString, $bmpString_oc, $oc);
   368 }
   369 
   370 sub parseBoolean($$;$) {
   371 	my ($argString, $oc, $lines) = @_;
   372 	
   373 	$argString =~ s/\s//g;
   374 	$argString = lc($argString);
   375 
   376 	my $bool;
   377 	if ($argString eq "t" || $argString eq "true" || $argString eq "1") {
   378 		$bool = 1;
   379 	}
   380 	elsif ($argString eq "f" || $argString eq "false" || $argString eq "0") {
   381 		$bool = 0;
   382 	}
   383 	else {
   384 		die "parseBoolean: Invalid boolean value \'$argString\'";
   385 	}
   386 	
   387 	return encodeBoolean($bool, $oc);
   388 }
   389 
   390 sub parseHash($$;$) {
   391 	my ($argString, $oc, $lines) = @_;
   392 	my ($algorithm) = getArgs($argString);
   393 
   394 	if (! defined $algorithm) {
   395 		die "parseHash: missing algortithm";
   396 	}
   397 
   398 	my $hashIn_oc = 0;
   399 	my $hashIn = parseScript($lines, \$hashIn_oc);
   400 
   401 	my $hashInFName = '_hashin.tmp';
   402 	my $hashOutFName = '_hashout.tmp';
   403 
   404 	# Create binary hash file
   405 	my $hashInFh;
   406 	open($hashInFh, ">$hashInFName") or die "Cannot create $hashInFName";
   407 	binmode($hashInFh);
   408 	print $hashInFh toBin($hashIn);
   409 	close $hashInFh;
   410 
   411 	my @command = ("cmd",
   412 				   "/C \"openssl dgst -${algorithm} -binary $hashInFName > $hashOutFName\"");	
   413 	if ($DEBUG == 1) {
   414 		print "${TABS}:parseHash:" . join(" ", @command) . "\n";
   415 	}
   416 
   417 	if ((my $err = system(@command)) != 0) {
   418 		die "parseHash: " . join(" ", @command) . "\nreturned error $err";
   419 	}
   420 
   421 	my $derHex = parseIncludeBinaryFile($hashOutFName, $oc);
   422 	
   423 	if (! $DEBUG) {
   424 		unlink($hashInFName);
   425 		unlink($hashOutFName);
   426 	}
   427 	return $derHex;
   428 }
   429 
   430 sub parseHmac($$;$) {
   431 	my ($argString, $oc, $lines) = @_;
   432 	my ($algorithm, $key) = getArgs($argString);
   433 
   434 	if (! defined $algorithm) {
   435 		die "parseHmac: missing algortithm";
   436 	}
   437 	$algorithm = uc($algorithm);
   438 	if (! $algorithm =~ /MD5|SHA1/) {
   439 		die "parseHmac: invalid algorithm $algorithm";
   440 	}
   441 
   442 	if (! defined $key) {
   443 		die "parseHmac: missing key";
   444 	}
   445 
   446 	my $hmacIn_oc = 0;
   447 	my $hmacIn = toBin(parseScript($lines, \$hmacIn_oc));
   448 	my $hmac;
   449 	my $binKey = toBin($key);
   450 
   451 	if ($algorithm eq "SHA1") {
   452 
   453 		$hmac = Digest::HMAC_SHA1->new($binKey);
   454 	}
   455 	else {
   456 		$hmac = Digest::HMAC_MD5->new($binKey);
   457 	}
   458 	$hmac->add($hmacIn);
   459 	my $digest = $hmac->digest;
   460 	$$oc += length($digest);
   461 
   462 	return toHex($digest);
   463 }
   464 
   465 sub parseIA5String($$;$) {
   466 	my ($argString, $oc, $lines) = @_;	
   467 	
   468 	my $ia5String_oc = 0;
   469 	my $ia5String = asciiToIA5String($argString, \$ia5String_oc);
   470 	return encodeIA5String($ia5String, $ia5String_oc, $oc);
   471 }
   472 
   473 
   474 sub parseIA5StringFile($$;$) {
   475 	my ($binFName, $oc, $lines) = @_;
   476 	$binFName =~ s/\s*//g;
   477 	
   478 	my $ia5String_oc = 0;
   479 	my $ia5String = encodeBinaryFile($binFName, \$ia5String_oc);	
   480 	
   481 	return encodeIA5String($ia5String, $ia5String_oc, $oc);
   482 }
   483 
   484 sub parseIncludeBinaryFile($$;$) {
   485 	my ($binFName, $oc, $lines) = @_;
   486 	$binFName =~ s/\s*//g;
   487 	
   488 	return encodeBinaryFile($binFName, $oc);
   489 }
   490 
   491 sub parseInclude($$$) {
   492 	my ($argString, $oc, $lines) = @_;   
   493 	my @args = getArgs($argString);
   494 
   495    	my $fileName = shift(@args);
   496 	if (! (defined $fileName && $fileName ne "")) {
   497 		die "parseInclude: Filename not specified\n";
   498 	}
   499 
   500 	my $derHex = "";
   501 	my @lines = readFile($fileName);	
   502 	$derHex = parseScript(\@lines, $oc, \@args);
   503 	return $derHex;
   504 }
   505 
   506 sub parseInteger($$;$) {
   507 	my ($argString, $oc, $lines) = @_;
   508 	
   509 	$argString =~ s/\s//g;
   510 	return encodeInteger($argString, $oc);
   511 }
   512 
   513 sub parseBigInteger($$;$) {
   514 	my ($argString, $oc, $lines) = @_;
   515 	
   516 	$argString =~ s/\s//g;
   517 	return encodeBigInteger($argString, $oc);
   518 }
   519 
   520 sub parseEncrypt($$;$) {
   521 	my ($argString, $oc, $lines) = @_;		
   522 	my ($cipher, $key, $iv) = getArgs($argString);
   523 
   524 	if (! defined $cipher) {
   525 		die "parseEncrypt: missing cipher\n";
   526 	}
   527 
   528 	if (! defined $key) {
   529 		die "parseEncrypt: missing key\n";
   530 	}
   531 
   532 	my $plainText_oc = 0;
   533 	my $plainText = parseScript($lines, \$plainText_oc);
   534 
   535 	my $plainTextFName = '_plaintext.tmp';
   536 	my $cipherTextFName = '_ciphertext.tmp';
   537 
   538 	# Create binary plaintext file
   539 	my $plainTextFh;
   540 	open($plainTextFh, ">$plainTextFName") or die "Cannot create $plainTextFName";
   541 	binmode($plainTextFh);
   542 	print $plainTextFh toBin($plainText);
   543 	close $plainTextFh;
   544 
   545 	my @command = ('openssl', 
   546 				   'enc', 
   547 				   "-${cipher}", 
   548 				   '-e',
   549 				   '-K', $key,
   550 				   '-in', $plainTextFName, 
   551 				   '-out', $cipherTextFName);
   552 
   553 	if (defined $iv) {
   554 		push @command, '-iv', $iv;
   555 	}
   556 	
   557 	if ($DEBUG == 1) {
   558 		print "${TABS}:parseEncrypt:" . join(" ", @command) . "\n";
   559 	}
   560 
   561 	if ((my $err = system(@command)) != 0) {
   562 		die "parseEncrypt: " . join(" ", @command) . "\nreturned error $err";
   563 	}
   564 
   565 	my $derHex = parseIncludeBinaryFile($cipherTextFName, $oc);
   566 	
   567 	if (! $DEBUG) {
   568 		unlink($plainTextFName);
   569 		unlink($cipherTextFName);
   570 	}
   571 	return $derHex;
   572 }
   573 
   574 sub parseEnumerated($$;$) {
   575 	my ($argString, $oc, $lines) = @_;
   576 	
   577 	$argString =~ s/\s//g;
   578 	return encodeEnumerated($argString, $oc);
   579 }
   580 
   581 sub parseExplicit($$;$) {
   582 	my ($argString, $oc, $lines) = @_;	
   583 	my ($tagNumber, $class) = getArgs($argString);
   584 
   585 	if (! defined $tagNumber || $tagNumber =~ /^\s*$/) {
   586 		$tagNumber = "0";
   587 	}
   588 	elsif (!($tagNumber =~ /^[0-9A-Fa-f]+$/)) {
   589 		die "parseExplicit: invalid tag number: \'$tagNumber\'";
   590 	}
   591 	$tagNumber = hex($tagNumber);
   592 
   593 	if (!defined $class || $class =~ /^\s*$/) {
   594 		$class = $CONTEXT_SPECIFIC_CLASS;
   595 	}
   596 	else {
   597 		$class =~ s/\s*//g;
   598 		$class = uc($class);
   599 	}
   600 
   601 	if (! isValidClass($class)) {
   602 		die "parseExplicit: invalid class \'$class\'";
   603 	}
   604 	
   605 	my $nested_oc = 0;
   606 	my $nested = parseScript($lines, \$nested_oc);
   607 
   608 	return encodeExplicit($class, $tagNumber, $nested, $nested_oc, $oc);
   609 }
   610 
   611 sub parseImplicit($$;$) {
   612 	my ($argString, $oc, $lines) = @_;	
   613 	my ($tagNumber, $class) = getArgs($argString);
   614 
   615 	if (! defined $tagNumber || $tagNumber =~ /^\s*$/) {
   616 		$tagNumber = "0";
   617 	}
   618 	elsif (!($tagNumber =~ /^[0-9A-Fa-f]+$/)) {
   619 		die "parseImplicit: invalid tag number: \'$tagNumber\'";
   620 	}
   621 	$tagNumber = hex($tagNumber);
   622 
   623 	if (!defined $class || $class =~ /^\s*$/) {
   624 		$class = $CONTEXT_SPECIFIC_CLASS;
   625 	}
   626 	else {
   627 		$class =~ s/\s*//g;
   628 		$class = uc($class);
   629 	}
   630 
   631 	if (! isValidClass($class)) {
   632 		die "parseImplicit: invalid class \'$class\'";
   633 	}
   634 	
   635 	my $nested_oc = 0;
   636 	my $nested = tidyHex(parseScript($lines, \$nested_oc));
   637 
   638 	# De-construct the nested data to allow the underlying type tag to be
   639 	# changed. The output of parseScript had better be valid DER or this 
   640 	# will go horribly wrong !
   641 	my $uClass = "";
   642 	my $uConstructed = 0;
   643 	my $uTag = 0;
   644 	my $uLength = 0;
   645 	my $uValue = "";
   646 	getTlv($nested, \$uClass, \$uConstructed, \$uTag, \$uLength, \$uValue);
   647 
   648 	if ($DEBUG == 2) {
   649 		print "${TABS}parseImplicit: underlyingType \'$uTag\'\n";
   650 	}
   651 	
   652 	# This only works for low tag numbers because we are assuming that the type
   653 	# tag is a single octet
   654 	return encodeImplicit($class, $uConstructed, $tagNumber, $uValue, $uLength, $oc);
   655 }
   656 
   657 sub parseNull($$;$) {
   658 	my ($argString, $oc, $lines) = @_;
   659 	
   660 	return encodeNull($oc);
   661 }
   662 
   663 sub parseOctetString($$;$) {
   664 	my ($argString, $oc, $lines) = @_;	
   665 	
   666 	my $octetString_oc = 0;
   667 	my $octetString = parseScript($lines, \$octetString_oc);
   668 
   669 	return encodeOctetString($octetString, $octetString_oc, $oc);
   670 }
   671 
   672 sub parseOid($$;$) {
   673 	my ($argString, $oc, $lines) = @_;
   674 	$argString =~ s/\s//g;
   675 	$argString = uc($argString);
   676 
   677 	if (! defined $argString) {
   678 		die "parseOid: Missing OID value.";
   679 	}
   680 
   681 	foreach (keys %OIDS) {
   682 		if ($argString =~ /$_/) {
   683 			$argString =~ s/\Q$_\E/$OIDS{$_}/g;
   684 		}
   685 	}
   686 	return encodeOid($argString, $oc);
   687 }
   688 
   689 sub parseOutputFile($$;$) {
   690 	my ($argString, $oc, $lines) = @_;	
   691 	my ($outputFile,$echo) = split(/,/, $argString);
   692 	
   693 	if (! defined $outputFile) {
   694 		die "parseOutputFile: Missing file-name.\n";
   695 	}
   696 	
   697 	my $content_oc = 0;
   698 	my $content = parseScript($lines, \$content_oc);
   699 
   700 	my $outFh;
   701 	if (! open($outFh, ">${outputFile}")) {
   702 		die "parseOutputFile: Cannot create $outputFile\n";
   703 	}
   704 	binmode($outFh);
   705 	print $outFh toBin($content);
   706 	close $outFh;
   707 	
   708 	# If echo is specified then include then contents of the output 
   709 	# file at this point in the stream.
   710 	if (defined $echo && $echo =~ /(1|t|true)/i) {
   711 		$$oc += $content_oc;
   712 		return $content;		
   713 	}
   714 	else {
   715 		return "";
   716 	}
   717 }
   718 
   719 sub parsePrintableString($$;$) {
   720 	my ($argString, $oc, $lines) = @_;	
   721 	
   722 	my $printableString_oc = 0;
   723 	my $printableString = asciiToPrintableString($argString, \$printableString_oc);
   724 	return encodePrintableString($printableString, $printableString_oc, $oc);
   725 }
   726 
   727 sub parsePrintableStringFile($$;$) {
   728 	my ($binFName, $oc, $lines) = @_;
   729 	$binFName =~ s/\s*//g;
   730 	
   731 	my $printableString_oc = 0;
   732 	my $printableString = encodeBinaryFile($binFName, \$printableString_oc);	
   733 	
   734 	return encodePrintableString($printableString, $printableString_oc, $oc);
   735 }
   736 
   737 sub parseRaw($$;$) {
   738 	my ($argString, $oc, $lines) = @_;
   739 	$argString =~ s/\s//g;
   740 	$argString = uc($argString);
   741 	
   742 	my $asnHex = "";
   743 	if (! ($argString =~ /(([A-Fa-f\d][A-Fa-f\d])[ :]*)+/)) {
   744 		die "parseRaw: Invalid hex string: $argString\n";
   745 	}
   746 	my $binary = toBin($argString);
   747 	$$oc += length($binary);
   748 	return tidyHex(toHex($binary));
   749 }
   750 
   751 sub parseSequence($$;$) {
   752 	my ($argString, $oc, $lines) = @_;	
   753 	
   754 	my $sequence_oc = 0;
   755 	my $sequence = parseScript($lines, \$sequence_oc);
   756 
   757 	return encodeSequence($sequence, $sequence_oc, $oc);
   758 }
   759 
   760 sub parseSet($$;$) {
   761 	my ($argString, $oc, $lines) = @_;	
   762 	
   763 	my $set_oc = 0;
   764 	my $set = parseScript($lines, \$set_oc);
   765 
   766 	return encodeSet($set, $set_oc, $oc);
   767 }
   768 
   769 # Create a PKCS#7 signed data object for a chunk of data using 
   770 # OpenSSL's SMIME command
   771 sub parseSign($$;$) {
   772 	my ($argString, $oc, $lines) = @_;
   773 	my ($signerCert, $signerKey) = getArgs($argString);
   774 
   775 	if (! defined $signerCert) {
   776 		die "parseSign: missing signing certificate";
   777 	}
   778 	elsif (! -f $signerCert) {
   779 		die "parseSign: signing certificate \'$signerCert\' does not exist.";
   780 	}
   781 
   782 	if (! defined $signerKey) {
   783 		die "parseSign: missing signing certificate";
   784 	}
   785 	elsif (! -f $signerKey) {
   786 		die "parseSign: signing key \'$signerKey\' does not exist.";
   787 	}
   788 
   789 	my $unsigned_oc = 0;
   790 	my $unsigned = parseScript($lines, \$unsigned_oc);
   791 
   792 	my $unsignedFName = '_unsigned.tmp';
   793 	my $signedFName = '_signed.tmp';
   794 
   795 	# Create binary unsigned data file
   796 	my $unsignedFh;
   797 	open($unsignedFh, ">$unsignedFName") or die "Cannot create $unsignedFName";
   798 	binmode($unsignedFh);
   799 	print $unsignedFh toBin($unsigned);
   800 	close $unsignedFh;
   801 
   802 	my @command = ('openssl', 
   803 				   'smime', 
   804 				   '-pk7out', 
   805 				   '-nodetach',
   806 				   '-outform',
   807 				   'der',
   808 				   '-sign',
   809 				   '-signer',
   810 				   $signerCert,
   811 				   '-inkey',
   812 				   $signerKey,
   813 				   '-in', $unsignedFName, 
   814 				   '-out', $signedFName);
   815 
   816 	if ($DEBUG == 1) {
   817 		print "${TABS}:parseSign:" . join(" ", @command) . "\n";
   818 	}
   819 
   820 	if ((my $err = system(@command)) != 0) {
   821 		die "parseSign: " . join(" ", @command) . "\nreturned error $err";
   822 	}
   823 
   824 	my $derHex = parseIncludeBinaryFile($signedFName, $oc);
   825 	
   826 	if (! $DEBUG) {
   827 		unlink($unsignedFName);
   828 		unlink($signedFName);
   829 	}
   830 	return $derHex;
   831 }
   832 
   833 sub parseShell($$;$) {
   834 	my ($argString, $oc, $lines) = @_;
   835 	my @command = getArgs($argString);
   836 
   837 	if (scalar(@command) < 1) {
   838 		die "parseShell: no arguments";
   839 	}
   840 
   841 	if ($DEBUG == 1) {
   842 		print "${TABS}:parseShell:" . join(" ", @command) . "\n";
   843 	}
   844 
   845 	if ((my $err = system(@command)) != 0) {
   846 		die "parseShell: " . join(" ", @command) . "\nreturned error $err";
   847 	}
   848 	return "";
   849 }
   850 
   851 sub parseUtcTime($$;$) {
   852 	my ($time, $oc, $lines) = @_;	
   853 	$time =~ s/\s//g;
   854 
   855 	my $time_oc = length($time);
   856 	return encodeUtcTime(toHex($time), $time_oc, $oc);
   857 }
   858 
   859 sub parseUtf8String($$;$) {
   860 	my ($argString, $oc, $lines) = @_;	
   861 	
   862 	my $utf8String_oc = 0;
   863 	my $utf8String = asciiToUtf8String($argString, \$utf8String_oc);
   864 	return encodeUtf8String($utf8String, $utf8String_oc, $oc);
   865 }
   866 
   867 sub parseUtf8StringFile($$;$) {
   868 	my ($binFName, $oc, $lines) = @_;
   869 	$binFName =~ s/\s*//g;
   870 	
   871 	my $utf8String_oc = 0;
   872 	my $utf8String = encodeBinaryFile($binFName, \$utf8String_oc);	
   873 	
   874 	return encodeUtf8String($utf8String, $utf8String_oc, $oc);
   875 }
   876 
   877 sub toHex($) {
   878 	my ($bin) = @_;
   879 	my $hex = unpack("H" . (length($bin) * 2), $bin);
   880 	$hex =~ s/(..)/$1:/g;
   881 	return $hex;
   882 }
   883 
   884 sub encodeBinaryFile($$) {
   885 	my ($binFName, $oc) = @_;
   886 
   887 	my $binFH;
   888 	open($binFH, "$binFName") || die "encodeBinaryFile: Cannot open $binFName\n";
   889 	binmode($binFH);
   890 
   891 	my $binBuf;
   892 	my $readBuf;
   893 	my $derHex = "";
   894 	while (my $len = sysread($binFH, $readBuf, 1024)) {
   895 		$binBuf .= $readBuf;
   896 		$$oc += $len;
   897 	}
   898 	close $binFH;	
   899 
   900 	return toHex($binBuf);;
   901 }
   902 
   903 # Creates a hex representation of the DER encoding of an arbitrary length bit string
   904 sub encodeBitString($$) {
   905 	my ($text, $oc) = @_;
   906 
   907 	# Bit string in hex including padding length octet
   908 	my $bit_str = "";
   909 	my $bit_str_oc = 1; # one octet for padding
   910 
   911 	# Current byte
   912 	my $byte = 0;	
   913 	my $len = length($text);
   914 
   915 	if ($len == 0) {
   916 		$$oc+=2;
   917 		return "03:00";
   918 	}
   919 
   920 	my $i = 0;
   921 	while ($i < $len) {		
   922 
   923 		# Read the ith character and insert it in the correct place in the byte
   924 		# (fill from the left)
   925 		my $c = substr($text, $i, 1);		
   926 		if ($c eq "1") {
   927 			$byte |= (1 << (7 - ($i % 8)));
   928 		}
   929 		elsif ($c ne "0") {
   930 			die "Invalid character $c in bit string $text";
   931 		}
   932 
   933 		if (++$i % 8 == 0) {
   934 			# Received 8 bits so output byte in hex
   935 			if ($bit_str ne "") {
   936 				$bit_str .= ":";
   937 			}
   938 			$bit_str .= sprintf("%2.2x", $byte);
   939 			$bit_str_oc++;
   940 			$byte = 0;
   941 		}
   942 	}
   943 	# Pad any remaining bits / make sure 0 is output for empty string
   944 	if ($byte != 0 || $bit_str_oc == 1) {
   945 		if ($bit_str ne "") {
   946 			$bit_str .= ":";
   947 		}
   948 		$bit_str .= sprintf("%2.2x", $byte);
   949 		$bit_str_oc++;
   950 	}
   951 
   952 	my $pad_length = "00";
   953 	if ($len % 8 > 0) {
   954 		# If this isn't a multiple of 8 bits then calculated
   955 		# the number of padding bits added.
   956 		$pad_length = sprintf("%2.2x", 8 - ($len % 8));
   957 	}
   958 	
   959 	if ($DEBUG == 2) {
   960 		print "${TABS}:ENC:encodeBitString, $bit_str_oc\n";
   961 	}
   962 	return encodeTlv($oc, $DER_BITSTRING_TAG, $bit_str_oc, "$pad_length:$bit_str");
   963 }
   964 
   965 # Creates a hex represenation of the DER encoding of a BMPSTRING
   966 sub encodeBmpString($$$) {
   967 	my ($bmpString, $bmpString_oc, $oc) = @_;
   968 
   969 	if ($DEBUG == 2) {
   970 		print "${TABS}:ENC:encodeBmpString, $bmpString_oc\n";
   971 	}
   972 	return encodeTlv($oc, $DER_BMPSTRING_TAG, $bmpString_oc, $bmpString);
   973 }
   974 
   975 sub encodeBoolean($$) {
   976 	my ($value, $oc) = @_;
   977 
   978 	my $boolean = "00";
   979 	if ($value) {
   980 		$boolean = "FF";
   981 	}
   982 
   983 	if ($DEBUG == 2) {
   984 		print "${TABS}:ENC:encodeBoolean, 1\n";
   985 	}
   986 	return encodeTlv($oc, $DER_BOOLEAN_TAG, 1, $boolean);
   987 }
   988 
   989 sub encodeEnumerated($$) {
   990 	my ($int, $oc) = @_;
   991 
   992 	$int =~ s/\s//g;
   993 
   994 	if (! ($int =~ /^-??\d+$/ || $int =~ /0x[0-9A-Fa-f]+/)) {
   995 		die "encodeEnumerated: Invalid argument: $int\n";
   996 	}
   997 	
   998 	if ($int =~ s/^0x//) {
   999 		$int = hex;
  1000 	}
  1001 	
  1002 	# Convert the enumerated to base 256 hex and find out how
  1003 	# many octets were required
  1004 	my $hex_enumerated_oc = 0;
  1005 	my $hex_enumerated = "";
  1006 	
  1007 	if ($int ne "") {
  1008 		$hex_enumerated = encodeBase256($int, \$hex_enumerated_oc);
  1009 	}
  1010 		
  1011 	if ($DEBUG == 2) {
  1012 		print "${TABS}:ENC: , $hex_enumerated_oc\n";
  1013 	}	
  1014 
  1015 	return encodeTlv($oc, $DER_ENUMERATED_TAG, $hex_enumerated_oc, $hex_enumerated);
  1016 }
  1017 
  1018 # explicit tags are always constructed
  1019 sub encodeExplicit($$$$) {
  1020 	my ($class, $tagNumber, $explicit, $explicit_oc, $oc) = @_;
  1021 
  1022 	if ($DEBUG == 2) {
  1023 		print "${TABS}:ENC: explicit, $explicit_oc\n";
  1024 	}
  1025 	return encodeTlv($oc, $tagNumber, $explicit_oc, $explicit, 1, $class);
  1026 }
  1027 
  1028 # Creates a hex represenation of the DER encoding of an IA5 string
  1029 sub encodeIA5String($$) {
  1030 	my ($ia5String, $ia5String_oc, $oc) = @_;
  1031 
  1032 	if ($DEBUG == 2) {
  1033 		print "${TABS}:ENC:encodeIA5String, $ia5String_oc\n";
  1034 	}
  1035 	return encodeTlv($oc, $DER_IA5STRING_TAG, $ia5String_oc, $ia5String);
  1036 }
  1037 
  1038 sub encodeImplicit($$$$$) {
  1039 	my ($class, $constructed, $tagNumber, $implicit, $implicit_oc, $oc) = @_;
  1040 
  1041 	if ($DEBUG == 2) {
  1042 		print "${TABS}:ENC: implicit, $implicit_oc\n";
  1043 	}
  1044 	return encodeTlv($oc, $tagNumber, $implicit_oc, $implicit, $constructed, $class);
  1045 }
  1046 
  1047 sub encodeBigInteger($$) {
  1048 	my ($hexString, $oc) = @_;
  1049 
  1050 	my $bin = toBin($hexString);
  1051 	my $int = toHex($bin);
  1052 	my $int_oc = length($bin);
  1053 
  1054 	if ($DEBUG == 2) {
  1055 		print "${TABS}:ENC: bigInteger, $int_oc\n";
  1056 	}
  1057 	return encodeTlv($oc, $DER_INTEGER_TAG, $int_oc, $int)
  1058 }
  1059 
  1060 sub encodeInteger($$) {
  1061 	my ($int, $oc) = @_;
  1062 
  1063 	$int =~ s/\s//g;
  1064 
  1065 	if (! ($int =~ /^-??\d+$/ || $int =~ /0x[0-9A-Fa-f]+/)) {
  1066 		die "encodeInteger: Invalid argument: $int\n";
  1067 	}
  1068 	
  1069 	if ($int =~ s/^0x//) {
  1070 		$int = hex;
  1071 	}
  1072 	
  1073 	# Convert the integer to base 256 hex and find out how
  1074 	# many octets were required
  1075 	my $hex_integer_oc = 0;
  1076 	my $hex_integer = "";
  1077 	
  1078 	if ($int ne "") {
  1079 		$hex_integer = encodeBase256($int, \$hex_integer_oc);
  1080 	}
  1081 		
  1082 	if ($DEBUG == 2) {
  1083 		print "${TABS}:ENC: integer, $hex_integer_oc\n";
  1084 	}	
  1085 
  1086 	return encodeTlv($oc, $DER_INTEGER_TAG, $hex_integer_oc, $hex_integer);
  1087 }
  1088 
  1089 sub encodeNull($) {
  1090 	my ($oc) = @_;	
  1091 	return encodeTlv($oc, $DER_NULL_TAG, 0, "");
  1092 }
  1093 
  1094 sub encodeOctetString($$$) {
  1095 	my ($octetString, $octetString_oc, $oc) = @_;
  1096 
  1097 	if ($DEBUG == 2) {
  1098 		print "${TABS}:ENC: octetString, $octetString_oc\n";
  1099 	}
  1100 	return encodeTlv($oc, $DER_OCTETSTRING_TAG, $octetString_oc, $octetString);
  1101 }
  1102 
  1103 sub encodeOid($$) {
  1104 	my ($text, $oc) = @_;
  1105 
  1106 	my @fields = split /\./, $text;
  1107 	
  1108 	if (! ($fields[0] >= 0 && $fields[0] <=2) ) { 
  1109 		die "Invalid OID: $text\n";
  1110 	}
  1111 	if (! ($fields[1] >= 0 && $fields[1] <= 39) ) {
  1112 		die "Invalid OID: $text";
  1113 	}
  1114 		
  1115 	my $oid = sprintf("%2.2x", (40 * $fields[0]) + $fields[1]);
  1116 	my $oid_oc = 1;
  1117 	shift @fields;
  1118 	shift @fields;
  1119 
  1120 	foreach (@fields) {		
  1121 		$oid .= ":" . encodeBase128($_, \$oid_oc);
  1122 	}
  1123 
  1124 	if ($DEBUG == 2) {
  1125 		print "${TABS}:ENC:encodeOid, $oid_oc\n";
  1126 	}
  1127 	return encodeTlv($oc, $DER_OID_TAG, $oid_oc, $oid);
  1128 }
  1129 
  1130 # Creates a hex represenation of the DER encoding of a PRINTABLE string
  1131 sub encodePrintableString($$$) {
  1132 	my ($printableString, $printableString_oc, $oc) = @_;
  1133 
  1134 	if ($DEBUG == 2) {
  1135 		print "${TABS}:ENC:encodePrintableString, $printableString_oc\n";
  1136 	}
  1137 	return encodeTlv($oc, $DER_PRINTABLESTRING_TAG, $printableString_oc, $printableString);
  1138 }
  1139 
  1140 sub encodeSet($$$) {
  1141 	my ($set, $set_oc, $oc) = @_;
  1142 
  1143 	if ($DEBUG == 2) {
  1144 		print "${TABS}:ENC: set, $set_oc\n";
  1145 	}
  1146 	return encodeTlv($oc, $DER_SET_TAG, $set_oc, $set, 1);
  1147 }
  1148 
  1149 sub encodeSequence($$$) {
  1150 	my ($sequence, $sequence_oc, $oc) = @_;
  1151 
  1152 	if ($DEBUG == 2) {
  1153 		print "${TABS}:ENC: sequence, $sequence_oc\n";
  1154 	}
  1155 	return encodeTlv($oc, $DER_SEQUENCE_TAG, $sequence_oc, $sequence, 1);
  1156 }
  1157 
  1158 sub encodeUtcTime($$$) {
  1159 	my ($utcTime, $utcTime_oc, $oc) = @_;
  1160 
  1161 	if ($DEBUG == 2) {
  1162 		print "${TABS}:ENC: UTCTime, $utcTime_oc\n";
  1163 	}
  1164 	return encodeTlv($oc, $DER_UTCTIME_TAG, $utcTime_oc, $utcTime);
  1165 }
  1166 
  1167 # Creates a hex represenation of the DER encoding of a UTF-8 string.
  1168 sub encodeUtf8String($$) {
  1169 	my ($utf8String, $utf8String_oc, $oc) = @_;
  1170 
  1171 	if ($DEBUG == 2) {
  1172 		print "${TABS}:ENC:encodeUTF8String, $utf8String_oc\n";
  1173 	}
  1174 	return encodeTlv($oc, $DER_UTF8STRING_TAG, $utf8String_oc, $utf8String);
  1175 }
  1176 
  1177 sub asciiToBmpString($$) {
  1178 	my ($input, $oc) = @_;
  1179 
  1180 	my $bmpString = "";
  1181 	my $input_len = length($input);
  1182 	$$oc += $input_len * 2;
  1183 
  1184 	for (my $i = 0; $i < $input_len; ++$i) {
  1185 		my $hex_val = ord(substr($input, $i, 1));
  1186 		if ($bmpString ne "") {
  1187 			$bmpString .= ":";
  1188 		}
  1189 		$bmpString .= sprintf(":00:%2.2x", $hex_val);
  1190 	}	
  1191 	return $bmpString;
  1192 }
  1193 
  1194 sub asciiToIA5String($$) {
  1195 	my ($input, $oc) = @_;
  1196 
  1197 	my $printableString = "";
  1198 	my $input_len = length($input);
  1199 	$$oc += $input_len;
  1200 
  1201 	for (my $i = 0; $i < $input_len; ++$i) {
  1202 		my $hex_val = ord(substr($input, $i, 1));
  1203 		if ($printableString ne "") {
  1204 			$printableString .= ":";
  1205 		}
  1206 		$printableString .= sprintf(":%2.2x", $hex_val);
  1207 	}	
  1208 	return $printableString;
  1209 }
  1210 
  1211 sub asciiToPrintableString($$) {
  1212 	my ($input, $oc) = @_;
  1213 
  1214 	my $ia5String = "";
  1215 	my $input_len = length($input);
  1216 	$$oc += $input_len;
  1217 
  1218 	for (my $i = 0; $i < $input_len; ++$i) {
  1219 		my $hex_val = ord(substr($input, $i, 1));
  1220 		if ($ia5String ne "") {
  1221 			$ia5String .= ":";
  1222 		}
  1223 		$ia5String .= sprintf(":%2.2x", $hex_val);
  1224 	}	
  1225 	return $ia5String;
  1226 }
  1227 
  1228 sub asciiToUtf8String($$) {
  1229 	my ($input, $oc) = @_;
  1230 
  1231 	my $utf8String = "";
  1232 	my $input_len = length($input);
  1233 	$$oc += $input_len;
  1234 
  1235 	for (my $i = 0; $i < $input_len; ++$i) {
  1236 		my $hex_val = ord(substr($input, $i, 1));
  1237 		if ($utf8String ne "") {
  1238 			$utf8String .= ":";
  1239 		}
  1240 		$utf8String .= sprintf(":%2.2x", $hex_val);
  1241 	}	
  1242 	return $utf8String;
  1243 }
  1244 
  1245 sub encodeBase128($$$) {
  1246 	my ($num, $oc) = @_;
  1247 
  1248 	my $base128 = "";
  1249 	$num = int($num);
  1250 	my $base128_length = 0;
  1251 
  1252 	while ($num > 0) {
  1253 		my $hexoctet;
  1254 
  1255 		if ($base128 eq "") {
  1256 			$hexoctet = sprintf("%2.2x", $num & 0x7f);
  1257 		}
  1258 		else {
  1259 			$hexoctet = sprintf("%2.2x", ($num & 0x7f) | 0x80);
  1260 		}
  1261 		
  1262 		if ($base128 eq "") {			
  1263 			$base128 = $hexoctet;	   
  1264 		}
  1265 		else {
  1266 			$base128 = "$hexoctet:$base128";
  1267 		}		
  1268 
  1269 		$num >>= 7;
  1270 		$base128_length++;
  1271 	}
  1272 	if ($base128 eq "") {
  1273 		$base128 = "00";
  1274 		$base128_length++;
  1275 	}
  1276 
  1277 	$$oc += $base128_length;
  1278 	
  1279 	if ($DEBUG == 2) {
  1280 		print "${TABS}:ENC: base128, $base128_length, $$oc\n";
  1281 	}
  1282 
  1283 	return $base128;
  1284 }
  1285 
  1286 # Return a hex represenation of the length using DER primitive (definate length encoding)
  1287 sub encodeLength($$) {
  1288 	my ($num, $oc) = @_;
  1289 
  1290 	if ($num < 128) {
  1291 		# Number is < 128 so encode in short form
  1292 		$$oc++;
  1293 		return sprintf("%2.2x", $num);
  1294 	}
  1295 	else {
  1296 		# Number >= 128 so encode in long form
  1297 		my $length_oc = 0;
  1298 		my $base256 = &encodeBase256($num, \$length_oc, 1);
  1299 		if ($length_oc > 127) {die "Encoding overflow.";}
  1300 		
  1301 		$$oc += 1 + $length_oc;
  1302 		
  1303 		# Set the top bit of the length octet to indicate long form		
  1304 		return "" . sprintf("%2.2x", ($length_oc | 0x80)) . ":$base256";
  1305 	}
  1306 }
  1307 
  1308 # Convert an integer into an ascii hex representation in base 256
  1309 # $num    - the number to encode
  1310 # $octets - refernce to the octet count to increment
  1311 # $unsigned - assume unsigned
  1312 sub encodeBase256($$) {
  1313 	my ($numIn, $oc, $unsigned) = @_;
  1314 
  1315 	my $base256 = "";
  1316 	my $num = int($numIn);	
  1317 
  1318 	while ($num != 0) {
  1319 		my $hexoctet = sprintf("%2.2x", $num & 0xFF);
  1320 		if ($base256 ne "") {
  1321 			$base256 = "$hexoctet:$base256";
  1322 		}
  1323 		else {
  1324 			$base256 = $hexoctet;
  1325 		}		
  1326 		$num >>= 8;
  1327 		$$oc++;
  1328 	}
  1329 	if ($base256 eq "") {
  1330 		$base256 = "00";
  1331 		$$oc++;
  1332 	}
  1333 
  1334 	# If the integer is +ve and the MSB is 1 then padd with a leading zero 
  1335 	# octet otherwise it will look -ve
  1336 	if ((! $unsigned) && $numIn > 0 && $base256 =~ /^:*[8ABCDEF]/i) {
  1337 		$base256 = "00:$base256";
  1338 		$$oc++;
  1339 	}
  1340 
  1341 	# If the first octet is all ones and the msb of the next bit
  1342 	# is also one then drop the first octet because negative
  1343 	# numbers should not be padded
  1344 	while ($base256 =~ s/^(FF:)([8ABCDEF][0-9A-F].*)/$2/i) {
  1345 		$$oc--;
  1346 	}
  1347 
  1348 	return $base256;
  1349 }
  1350 
  1351 # Encode the Type
  1352 # Only low tag form is supported at the moment
  1353 sub encodeType($$;$$) {
  1354 	my ($oc, $tagNumber, $constructed, $class) = @_;
  1355 
  1356 	$tagNumber = hex($tagNumber);
  1357 
  1358 	if ($tagNumber < 0 || $tagNumber > 30) {
  1359 		die "encodeType: Currently, only low tag numbers (0 - 30) are supported.";
  1360 	}
  1361 
  1362 	if (! defined $class) {
  1363 		$class = "UNIVERSAL";
  1364 	}
  1365 	
  1366 	$class = uc($class);	
  1367 	if (! isValidClass($class)) {
  1368 		die "encodeType: invalid class \'$class\'";
  1369 	}   
  1370 
  1371 	# If the type is constructed then set bit 6
  1372 	if (defined $constructed && $constructed == 1) {
  1373 		$tagNumber |= 0x20;
  1374 	}
  1375 
  1376 	if ($class eq $UNIVERSAL_CLASS) {
  1377 	   # do nothing, bits 7 and 8 are zero
  1378 	}
  1379 	elsif ($class eq $APPLICATION_CLASS) {
  1380 		# set bit 7
  1381 		$tagNumber |= 0x40;
  1382 	}
  1383 	elsif ($class eq $CONTEXT_SPECIFIC_CLASS) {
  1384 		# set bit 8
  1385 		$tagNumber |= 0x80;
  1386 	}
  1387 	elsif ($class eq $PRIVATE_CLASS) {
  1388 		# set bits 7 and 8
  1389 		$tagNumber |= 0xC0;
  1390 	}
  1391 	$$oc++;
  1392 	return sprintf("%2.2x", $tagNumber);
  1393 }
  1394 
  1395 sub encodeTlv($$$$;$$) {
  1396 	my ($oc, $tag, $length, $value, $constructed, $class) = @_;
  1397 
  1398 	if ($DEBUG == 3) {
  1399 		print "${TABS}encodeTlv\n";
  1400 		print "${TABS}oc=$$oc\n";
  1401 		print "${TABS}tag=$tag\n";
  1402 		print "${TABS}length=$length\n";
  1403 		print "${TABS}value=$value\n";
  1404 		if (defined $constructed) {
  1405 			print "${TABS}constructed=$constructed\n";
  1406 		}
  1407 		if (defined $class) {
  1408 			print "${TABS}class=$class\n";
  1409 		}
  1410 	}
  1411 
  1412 	my $hex;
  1413 	$hex = encodeType($oc, $tag, $constructed, $class);
  1414 	$hex .= ":" . encodeLength($length, $oc);
  1415 	$$oc += $length;
  1416 	$hex .= ":" . $value;
  1417 
  1418 	if ($DEBUG == 3) {
  1419 		print "${TABS}oc=$$oc\n";
  1420 		print "${TABS}encoding=$hex\n";
  1421 		print "${TABS}end\n";
  1422 
  1423 		toBin($hex);
  1424 	}
  1425 	return $hex;
  1426 }
  1427 
  1428 # increment debug tabbing level
  1429 sub nest() {
  1430 	$TABS .= "   ";
  1431 }
  1432 
  1433 # decrement debug tabbing level
  1434 sub leaveNest() {
  1435 	$TABS =~ s/^...//;
  1436 }
  1437 
  1438 sub isValidClass($) {
  1439 	my ($class) = @_;
  1440 
  1441 	if (defined $class &&
  1442 		$class =~ /^(UNIVERSAL|APPLICATION|CONTEXT-SPECIFIC|PRIVATE)$/) {
  1443 		return 1;
  1444 	}
  1445 	return 0;
  1446 }
  1447 
  1448 # Parse a DER field
  1449 sub getTlv($$$$$$) {
  1450 	my ($input, $class, $constructed, $tag, $length, $value) = @_;
  1451 	
  1452 	my @hexOctets = split(/:+/,tidyHex($input));
  1453 	
  1454 	if (scalar(@hexOctets) < 2) {
  1455 		die "getTlv: too short";
  1456 	}
  1457 
  1458 	my $type = hex(shift @hexOctets);
  1459 	if (($type & 0xC0) == 0x00) {
  1460 		# universal: bit 8 = 0, bit 7 = 0
  1461 		$$class = $UNIVERSAL_CLASS;
  1462 	}
  1463 	elsif (($type & 0xC0) == 0x40) {
  1464 		# application: bit 8 = 0, bit 7 = 1
  1465 		$$class = $APPLICATION_CLASS;
  1466 	}
  1467 	elsif (($type & 0xC0) == 0x80) {
  1468 		# application: bit 8 = 1, bit 7 = 0
  1469 		$$class = $CONTEXT_SPECIFIC_CLASS;
  1470 	}
  1471 	elsif (($type & 0xC0) == 0xC0) {
  1472 		# application: bit 8 = 1, bit 7 = 1
  1473 		$$class = $PRIVATE_CLASS;
  1474 	}
  1475 	else {
  1476 		die "getTlv: assert";
  1477 	}
  1478 
  1479 	if ($type & 0x20) {
  1480 		# constructed if bit 6 = 1
  1481 		$$constructed = 1;
  1482 	}
  1483 	else {
  1484 		$$constructed = 0;
  1485 	}
  1486 	
  1487 	# We assumme the tag number is in low form
  1488 	# and just look at the bottom 5 hits
  1489 	$$tag = $type & 0x1F;
  1490 
  1491 	$$length = hex(shift @hexOctets);
  1492 	if ($$length & 0x80) {
  1493 		# long form
  1494 		my $length_oc = $$length & 0x7F;
  1495 		$$length = 0;
  1496 		for (my $i = 0; $i < $length_oc; $i++) {
  1497 			# length is encoded base 256
  1498 			$$length *= 256;
  1499 			$$length += hex(shift @hexOctets);
  1500 		}
  1501 	}
  1502 	else {
  1503 		# short form
  1504 		# don't do anything here, length is just bits 7 - 1 and 
  1505 		# we already know bit 8 is zero.
  1506 	}
  1507 
  1508 	$$value = "";
  1509 	foreach (@hexOctets) {
  1510 		$$value .= ":$_";
  1511 	}
  1512 
  1513 	if ($DEBUG == 3) {
  1514 		print "${TABS} class=$$class\n";
  1515 		print "${TABS} constructed=$$constructed\n";
  1516 		print "${TABS} tag=$$tag\n";
  1517 		print "${TABS} length=$$length\n";
  1518 	}
  1519 }
  1520 
  1521 # parse an escaped (\) comma seperated argument string
  1522 # into an array
  1523 sub getArgs($) {
  1524 	my ($argString) = @_;
  1525 	my @args = ();
  1526 	
  1527 	while ($argString =~ /(^|.*?[^\\]),(.*)/ ) {
  1528 		my $match = $1;
  1529 		$argString = $2;
  1530 		if ($match ne "") {
  1531 			
  1532 			# unescape
  1533 			$match =~ s/(\\)([^\\])/$2/g;
  1534 			push @args, $match;
  1535 		}
  1536 	}
  1537 	if ($argString ne "") {
  1538 		push @args, $argString;
  1539 	}
  1540     return @args;
  1541 }