securityanddataprivacytools/securitytools/certapp/certapp.cpp
changeset 0 2c201484c85f
child 8 35751d3474b7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/securityanddataprivacytools/securitytools/certapp/certapp.cpp	Wed Jul 08 11:25:26 2009 +0100
@@ -0,0 +1,1046 @@
+/*
+* Copyright (c) 2008-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: 
+*
+*/
+
+
+static const char * const sVersion = "certapp version 1.1.++";
+#include <e32base.h>
+#include <f32file.h>
+#include <s32file.h>
+#include <map>
+#include <sys/stat.h>
+#include "encdec.h"
+#include "certclients.h"
+#include "filecertstore.h"
+#include "swicertstore.h"
+#include "logger.h"
+#include "stringconv.h"
+#include <errno.h>
+#include "appuidmap.h"
+#include "openssl_license.h"
+#include "utils.h"
+
+#ifdef __TOOLS2_LINUX__
+#include <unistd.h>
+#include <stdio.h>
+#else
+#include <io.h>
+#endif // __TOOLS2_LINUX__
+
+#ifdef __TOOLS2_LINUX__
+#define DIR_SEPARATOR "/"
+#else
+#define DIR_SEPARATOR "\\"
+#endif
+
+enum CertStoreFileType
+{
+	EBinCertClients,
+	EHumanCertClients,
+	EBinFileCertStore,
+	EHumanFileCertStore,
+	EBinSwiCertStore,
+	EHumanSwiCertStore
+};
+
+struct AppMapEntry
+	{
+	TUint32 iUid;
+	TUint32 iInputFileIndex;
+	};
+typedef std::map<std::string, AppMapEntry> AppMap;
+
+typedef std::map<std::string, TUint32> FCSLabelMap; // maps cert label to inputFileIndex
+
+typedef std::vector<std::string> StringVector;
+typedef std::vector<CertStoreFileType> CertStoreFileTypeVector;
+
+void ProcessCommandLine(int aArgc, char **aArgv, 
+						StringVector &aInputFiles, 
+						StringVector &aInputDirs,
+						CertStoreFileTypeVector &aInputFileTypes,
+						StringVector &aOutputFiles,
+						StringVector &aOutputDirs,
+						CertStoreFileTypeVector &aOutputFileTypes,
+						bool &aVerbose, bool &aPemOut, bool &aAllowDuplicates);
+
+void ProcessCertClientFiles(const std::string &aBaseDir,
+							const StringVector &aInputFiles,
+							const StringVector &aInputDirs,
+							const CertStoreFileTypeVector &aInputFileTypes,
+							bool &aAllowDuplicates,
+							EncDecContainer &aCertAppInfoContainer);
+
+void GenerateOutputFiles(const EncDecContainer &aCertAppInfoContainer,
+						 const EncDecContainer &aFileCertStoreContainer,
+						 const EncDecContainer &aSwiCertStoreContainer,
+						 const std::string &aBaseDir,
+						 const StringVector &aOutputFiles, 
+						 const StringVector &aOutputDirs,
+						 const CertStoreFileTypeVector &aOutputFileTypes,
+						 bool aVerbose, bool aPemOut);
+
+bool ValidateLabel(FCSLabelMap &aLabelMap, 
+				   const StringVector &aInputFiles, 
+				   CertStoreFileType aFileType,
+				   TUint32 aFileIndex, 
+				   const TCertLabel &aCertLabel);
+
+
+struct SubjectToSubjectKeyIdEntry
+	{
+	bool iDuplicate;
+	std::string iLabel;
+	TKeyIdentifier iSubjectKeyIdentifier;
+	};
+typedef std::map<std::string, SubjectToSubjectKeyIdEntry> SubjectToSubjectKeyIdMap;
+void BuildSubjectToSubjectKeyIdMap(const EncDecContainer &aCertStoreContainer,
+								   SubjectToSubjectKeyIdMap &aSubjectMap);
+
+void SetIssuerKeyId(SubjectToSubjectKeyIdMap &aSubjectMap, 
+					EUseCertificateExtension aUseExtension,
+					EncDecContainer &aCertStoreContainer);
+
+static const std::string OPT_PROGRESS("--progress=");
+static const std::string OPT_ERRORS("--errors=");
+static const std::string OPT_VERBOSE("--verbose");
+static const std::string OPT_ALLOWDUPLICATES("--allowduplicates");
+static const std::string OPT_CHDIR("--chdir=");
+static const std::string OPT_HCERTCLIENTS_L("--hcertclients=");
+static const std::string OPT_HCERTCLIENTS_S("--hcc=");
+static const std::string OPT_BCERTCLIENTS_L("--bcertclients=");
+static const std::string OPT_BCERTCLIENTS_S("--bcc=");
+static const std::string OPT_HFILECERTSTORE_L("--hfilecertstore=");
+static const std::string OPT_HFILECERTSTORE_S("--hca=");
+static const std::string OPT_BFILECERTSTORE_L("--bfilecertstore=");
+static const std::string OPT_BFILECERTSTORE_S("--bca=");
+static const std::string OPT_HSWICERTSTORE_L("--hswicertstore=");
+static const std::string OPT_HSWICERTSTORE_S("--hswi=");
+static const std::string OPT_BSWICERTSTORE_L("--bswicertstore=");
+static const std::string OPT_BSWICERTSTORE_S("--bswi=");
+
+
+
+void usage()
+{
+	prog << "certapp: general_options file_options --out file_options" << Log::Endl();
+	prog << Log::Endl();
+	prog << "Basic usage is to give one or more input files of any supported type, followed by --out and a list of output files using the same syntax" << Log::Endl();
+	prog << "Typically at least one input file of type certclients should be given (via --bcertclients or --hcertclients) because this is required to encode/decode the application usage fields in the swicertstore and filecertstore files." << Log::Endl();
+	prog << Log::Endl();
+	prog << "general_options contains one or more of the following options:-" << Log::Endl();
+	prog << "\t--help|-h\tDisplay this usage message" << Log::Endl();
+	prog << "\t" << OPT_PROGRESS << "filename\tSave progress output to specified file" << Log::Endl();
+	prog << "\t" << OPT_ERRORS << "filename\tSave error output to specified file" << Log::Endl();
+	prog << "\t" << OPT_VERBOSE << "Include additional debug comments in output files" << Log::Endl();
+	prog << "\t--license Display license information" << Log::Endl();
+	prog << "\t--pemout Output certificates in PEM format (nb. format is auto-detected when reading)" << Log::Endl();
+	prog << "\t" << OPT_ALLOWDUPLICATES << "\tWhen reading human readable config files, permit adding duplicate certificate labels in stores and UIDs in certclients (testing ONLY)" << Log::Endl();
+	prog << "An errors/progress filename of - will write to the standard output." << Log::Endl();
+	prog << "If the errors/progress filenames are identical, the output will be merged." << Log::Endl();
+	prog << Log::Endl();
+	prog << "Both instances of file_options contains one or more of the following options:-" << Log::Endl();
+	prog << "\t" << OPT_HCERTCLIENTS_L << "|" << OPT_HCERTCLIENTS_S << "filename\t\tHuman readable certclients file" << Log::Endl();
+	prog << "\t" << OPT_BCERTCLIENTS_L << "|" << OPT_BCERTCLIENTS_S << "filename\t\tBinary certclients file" << Log::Endl();
+	prog << Log::Endl();
+	prog << "\t" << OPT_HFILECERTSTORE_L << "|" << OPT_HFILECERTSTORE_S << "filename\tHuman readable filecertstore" << Log::Endl();
+	prog << "\t" << OPT_BFILECERTSTORE_L << "|" << OPT_BFILECERTSTORE_S << "filename\tBinary filecertstore" << Log::Endl();
+	prog << Log::Endl();
+	prog << "\t" << OPT_HSWICERTSTORE_L << "|" << OPT_HSWICERTSTORE_S << "filename\tHuman readable swicertstore" << Log::Endl();
+	prog << "\t" << OPT_BSWICERTSTORE_L << "|" << OPT_BSWICERTSTORE_S << "filename\tBinary swicertstore" << Log::Endl();
+	prog << Log::Endl();
+	prog << "\t" << "--out Change to specifying output files" << Log::Endl();
+	prog << "\t" << "--in Change to specifying input files" << Log::Endl();
+	prog << "\t" << "--chdir=relativeDir Change to the specified dir. Can be specified multiple times. Missing dir will be created if only last element is missing." << Log::Endl();	
+	prog << Log::Endl();
+	
+	prog << "Examples" << Log::Endl();
+	prog << "Read/dump a swicertstore" << Log::Endl();
+	prog << "\tcertapp --bcertclients=certclients.dat --bswicertstore=swicertstore.dat --out --hswicertstore=swicertstore.txt" << Log::Endl();
+	prog << "Read/dump a filecertstore" << Log::Endl();
+	prog << "\tcertapp --bcertclients=certclients.dat --bfilecertstore=cacerts.dat --out --hfilecertstore=cacerts.txt" << Log::Endl();
+	prog << "Augment a filecertstore" << Log::Endl();
+	prog << "\tcertapp --bcertclients=certclients.dat --bfilecertstore=cacerts.dat --hfilecertstore=cacerts_extras.txt --out --bfilecertstore=cacerts_new.dat" << Log::Endl();
+	prog << Log::Endl();
+	prog << "Device file locations" << Log::Endl();
+	prog << "ROM swicertstore - z:\\resource\\swicertstore.dat" << Log::Endl();
+	prog << "Writable swicertstore - !:\\resource\\swicertstore\\dat\\* where ! is the system drive" << Log::Endl();
+	prog << "Initial filecertstore and certclients z:\\private\\101f72a6\\cacerts.dat and certclients.dat. Copied to sys drive on first use." << Log::Endl();
+	prog << "Filecertstore !:\\private\\101f72a6\\cacerts.dat and certclients.dat. where ! is the system drive." << Log::Endl();
+}
+
+void ChangeDir(const std::string &aBaseDir, const std::string &aRelativeDir)
+{
+	std::string dir(aBaseDir);
+	if(aRelativeDir != ".")
+		{
+		// Build dir to create and change into
+		dir.append(DIR_SEPARATOR);
+		dir.append(aRelativeDir);
+		}
+		
+	prog << Log::Indent() << "Setting dir to " << dir << Log::Endl();
+#ifdef __LINUX__
+	(void) mkdir(dir.c_str(),0755); // May already exist so no need to check return code
+#else
+	(void) mkdir(dir.c_str()); // May already exist so no need to check return code
+#endif
+	if(chdir(dir.c_str()) < 0)
+		{
+		dbg << Log::Indent() << "failed to change dir to " << dir << Log::Endl();
+		FatalError();
+		}
+	return;
+}
+
+int main(int argc, char **argv)
+{
+	dbg.SetStream(&std::cout);
+	prog.SetStream(&std::cout);
+
+	try{
+	if(argc==1)
+		{
+		prog << sVersion << " Use -h for help." << Log::Endl();
+		}
+
+	StringVector inputFiles;
+	StringVector inputDirs;
+	CertStoreFileTypeVector inputFileTypes;
+
+	StringVector outputFiles;
+	StringVector outputDirs;
+	CertStoreFileTypeVector outputFileTypes;
+
+
+	bool verbose = false;
+	bool pemOut = false;
+	bool allowDuplicates = false;
+
+	// Process all the command line options and file arguments
+	ProcessCommandLine(argc, argv, 
+					   inputFiles, inputDirs, inputFileTypes,
+					   outputFiles, outputDirs, outputFileTypes,
+					   verbose, pemOut, allowDuplicates);
+
+
+	// Save current directory
+	std::string baseDir;
+	{
+	char tmp[FILENAME_MAX];
+	if(getcwd(tmp, FILENAME_MAX) == 0)
+		{
+		dbg << Log::Indent() << "Failed to read current dir" << Log::Endl();
+		FatalError();
+		}
+	baseDir = tmp;
+	}
+	
+
+	//
+	// Process input files starting with certclient files and working from right to left
+	//
+	EncDecContainer certAppInfoContainer("ClientInfo", CertificateAppInfo::Factory);
+	ProcessCertClientFiles(baseDir, inputFiles, inputDirs, inputFileTypes, 
+						   allowDuplicates,
+						   certAppInfoContainer);
+	
+	
+	// Generate config data for application uid EncDecEnum object in AppUidListEntry
+	AppUidMap::GenerateEnumEntries();
+
+	//
+	// Process remaining input files working from right to left
+	//
+	EncDecContainer fileCertStoreContainer("CertStoreEntries", CertStoreEntry::Factory);
+	FCSLabelMap fcsLabels;
+	EncDecContainer swiCertStoreContainer("SwiCertStoreEntries", SwiCertStoreEntry::Factory);
+	FCSLabelMap swiLabels;
+	for(int fileIndex = inputFiles.size()-1; fileIndex >= 0 ; --fileIndex)
+		{
+		CertStoreFileType fileType = inputFileTypes[fileIndex];
+		if((fileType == EBinFileCertStore) || (fileType == EHumanFileCertStore))
+			{
+			// Change to correct directory
+			ChangeDir(baseDir, inputDirs[fileIndex]);
+
+			EncDecContainer tmpFileCertStoreContainer("CertStoreEntries", CertStoreEntry::Factory);
+			if(fileType == EBinFileCertStore)
+				{
+				prog << "Reading binary filecertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl();
+				AutoIndent ai(prog);
+				readContainer(inputFiles[fileIndex], false, tmpFileCertStoreContainer);
+				}
+			if(fileType == EHumanFileCertStore)
+				{
+				prog << "Reading human filecertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl();
+				AutoIndent ai(prog);
+				readContainer(inputFiles[fileIndex], true, tmpFileCertStoreContainer);
+				}
+			
+			// Now merge the new file into the running store.
+			prog << Log::Indent() << "Merging filecertstore data" << Log::Endl();
+			AutoIndent ai(prog);
+			for(TUint32 entryIndex = 0; entryIndex < tmpFileCertStoreContainer.size(); ++entryIndex)
+				{
+				const CertStoreEntry &entry = static_cast<const CertStoreEntry &>(tmpFileCertStoreContainer[entryIndex]);
+				std::string nname = stringFromUtf16(entry.Label());
+
+				if(!ValidateLabel(fcsLabels, inputFiles, fileType, fileIndex, entry.Label()))
+					{
+					// Duplicate detected
+					if(!allowDuplicates || (fileType == EBinFileCertStore))
+						{
+						continue; // Skip adding duplicate
+						}
+					// Only allow duplicates if debugging and reading
+					// human readable config file.
+					dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl();
+					}
+				
+				// Add entry
+				CertStoreEntry *newEntry = new CertStoreEntry;
+				*newEntry = entry;
+				fileCertStoreContainer.push_back(newEntry);
+				}
+			continue;
+			}
+		
+
+		if((fileType == EBinSwiCertStore) || (fileType == EHumanSwiCertStore))
+			{
+			// Change to correct directory
+			ChangeDir(baseDir, inputDirs[fileIndex]);
+
+			EncDecContainer tmpSwiCertStoreContainer("SwiCertStoreEntries", SwiCertStoreEntry::Factory);
+			if(fileType == EBinSwiCertStore)
+				{
+				prog << "Reading binary swicertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl();
+				AutoIndent ai(prog);
+				readContainer(inputFiles[fileIndex], false, tmpSwiCertStoreContainer);
+				}
+			if(fileType == EHumanSwiCertStore)
+				{
+				prog << "Reading human swicertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl();
+				AutoIndent ai(prog);
+				readContainer(inputFiles[fileIndex], true, tmpSwiCertStoreContainer);
+				}
+
+			// Now merge the new file into the running store.
+			prog << Log::Indent() << "Merging swicerstore data" << Log::Endl();
+			AutoIndent ai(prog);
+			for(TUint32 entryIndex = 0; entryIndex < tmpSwiCertStoreContainer.size(); ++entryIndex)
+				{
+				const SwiCertStoreEntry &entry = static_cast<const SwiCertStoreEntry &>(tmpSwiCertStoreContainer[entryIndex]);
+				std::string nname = stringFromUtf16(entry.Label());
+		
+				if(!ValidateLabel(swiLabels, inputFiles, fileType, fileIndex, entry.Label()))
+					{
+					// Duplicate detected
+					if(!allowDuplicates || (fileType == EBinSwiCertStore))
+						{
+						continue; // Skip adding duplicate
+						}
+					// Only allow duplicates if debugging and reading
+					// human readable config file.
+					dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl();
+					}
+				
+				// Add entry
+				SwiCertStoreEntry *newEntry = new SwiCertStoreEntry;
+				*newEntry = entry;
+				swiCertStoreContainer.push_back(newEntry);
+				}
+			continue;
+			}
+		}
+
+	// Fix Certificate IDs in fileCertStoreContainer
+	for(TUint32 entryIndex=0; entryIndex < fileCertStoreContainer.size(); ++entryIndex)
+		{
+		CertStoreEntry &entry = static_cast<CertStoreEntry &>(fileCertStoreContainer[entryIndex]);
+		entry.Info().SetOutputCertificateId(entryIndex);
+		}
+	
+	// Fix Certificate IDs in swicertstore container.
+	for(TUint32 entryIndex=0; entryIndex < swiCertStoreContainer.size(); ++entryIndex)
+		{
+		SwiCertStoreEntry &entry = static_cast<SwiCertStoreEntry &>(swiCertStoreContainer[entryIndex]);
+		entry.Info().SetOutputCertificateId(entryIndex);
+		}
+
+	//
+	// Fix auto IssuerKeyId values
+	//
+	SubjectToSubjectKeyIdMap subjectMap;
+	//
+	// Fix IssuerKeyId values in swiCertStoreContainer
+	//
+	// We do not use the AuthorityKeyId extension and only consider
+	// certificates in swi certstores.
+	//
+	// First map all the SWI certificate subject names to SubjectKeyId values
+	BuildSubjectToSubjectKeyIdMap(swiCertStoreContainer, subjectMap);
+	// Now update IssuerKeyId fields which are set to auto.
+	// The AuthorityKeyId extension value will be ignored.
+	// The SubjectKeyId of a matching certificate (if there is a
+	// single match on issuer name) in the swi cert store will be
+	// used.
+	SetIssuerKeyId(subjectMap, KIgnoreCertificateExtension, swiCertStoreContainer);
+
+
+	//
+	// Fix IssuerKeyId values in fileCertStoreContainer
+	//
+	// Add all filecertstore certificates to the
+	// subjectName/SubjectKeyId map
+	BuildSubjectToSubjectKeyIdMap(fileCertStoreContainer, subjectMap);
+	// Now update IssuerKeyId fields which are set to auto.  If an the
+	// AuthorityKeyId extension is present and <160bits use it,
+	// otherwise use the SubjectKeyId of the matching certificate (if
+	// there is a single match on issuer name).
+	SetIssuerKeyId(subjectMap, KUseCertificateExtension, fileCertStoreContainer);
+
+	//
+	// Now generate output files
+	//
+	GenerateOutputFiles(certAppInfoContainer, fileCertStoreContainer, swiCertStoreContainer,
+						baseDir, outputFiles, outputDirs, outputFileTypes, verbose, pemOut);
+	
+
+	}
+	catch(...)
+		{
+		dbg << Log::Indent() << "C++ expection!" << Log::Endl();
+		FatalError();
+		}
+	
+	prog << Log::Indent() << "Normal exit" << Log::Endl();
+	prog.Stream().flush();
+	dbg.Stream().flush();
+	return 0; // All ok
+}
+
+/**
+   ProcessCommandLine
+*/
+void ProcessCommandLine(int aArgc, char **aArgv, 
+						StringVector &aInputFiles, StringVector &aInputDirs, CertStoreFileTypeVector &aInputFileTypes,
+						StringVector &aOutputFiles, StringVector &aOutputDirs, CertStoreFileTypeVector &aOutputFileTypes,
+						bool &aVerbose, bool &aPemOut, bool &aAllowDuplicates)
+{
+	std::string progressFile("-");
+	static std::fstream sProgressStream;
+
+	std::string dbgFile("-");
+	static std::fstream sDbgStream;
+
+	StringVector *files = &aInputFiles;
+	StringVector *dirs = &aInputDirs;
+	CertStoreFileTypeVector *fileTypes = &aInputFileTypes;
+
+	int argIndex=1;
+	// Process overall arguments (-h --progress --errors)
+	for(; argIndex < aArgc; ++argIndex)
+		{
+		std::string arg(aArgv[argIndex]);
+
+		if(arg == "--license")
+			{
+			prog << sVersion << Log::Endl();
+			prog << "Copyright (c) 2008 Symbian Software Ltd. All rights reserved." << Log::Endl();
+			prog << "Linked against openssl. Credits and license for openssl follow:-" << Log::Endl();
+			prog << openssl_license << Log::Endl();
+			
+			continue;
+			}
+
+		if(arg == OPT_VERBOSE)
+			{
+			prog << "Enabling additional output file comments" << Log::Endl();
+			aVerbose = true;
+			
+			continue;
+			}
+
+		if(arg == "--pemout")
+			{
+			prog << "Setting output certificate format to PEM" << Log::Endl();
+			aPemOut = true;
+			
+			continue;
+			}
+
+		if(arg == OPT_ALLOWDUPLICATES)
+			{
+			prog << "Allowing addition of duplicate labels in stores and UIDs in certclients when reading human readable input (testing ONLY)" << Log::Endl();
+			aAllowDuplicates = true;
+			
+			continue;
+			}
+
+		if((arg.find(OPT_PROGRESS) == 0) ||
+		   (arg.find(OPT_ERRORS) == 0))
+			{
+			// The following logic is required so that if both streams
+			// are set to the same destination we share a streams
+			// object and hence avoid buffering issues.
+			std::string *thisFile;
+			std::fstream *thisStream;
+			Log *thisLog;
+			std::string *otherFile;
+			std::fstream *otherStream;
+			Log *otherLog;
+
+			if(arg.find(OPT_PROGRESS) == 0)
+				{
+				thisFile = &progressFile;
+				thisStream = &sProgressStream;
+				thisLog = &prog;
+				otherFile = &dbgFile;
+				otherStream = &sDbgStream;
+				otherLog = &dbg;
+
+				*thisFile = arg.substr(OPT_PROGRESS.size(), arg.npos);
+				}
+			else
+				{
+				thisFile = &dbgFile;
+				thisStream = &sDbgStream;
+				thisLog = &dbg;
+				otherFile = &progressFile;
+				otherStream = &sProgressStream;
+				otherLog = &prog;
+
+				*thisFile = arg.substr(OPT_ERRORS.size(), arg.npos);
+				}
+
+			if(*thisFile == *otherFile)
+				{
+				// Reuse existing stream. This avoids two streams opening the same file...
+				thisLog->SetStream(&otherLog->Stream());
+				continue;
+				}
+
+			// Need to open a new stream
+			if(thisStream->is_open()) thisStream->close();
+			if(*thisFile == "-")
+				{
+				thisLog->SetStream(&std::cout);
+				continue;
+				}
+			
+			OpenUtf8FStreamForWrite(*thisStream, thisFile->c_str());
+			if(thisStream->fail())
+				{
+				if(thisLog == &dbg)
+					{
+					dbg.SetStream(&std::cout);
+					}
+				dbg << Log::Indent() << "Failed to open log file specified by " << aArgv[argIndex-1] << " '" << *thisFile << "'" << Log::Endl();
+				return;
+				}
+			thisLog->SetStream(thisStream);
+			continue;
+			}
+
+		if((strcmp(aArgv[argIndex], "--help") == 0) || (strcmp(aArgv[argIndex], "-h") == 0))
+			{
+			usage();
+			continue;
+			}
+
+		// Not a general option, probably an input file...
+		break;
+		}
+	
+	// Process main arguments
+	for(; argIndex < aArgc; ++argIndex)
+		{
+		std::string arg(aArgv[argIndex]);
+
+		bool gotFile = false;
+		if((strcmp(aArgv[argIndex], "--help") == 0) || (strcmp(aArgv[argIndex], "-h") == 0))
+			{
+			usage();
+			continue;
+			}
+		
+		if(arg.find(OPT_CHDIR) == 0)
+			{
+			dirs->push_back(arg.substr(OPT_CHDIR.size(), arg.npos));
+			// Move to next option
+			++argIndex;
+			if(argIndex >= aArgc) break;
+			arg = aArgv[argIndex];
+			}
+		else
+			{
+			dirs->push_back(".");
+			}
+
+		std::string fileArg;
+		if(arg.find(OPT_BCERTCLIENTS_L) == 0)
+			{
+			fileTypes->push_back(EBinCertClients);
+			fileArg = arg.substr(OPT_BCERTCLIENTS_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_BCERTCLIENTS_S) == 0)
+			{
+			fileTypes->push_back(EBinCertClients);
+			fileArg = arg.substr(OPT_BCERTCLIENTS_S.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HCERTCLIENTS_L) == 0)
+			{
+			fileTypes->push_back(EHumanCertClients);
+			fileArg = arg.substr(OPT_HCERTCLIENTS_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HCERTCLIENTS_S) == 0)
+			{
+			fileTypes->push_back(EHumanCertClients);
+			fileArg = arg.substr(OPT_HCERTCLIENTS_S.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_BFILECERTSTORE_L) == 0)
+			{
+			fileTypes->push_back(EBinFileCertStore);
+			fileArg = arg.substr(OPT_BFILECERTSTORE_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_BFILECERTSTORE_S) == 0)
+			{
+			fileTypes->push_back(EBinFileCertStore);
+			fileArg = arg.substr(OPT_BFILECERTSTORE_S.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HFILECERTSTORE_L) == 0)
+			{
+			fileTypes->push_back(EHumanFileCertStore);
+			fileArg = arg.substr(OPT_HFILECERTSTORE_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HFILECERTSTORE_S) == 0)
+			{
+			fileTypes->push_back(EHumanFileCertStore);
+			fileArg = arg.substr(OPT_HFILECERTSTORE_S.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_BSWICERTSTORE_L) == 0)
+			{
+			fileTypes->push_back(EBinSwiCertStore);
+			fileArg = arg.substr(OPT_BSWICERTSTORE_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_BSWICERTSTORE_S) == 0)
+			{
+			fileTypes->push_back(EBinSwiCertStore);
+			fileArg = arg.substr(OPT_BSWICERTSTORE_S.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HSWICERTSTORE_L) == 0)
+			{
+			fileTypes->push_back(EHumanSwiCertStore);
+			fileArg = arg.substr(OPT_HSWICERTSTORE_L.size(), arg.npos);
+			gotFile = true;
+			}
+		if(arg.find(OPT_HSWICERTSTORE_S) == 0)
+			{
+			fileTypes->push_back(EHumanSwiCertStore);
+			fileArg = arg.substr(OPT_HSWICERTSTORE_S.size(), arg.npos);
+			gotFile = true;
+			}
+
+		if(arg.find("--out") == 0)
+			{
+			files = &aOutputFiles;
+			dirs = &aOutputDirs;
+			fileTypes = &aOutputFileTypes;
+			continue;
+			}
+
+		if(arg.find("--in") == 0)
+			{
+			files = &aInputFiles;
+			dirs = &aInputDirs;
+			fileTypes = &aInputFileTypes;
+			continue;
+			}
+
+		if(gotFile)
+			{
+			files->push_back(fileArg);
+			continue;
+			}
+		
+		usage();
+		dbg << Log::Indent() << "Unknown option " << (const char *) aArgv[argIndex] << Log::Endl();
+		FatalError();
+		}
+	return;
+}
+
+void ProcessCertClientFiles(const std::string &aBaseDir,
+							const StringVector &aInputFiles,
+							const StringVector &aInputDirs,
+							const CertStoreFileTypeVector &aInputFileTypes,
+							bool &aAllowDuplicates,
+							EncDecContainer &aCertAppInfoContainer)
+{
+	AppMap appMap;
+	for(int fileIndex = aInputFiles.size()-1; fileIndex >= 0 ; --fileIndex)
+		{
+		CertStoreFileType fileType = aInputFileTypes[fileIndex];
+		if((fileType != EBinCertClients) && (fileType != EHumanCertClients))
+			{
+			continue;
+			}
+		
+		// Change to correct directory
+		ChangeDir(aBaseDir, aInputDirs[fileIndex]);
+
+		EncDecContainer tmpCertInfoContainer("ClientInfo", CertificateAppInfo::Factory);
+
+		if(fileType == EBinCertClients)
+			{
+			prog << Log::Indent() << "Reading binary certclients file '" << aInputFiles[fileIndex] << "'" << Log::Endl();
+			AutoIndent ai(prog);
+			readContainer(aInputFiles[fileIndex], false, tmpCertInfoContainer);
+			}
+		else
+			{
+			prog << Log::Indent() << "Reading human certclients file '" << aInputFiles[fileIndex] << "'" << Log::Endl();
+			AutoIndent ai(prog);
+			readContainer(aInputFiles[fileIndex], true, tmpCertInfoContainer);
+			}
+
+		// Now merge the new file into the running store.
+		prog << Log::Indent() << "Merging certclients data" << Log::Endl();
+		AutoIndent ai(prog);
+		AutoIndent ai2(dbg);
+		for(TUint32 entryIndex = 0; entryIndex < tmpCertInfoContainer.size(); ++entryIndex)
+			{
+			const CertificateAppInfo &info = static_cast<const CertificateAppInfo &>(tmpCertInfoContainer[entryIndex]);
+			std::string nname = stringFromUtf16(info.Name());
+			//prog << Log::Indent() << "checking " << nname << Log::Endl();
+
+
+			TInt32 lastIndex;
+			std::string firstDef;
+			if(!AppUidMap::InsertUidDefinition(info.Id().iUid, nname, fileIndex,
+											   lastIndex, firstDef))
+				{
+				// Duplicate entry for UID
+				if(nname == firstDef)
+					{
+					// But both entries have the same value
+					prog << Log::Indent() << "Duplicate, but identical, entries for UID 0x" << info.Id().iUid << " '" << nname << "'." << Log::Endl();
+					AutoIndent ai(prog);
+					prog << Log::Indent() << "From files '" << aInputFiles[lastIndex] << "' and '" << aInputFiles[fileIndex] << "'." << Log::Endl();
+					}
+				else
+					{
+					// Entries have different values
+					dbg << Log::Indent() << "DUPLICATE entry for UID 0x" << info.Id().iUid << Log::Endl();
+					AutoIndent ai(dbg);
+					dbg << Log::Indent() << "Ignoring '" << nname << "' from '"  << aInputFiles[fileIndex] << "'." << Log::Endl();
+					dbg << Log::Indent() << "Keeping '" << firstDef << "' from '" << aInputFiles[lastIndex] << "'." << Log::Endl();
+					if(lastIndex == fileIndex)
+						{
+						if(fileType == EBinCertClients)
+							{
+							dbg << Log::Indent() << "Both entries are in the same binary same file!" << Log::Endl();
+							continue; // Skip adding duplicate
+							}
+						dbg << Log::Indent() << "Clash is within a single text configuration file!" << Log::Endl();
+						if(!aAllowDuplicates)
+							{
+							FatalError();
+							}
+						}
+					}
+
+				// Only add duplicates when debugging and the add is
+				// from a human readable config file.
+				if(!aAllowDuplicates || (fileType != EHumanCertClients))
+					{
+					continue; // Skip adding duplicate
+					}
+				dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl();
+				}
+
+			// Add entry
+			CertificateAppInfo *newInfo = new CertificateAppInfo;
+			*newInfo = info;
+			aCertAppInfoContainer.push_back(newInfo);
+			}
+		}
+}
+
+
+
+/**
+   Write output files to disk
+ */
+void GenerateOutputFiles(const EncDecContainer &aCertAppInfoContainer,
+						 const EncDecContainer &aFileCertStoreContainer,
+						 const EncDecContainer &aSwiCertStoreContainer,
+						 const std::string &aBaseDir,
+						 const StringVector &aOutputFiles, 
+						 const StringVector &aOutputDirs,
+						 const CertStoreFileTypeVector &aOutputFileTypes,
+						 bool aVerbose, bool aPemOut)
+{
+	for(int fileIndex = aOutputFiles.size()-1; fileIndex >= 0 ; --fileIndex)
+		{
+		// Change to correct directory
+		ChangeDir(aBaseDir, aOutputDirs[fileIndex]);
+		
+		CertStoreFileType fileType = aOutputFileTypes[fileIndex];
+		//
+		// Set the container and write mode to use
+		//
+		const EncDecContainer *container = 0;
+		bool humanReadable = false;
+		if(fileType == EBinCertClients)
+			{
+			container = &aCertAppInfoContainer;
+			humanReadable = false;
+			}
+		if(fileType == EHumanCertClients)
+			{
+			container = &aCertAppInfoContainer;
+			humanReadable = true;
+			}
+		if(fileType == EBinFileCertStore)
+			{
+			container = &aFileCertStoreContainer;
+			humanReadable = false;
+			}
+		if(fileType == EHumanFileCertStore)
+			{
+			container = &aFileCertStoreContainer;
+			humanReadable = true;
+			}
+		if(fileType == EBinSwiCertStore)
+			{
+			container = &aSwiCertStoreContainer;
+			humanReadable = false;
+			}
+		if(fileType == EHumanSwiCertStore)
+			{
+			container = &aSwiCertStoreContainer;
+			humanReadable = true;
+			}
+		
+		if(container == 0)
+			{
+			// failed to decode the output file type!
+			FatalError();
+			}
+		//
+		// Write the container out
+		//
+		writeContainer(aOutputFiles[fileIndex].c_str(), humanReadable, aPemOut, aVerbose, *container);
+		}
+	return;
+}
+
+
+
+/**
+   ValidateLabel
+
+   This function maintains a map of certificate labels to input file
+   (ie file index) and definition.
+   
+   If the label is NOT already defined in the map, then the function
+   returns true, which instructs the caller to include the
+   label/certificate in the generated store.
+
+   If the label is already defined in the map, then the function
+   returns false, which instructs the caller to NOT include the
+   label/certificate in the generated store.
+
+   The files on the command line are processed right to left, so if
+   multiple definitions (for the same label) are seen, only the first
+   will be included in the generated store.
+
+   The information saved in the map is used to generate helpful
+   warning/error messages as follows:-
+
+   1) The saved definition is the first definition encountered, and is
+   therefore the one which has been included in the store.
+
+   2) The saved file index is the index of the LAST file processed
+   containing a definition for this label. This may be different to
+   the one containing the first definition.
+
+   Consider the following sequence:-
+
+   First processed file - Definition for label Fred
+   Second processed file - Another two definitions for label Fred
+
+   When processing the third definition (in the second file), the
+   saved file index will be that of the second file. The code uses
+   this to check if there are multiple definitions within a SINGLE
+   input file, for the same label.
+
+   If the multiple definitions are within a single human readable
+   file, then this is considered a fatal error, otherwise only a
+   warning is generated.
+
+   Duplicate definitions in different files, generate a warning.
+ */
+bool ValidateLabel(FCSLabelMap &aLabelMap, 
+				   const StringVector &aInputFiles, 
+				   CertStoreFileType aFileType,
+				   TUint32 aFileIndex, 
+				   const TCertLabel &aCertLabel)
+{
+	std::string nname = stringFromUtf16(aCertLabel);
+	FCSLabelMap::value_type e(nname, aFileIndex);
+	std::pair<FCSLabelMap::iterator,bool> result = aLabelMap.insert(e);
+	if(result.second == true)
+		{
+		// Not a duplicate
+		return true;
+		}
+	
+	// Duplicate label entry
+	dbg << Log::Indent() << "DUPLICATE label detected in '" << aInputFiles[aFileIndex] << "'." << Log::Endl();
+	AutoIndent ai(dbg);
+	dbg << Log::Indent() << "Duplicate entry for '" << e.first << "' ignored. Keeping entry from '" << aInputFiles[(*result.first).second] << "'" <<Log::Endl();
+	if((result.first)->second == TUint32(aFileIndex))
+		{
+		dbg << Log::Indent() << "Both entries are in the same file." << Log::Endl();
+		if((aFileType == EHumanFileCertStore) || (aFileType == EHumanSwiCertStore))
+			{
+			dbg << Log::Indent() << "FATAL error - Clash is within a single text configuration file - aborting!" << Log::Endl();
+			FatalError();
+			}
+		return false; // Insert failed, keep earlier def
+		}
+	else
+		{
+		// Update file index for last definition. This is used to
+		// detect if the last two defs were within a single file.
+		(result.first)->second = TUint32(aFileIndex);
+		return false; // Insert failed, keep earlier def
+		}
+	return false;
+}
+
+void BuildSubjectToSubjectKeyIdMap(const EncDecContainer &aCertStoreContainer,
+								   SubjectToSubjectKeyIdMap &aSubjectMap)
+{
+	// Collect subjectName/SubjectKeyId for all certs
+	for(TUint32 entryIndex=0; entryIndex < aCertStoreContainer.size(); ++entryIndex)
+		{
+		const CertStoreEntry &entry = static_cast<const CertStoreEntry &>(aCertStoreContainer[entryIndex]);
+		if(entry.Info().CertificateFormat() !=  EX509Certificate)
+			{
+			continue;
+			}
+
+		std::pair<SubjectToSubjectKeyIdMap::key_type, SubjectToSubjectKeyIdMap::mapped_type> e;
+		e.first = entry.CertSubject();
+		e.second.iDuplicate = false;
+		e.second.iLabel = stringFromUtf16(entry.Label());
+		e.second.iSubjectKeyIdentifier =  entry.Info().SubjectKeyId().iHash;
+		std::pair<SubjectToSubjectKeyIdMap::iterator, bool> result = aSubjectMap.insert(e);
+		if(result.second == false)
+			{
+			dbg << Log::Indent() << "WARNING: Certificate '" << e.second.iLabel << "' has a subject name of '" << e.first << "' which clashes with certificate '" << (*result.first).second.iLabel <<"'" << Log::Endl();
+			(*result.first).second.iDuplicate = true;
+			}
+		}
+	
+}
+
+
+void SetIssuerKeyId(SubjectToSubjectKeyIdMap &aSubjectMap,
+					EUseCertificateExtension aUseExtension,
+					EncDecContainer &aCertStoreContainer)
+{
+	// Now loop across certs setting the issuer key id.
+	for(TUint32 entryIndex=0; entryIndex < aCertStoreContainer.size(); ++entryIndex)
+		{
+		CertStoreEntry &entry = static_cast<CertStoreEntry &>(aCertStoreContainer[entryIndex]);
+		if(entry.Info().CertificateFormat() !=  EX509Certificate)
+			{
+			continue;
+			}
+		if(!entry.Info().IssuerKeyId().iAutoKey)
+			{
+			continue;
+			}
+
+		std::string certLabel = stringFromUtf16(entry.Label());
+		
+		prog << Log::Indent() << "Attempting to auto set IssuerIeyId for '" << certLabel << "'" << Log::Endl();
+
+		AutoIndent ai(prog);
+
+		// Lookup issuer key id in certificate extension and if found use that.
+		// 
+		// X509IssuerKeyId will always set the issuerName.
+		// If aIgnoreExtension is false, then it will attempt to read
+		// the AuthorityKeyId extension. If found (and <160bits), it
+		// will write the ID to issuerId and return true.
+		// Otherwise it will return false.
+		// Certificate read errors are fatal.
+		std::string issuerName;
+		TKeyIdentifier issuerId;
+		if(X509IssuerKeyId(aUseExtension,
+						   entry.CertData(), entry.Info().CertSize(),
+						   issuerName, issuerId))
+			{
+			// There is an authority key id extension so use its value
+			prog << Log::Indent() << "Using value from certificate '" << certLabel << "' extension" << Log::Endl();
+			entry.Info().IssuerKeyId().iHash = issuerId;
+			}
+		else
+			{
+			// No extension so lookup issuerName in the map
+			prog << Log::Indent() << "Looking up issuer '" << issuerName << "' in map" << Log::Endl();
+			SubjectToSubjectKeyIdMap::const_iterator it = aSubjectMap.find(issuerName);
+			if(it == aSubjectMap.end())
+				{
+				prog << Log::Indent() << "Not found - Using empty IssuerKeyId " << Log::Endl();
+				}
+			else
+				{
+				if((*it).second.iDuplicate)
+					{
+					prog << Log::Indent() << "Found - but multiple certs with matching subject name - Using empty IssuerKeyId" << Log::Endl();
+					}
+				else
+					{
+					prog << Log::Indent() << "Found - Using Subject Key of matching cert" << Log::Endl();
+					entry.Info().IssuerKeyId().iHash = (*it).second.iSubjectKeyIdentifier;
+					}
+				}
+			}
+		}
+
+}
+
+
+
+// End of file