installationservices/swi/test/tdevcerts/buildsis.pl
changeset 0 ba25891c3a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swi/test/tdevcerts/buildsis.pl	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,552 @@
+#
+# Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+# Test tool used to make and sign SISX files signed by certificate
+# chains of aribtrary lengths as defiend by a template file.
+#
+
+use warnings;
+use strict;
+use Getopt::Long;
+
+my $CA_DIR=		"$ENV{'SECURITYSOURCEDIR'}\\installationservices\\switestfw\\testcertificates\\swi\\test\\tsisfile\\data\\signedsis";
+my $CERTS_DIR=	"$ENV{'SECURITYSOURCEDIR'}\\installationservices\\switestfw\\testcertificates\\swi\\test\\tdevcerts\\certs";
+my $REQS_DIR="reqs";
+my $KEYS_DIR=	"$ENV{'SECURITYSOURCEDIR'}\\installationservices\\switestfw\\testcertificates\\swi\\test\\tdevcerts\\keys";
+my $PKG_DIR="pkg";
+my $SIS_DIR="sis";
+my $TEMPLATE_DIR="templates";
+my $UID_FILE="uid.txt";
+my $SCRIPT_DIR="z:\\tswi\\tdevcerts\\scripts";
+my $DATA_DIR="z:\\tswi\\tdevcerts\\data";
+my $TMP_DIR="tmp";
+my $DAYS="3650";
+my $CREATE_CERTS="";
+my $TDEVCERTS_SCRIPT_DIR="\\epoc32\\winscw\\c\\tswi\\tdevcerts\\scripts";
+
+my $PLATFORM = '';
+my $CONFIGURATION = '';
+my $startDate = '';
+my $endDate = '';
+
+my $CA_TEXT=
+'
+subjectKeyIdentifier=hash
+#authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints=critical,CA:TRUE, pathlen:5
+keyUsage=critical,keyCertSign
+
+';
+
+
+&main;
+exit(0);
+
+sub main() {
+	my $section = '';
+	my $packageTemplate = '';
+	my @chains = ();
+	my @files = ();
+	my $installText = '';
+	my $uninstallText = '';
+	my $iniFile = '';
+	my $name = '';
+
+	GetOptions('platform=s' => \$PLATFORM,
+			   'configuration=s' => \$CONFIGURATION,
+			   'sis-dir=s' => \$SIS_DIR,
+			   'days=s', \$DAYS,
+			   'create-certs=s', \$CREATE_CERTS,
+			   'startdate=s', \$startDate,
+			   'enddate=s', \$endDate);
+	
+	if ($#ARGV < 0) {
+		&usage;
+	}
+	my ($config) = @ARGV;
+	open IN, $config or die "Cannot open $config\n";
+	print "Processing $config...\n";
+
+	while (<IN>) {
+		chomp;
+
+		s/<DATA_DIR>/${DATA_DIR}/g;
+		s/<SCRIPT_DIR>/${SCRIPT_DIR}/g;
+		s/<PLATFORM>/$PLATFORM/g;
+		s/<CONFIGURATION>/$CONFIGURATION/g;
+		
+		# Extract section name
+		if (/^\s*\[\w+\]/) {
+			s/.*\[\s*(.*)\s*].*/$1/g;
+			$section = lc;			
+		}
+    	elsif ($section eq "install") {
+			$installText .= "$_\n";
+	    }
+      	elsif ($section eq "uninstall") {
+			$uninstallText .= "$_\n";
+	    }
+		# Skip comments
+     	elsif (/^\s*\#/) {
+			next;
+	    }
+	    elsif (/\s*\w+\s*=.*/) { # Parse key/value pair
+
+			my ($key, $value) = split /=/;
+
+			$key =~ s/\s//g;			
+			$key = lc($key);
+			if ($section eq "chains") {
+				if ($key =~ /chain\d+/) {
+					parseChain($value, \@chains);
+				}
+			}
+			elsif ($section eq "main") {
+				if ($key eq 'template') {
+					$packageTemplate = $value;
+				}
+				elsif ($key eq 'name') {
+					$name = $value;
+				}
+                elsif ($key eq 'inifile') {
+					$iniFile = $value;
+				}
+			}
+			elsif ($section eq "files") {
+				if ($key =~ /^\s*sis.*/) {
+					my $componentUID = embeddedSIS($value);
+					my $sisText = "\@\"${SIS_DIR}\\${PLATFORM}\\$value\",(${componentUID})\n";
+					push @files, $sisText;
+				}
+				else {
+					push @files, "$value\n";
+				}
+			}
+     	}
+	}
+   close IN;	
+
+   die "\'iniFile\' not defined in main section" unless $iniFile ne '';
+   die "\'name\' not defined in main section" unless $name ne '';
+
+   my @chainFiles;  # The combined chain files
+   my @chainKeys;   # The keys for the leaf of each chain
+
+   if ($CREATE_CERTS eq "install") {
+
+	   print "\tCreating certificates \n";
+
+	   my $chainNum = 0;
+	   foreach my $chain (@chains) {
+		   print "\tProcessing chain $chainNum\n";
+		   buildChain($name, 
+					  $chainNum, 
+					  \@chainFiles,
+					  \@chainKeys,
+					  @$chain);
+		   $chainNum++;
+	   }
+   }
+   else
+   {
+	   print "\Using existing certificates \n";
+
+	   my $chainNum = 0;
+	   foreach my $chain (@chains) {
+		   print "\tProcessing chain $chainNum\n";
+		   enumChain($name, 
+					 $chainNum, 
+					 \@chainFiles,
+					 \@chainKeys,
+					 @$chain);
+		   $chainNum++;
+	   }
+   }
+   
+   my $temp = $packageTemplate;
+   $temp =~ s/.*\\(\w+).*/$1/;	
+
+   my $packageFile = "${PKG_DIR}\\${name}.pkg";
+   my $uid = getUID();
+   buildPackage($PLATFORM, 
+				$CONFIGURATION, 
+				$uid,
+				"${TEMPLATE_DIR}\\$packageTemplate", 
+				$packageFile,
+				@files);
+   
+   # Create unsigned SIS file
+   mkdir("${SIS_DIR}\\${PLATFORM}");
+   my $sisFile = "${SIS_DIR}\\${PLATFORM}\\${name}.sis";
+   makesis($packageFile, $sisFile);
+
+  for (my $i = 0; $i <= $#chainFiles; $i++) {
+	  signsis("${sisFile}", "${sisFile}.tmp", $chainFiles[$i], $chainKeys[$i]);
+	  unlink $sisFile;
+	  rename "${sisFile}.tmp", "${sisFile}";
+  }
+
+  # Add install/uninstall script entries for this package
+  addIniEntries("${TDEVCERTS_SCRIPT_DIR}\\$iniFile", 
+				$uid, $name, $installText, $uninstallText, 
+				"${TEMPLATE_DIR}\\$packageTemplate" );
+}
+
+sub buildChain(\$\$\$\$\@) {
+	my ($name, $chainNum, $chainFiles, $chainKeys, @extensions) = @_;
+  
+	my $root = shift @extensions;
+	my $certNum = 0;
+	my $signingCert = "${CA_DIR}\\$root\\ca.pem";
+	my $signingKey  = "${CA_DIR}\\$root\\ca.key.pem";
+	my @chainElements = (); # List of cert filenames in the current chain
+
+    print "\tbuildChain $name\n";
+
+	# For each extension create a request for a certificate and
+	# sign that request with the previous certificate, or the root
+	# certificate for the first element
+	foreach my $extension (@extensions) {
+		my $isCA = $certNum < $#extensions;
+
+	    print "\tCreating certificates \n";
+		
+		die "Cannot open extension file ext\\$extension.cfg\n"
+			unless -e "ext/$extension.cfg";
+		
+		my $file = "${name}_${extension}.${chainNum}.${certNum}";
+		my $subject = "/C=UK/O=Symbian/CN=Entity Cert ${name} ${extension} ${chainNum} ${certNum}";
+		createRequest($isCA,
+					  "${REQS_DIR}\\${file}.req.pem",
+					  $subject,
+					   "${KEYS_DIR}\\${file}.key.pem",
+					  'openssl.cfg');
+		
+		signRequest($isCA, 
+					"${REQS_DIR}\\${file}.req.pem",
+					"${CERTS_DIR}\\${file}.cert.pem",
+					$signingCert,
+					$signingKey,
+					"ext\\$extension.cfg");
+		
+		$signingCert = "${CERTS_DIR}\\${file}.cert.pem";
+		$signingKey = "${KEYS_DIR}\\${file}.key.pem";
+		
+		push @chainElements, "${CERTS_DIR}\\${file}.cert.pem";
+		$certNum++;
+	}
+	# Record the filename of the leaf signing key. This is needed by sisgnsis	
+	push @$chainKeys, $signingKey; 
+
+	# Concatenate all the certificates for signsis
+	my $chainFile = "${CERTS_DIR}\\${name}_chain_${chainNum}.pem";
+	push @$chainFiles, $chainFile;
+
+	open OUT, ">$chainFile" or 
+		die "Cannot write to $chainFile\n";
+	foreach (reverse @chainElements) {
+		open IN, $_ or 
+			die "Cannot open $_\n";
+		while (<IN>) {
+			print OUT;
+		}
+		close IN;
+	}
+	close OUT;
+}
+
+sub enumChain(\$\$\$\$\@) {
+	my ($name, $chainNum, $chainFiles, $chainKeys, @extensions) = @_;
+  
+	my $root = shift @extensions;
+	my $signingKey  = "${CA_DIR}\\$root\\ca.key.pem";
+
+    print "\tenumChain $name\n";
+
+	my $certNum = $#extensions;
+	if ( $certNum >= 0 )
+	{
+		my $extension = pop @extensions;
+		my $file = "${name}_${extension}.${chainNum}.${certNum}";
+		$signingKey = "${KEYS_DIR}\\${file}.key.pem";
+	}
+	
+	# Record the filename of the leaf signing key. This is needed by sisgnsis	
+	push @$chainKeys, $signingKey; 
+
+	# Concatenate all the certificates for signsis
+	my $chainFile = "${CERTS_DIR}\\${name}_chain_${chainNum}.pem";
+	push @$chainFiles, $chainFile;
+}
+
+sub buildPackage(\$\$\$\$\$\@) {
+	my ($PLATFORM, $CONFIGURATION, $packageUID, $packageTemplate, $packageFile, @files) = @_;
+	my $packageUIDText = sprintf "0x%8.8x", $packageUID;
+	
+	print "\tCreating package $packageFile\n";
+
+	open(IN, "${packageTemplate}") or
+		die "Cannot open package file template $packageTemplate\n";
+
+	open(OUT, ">$packageFile") or
+		die "Cannot write to package file $packageFile\n";
+
+	# Substitute the list of files for the place holder in the template
+	# package file.
+	while (<IN>) {
+		s/\#\#PACKAGE_UID\#\#/${packageUIDText}/g;
+
+		if (/\#\#FILES\#\#/) {
+			foreach (@files) {
+				# The SISX files have to be built for every supported
+				# platform and configuration
+				print OUT;
+			}
+		}
+		else {
+			print OUT;
+		}
+	}
+	close IN;
+	close OUT;
+}
+
+sub parseChain(\$\$) {
+	my ($chainDesc, $chains) = @_;   
+	my @elements = split(/,/, $chainDesc);
+	push @$chains, \@elements;
+}
+
+sub createRequest(\$,\$\$\$\$) {
+	my ($isCA, $requestFile, $subject, $keyFile, $config) = @_;
+
+	my @args = ('openssl',
+				'req',
+				'-newkey',
+				'rsa:512',
+				'-nodes',
+				'-out',
+				$requestFile,
+				'-keyout', 
+				$keyFile,
+				'-subj',
+				$subject,
+				'-config', 
+				$config, 
+				'-days', 
+				'3650');
+
+	system (@args) == 0 or
+		die "Cannot create certificate request $requestFile\n";	
+}
+
+sub signRequest(\$\$\$\$\$) {
+	my ($isCA, $requestFile, $certFile, $CAcert, $CAkey, $extfile) = @_;
+	my $tmpFile = "${extfile}.tmp";
+
+	my @args = ();
+	my $demoCaDir = "$ENV{'SECURITYSOURCEDIR'}\\installationservices\\swi\\test\\tdevcerts\\demoCA";
+	
+	# Workaround to insert CA extensions 
+	# Open SSL expects all the extensions to be kept together so we
+	# can't just specify the CA bits in the request because otherwise
+	# it will overwrite them
+	system 'copy', $extfile, $tmpFile;
+	system 'attrib', '-r', $tmpFile;
+
+	if ($isCA) {
+		open OUT, ">>$tmpFile" or die "Cannot open temporary file $tmpFile\n";
+		print OUT $CA_TEXT;
+		close OUT;
+	}
+
+	if ($startDate && $endDate) 
+	{
+		@args = ('openssl',
+				'ca',
+				'-in',
+				$requestFile,
+				'-out',
+		 		$certFile,
+				'-cert',
+				$CAcert,
+				'-keyfile',
+				$CAkey,
+				'-extfile',
+				$tmpFile,
+				'-startdate',
+				$startDate,
+				'-enddate',
+				$endDate,
+				'-batch',
+				'-config',	
+				$demoCaDir.'/'.'openssl.cfg');
+
+		system (@args) == 0 or 			   
+			   die "Cannot sign certificate request $requestFile\n";
+
+		# Convert the generated certificate to x509 format if "openssl ca" command is used.
+		@args = ( 'openssl',
+				  'x509',
+				  '-in',
+				  $certFile,	
+				  '-out',
+				  $certFile);
+		
+		system (@args) == 0 or 			   
+			   die "Cannot covert $certFile to x509 format.\n";
+	}
+	else
+	{
+		@args = ('openssl',
+				'x509',
+				'-req',
+				'-in',
+				$requestFile,
+				'-out',
+		 		$certFile,
+				'-CA',
+				$CAcert,
+				'-CAkey',
+				$CAkey,
+				'-extfile',
+				$tmpFile,
+				'-days',
+				"${DAYS}",
+				'-CAcreateserial');
+
+		system (@args) == 0 or 			   
+			   die "Cannot sign certificate request $requestFile\n";
+	}
+	unlink $tmpFile;
+}
+
+sub makesis(\$\$) {
+	my ($packageFile, $sisFile) = @_;
+	print "\tCreating SIS file $sisFile\n";
+	system 'makesis', $packageFile, $sisFile;
+}
+
+sub signsis(\$\$\$\$) {
+	my ($input, $output, $certificate, $key) = @_;
+
+	print "\tSigning $input with certchain=${certificate} key=${key}\n";
+
+	system('signsis',
+		   $input,
+		   $output,
+		   $certificate,
+		   $key) == 0 or
+			   die "Cannot sign SIS file $input $output $certificate $key\n";
+}
+
+sub usage {
+	print STDERR "Usage: buildsis.pl --platform <platform> --configuration <udeb|urel> --sis-dir <sis dir> <test spec>\n";
+	exit -1;
+}
+
+# Make sure each package has a unique UID by repeatedly adding one
+# from a pre-determined base.
+sub getUID {
+
+	if (! -e "uid.txt.tmp") {
+		system 'copy','uid.txt','uid.txt.tmp';
+		system 'attrib','-r','uid.txt.tmp';
+	}
+
+	open IN, "uid.txt.tmp" or die "Cannot open uid.txt.tmp\n";
+	my $uidText = <IN>;
+	chomp $uidText;
+	my $uid = hex($uidText);
+	close IN;   
+	
+	die "Invalid uid = $uidText\n" unless $uid != 0;
+	
+	open OUT, ">uid.txt.tmp" or die "Cannot write to uid.txt.tmp\n";
+	print OUT sprintf("0x%8.8x", $uid + 1);
+	close OUT;
+	return $uid;
+}
+
+# Add install & uninstall entries
+sub addIniEntries(\$\$\$\$\$) {
+	my ($iniFile, $uid, $name, $installText, $uninstallText, $template) = @_;
+	
+	print "\tAdding install entry to ${iniFile}\n";
+	
+	open OUT, ">>${iniFile}" or die "Cannot write to ${iniFile}\n";
+
+	print OUT "[${name}]\n";
+	print OUT "sis=${DATA_DIR}\\${name}.sis\n";
+    print OUT "${installText}\n";
+
+	open( TEMPLATE, "< $template") or
+		die "Cannot open package file template $template\n";
+	my @config = <TEMPLATE>;
+	close TEMPLATE;
+
+	print "\tAdding uninstall entry to ${iniFile}\n";
+	print OUT "[u_${name}]\n";
+
+	# If package uid not pre-defined, use auto-generated id for uninstall
+	my $puid = grep ( /##PACKAGE_UID##/, @config );
+	if ( $puid == 1 ) {
+		print OUT "uid=";
+		print OUT sprintf("%8.8x", $uid);
+		print OUT "\n";
+	} else {
+		my @line = grep( /testprotpUID/, @config );
+		$line[0] =~ m/#{"testprotpUID"},\s+\(0x(.*)\), 1, 2, 3, TYPE=/;
+		print OUT "uid=$1\n";
+	}
+    print OUT "${uninstallText}\n";
+
+	close OUT;
+}
+
+# Takes the name of an embedded SIS file and returns the text required 
+# by the package body to embed this file.
+# In addition, this function calculates the component uid of the package file
+# by searching the package directory for a file which matches the name of the SIS 
+# file.
+sub embeddedSIS(\$) {
+	my ($sisfile) = @_;
+
+	my $absSISfile = "${SIS_DIR}\\${PLATFORM}\\$sisfile";
+
+	die "Cannot find embedded sis file $absSISfile\n"
+		unless (-e $absSISfile);		
+
+	my $packageFile = $sisfile;
+	$packageFile =~ s/\.sis/.pkg/g;
+	
+	my $absPackageFile = "${PKG_DIR}\\${packageFile}";
+	open PKG, "${absPackageFile}" or die "Cannot open ${absPackageFile}\n";
+
+	my $componentUID = '';
+	while (<PKG>) {
+		chomp;
+
+		# Extract the component uid
+		if (s/\s*\#.*\(([0-9A-Fa-fx]+)\).*/$1/) {
+			$componentUID = $_;
+		}
+	}
+	close PKG;
+
+	if ($componentUID eq "") {
+		die "Cannot extract UID from package ${absPackageFile}\n";	
+	}	
+	return $componentUID;
+}