/*
* Copyright (c) 1997-2010 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: 
*
*/


/**
 @file 
 @released
 @internalTechnology
*/
 
#include <f32file.h>

#include "securitymanager.h"
#include "swi/sishash.h"
#include "swi/sisdataprovider.h"
#include "swi/sissignaturealgorithm.h"
#include "swi/sissignaturecertificatechain.h"
#include "swi/siscertificatechain.h"
#include "swi/sisinfo.h"
#include "swi/sissupportedoptions.h"
#include "swi/sissupportedlanguages.h"
#include "swi/sisprerequisites.h"
#include "swi/sislogo.h"
#include "swi/sisproperties.h"
#include "swi/sisinstallblock.h"
#include "swi/sistruststatus.h"
#include "hashcontainer.h"
#include "certchainconstraints.h"
#include "devinfosupportclient.h"
#include "cleanuputils.h"
#include "log.h"

// PKIX dependencies
#include <pkixcertchain.h>
#include <x509keys.h> 
#include <ocsp.h>
#include <securitydefsconst.h>

#include <ccertattributefilter.h>
#include <ct/mcttoken.h>
//#include <mctcertstore.h>
#include "cfstokentypeclient.h"

// Crypto dependencies
#include <hash.h>
#include <swicertstore.h>

// SecMan includes
#include "chainvalidator.h"
#include "certificateretriever.h"
#include "signatureverifier.h"
#include "securitypolicy.h"
#include "revocationhandler.h"

const TInt KFileBufferSize = 1024;  // 1k buffer used to read the data to be hashed
_LIT(KSecurityManagerName, "_Security_Manager_");


using namespace Swi;

//
// Life Cycle methods
//

EXPORT_C CSecurityManager* CSecurityManager::NewL()
	{
	CSecurityManager* self = CSecurityManager::NewLC();
	CleanupStack::Pop(self);
	return self;
	}
 
EXPORT_C CSecurityManager* CSecurityManager::NewLC()
	{
	CSecurityManager* self = new(ELeave) CSecurityManager();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

EXPORT_C CSecurityPolicy& CSecurityManager::SecurityPolicy() const
	{
	return *iSecPolicy;
	}

CSecurityManager::CSecurityManager() : CActive(EPriorityNormal) 
	{
	CActiveScheduler::Add(this);	
	}

CSecurityManager::~CSecurityManager()
	{	
	Deque();

	if (iCurrentPkixChain)
		{
		delete iCurrentPkixChain;
		}

	if (iCertStore)
		{
		iCertStore->Release();
		}
	
	if (iCertificateRetriever)
		{
		delete iCertificateRetriever;
		}
		
	if (iChainValidator)
		{
		delete iChainValidator;
		}
		
	if (iRevocationHandler)
		{
		delete iRevocationHandler;
		}
		
	iValidPkixChains.ResetAndDestroy();
	iUntrustedRoots.ResetAndDestroy();
	iDeviceIDs.ResetAndDestroy();
	TInt count = iTrustedRoots.Count();
	// Release certificates from the list
	for (TInt i = 0; i < count; ++i)
		{
		iTrustedRoots[0]->Release(); 
		iTrustedRoots.Remove(0);
		}
	iTrustedRoots.Close();	
	iCertMetaInfo.Reset();
	iFs.Close();
	}


void CSecurityManager::ConstructL()
	{	
	User::LeaveIfError(iFs.Connect());
	iSecPolicy = CSecurityPolicy::GetSecurityPolicyL();	
	iCertStore=CSWICertStore::NewL(iFs);
	}
	
	
//
// Active Objects methods
//

void CSecurityManager::RunL()
	{
	DEBUG_PRINTF3(_L8("Security Manager - RunL(). State: %d, Status: %d."),
		iState, iStatus.Int());
	
	if (iStatus.Int() != KErrNone)
		{
		User::Leave(iStatus.Int());     // Hop into RunError()
		}
		
	switch (iState)
		{ 			
		case ERetrievedTrustedRoots:
			{
			iCertMetaInfo.Reset();
			TInt trustedCertCount = iTrustedRoots.Count();
			for (TInt i = 0; i < trustedCertCount; ++i)
				{
				CCTCertInfo& certInfo=*iTrustedRoots[i];
				DEBUG_CODE_SECTION(			
					DEBUG_PRINTF2(_L("Security Manager - SWI certstore contains trust anchor certificate '%S'"),
						&(certInfo.Label()));
						);
				TCertMetaInfo metaInfo = iCertStore->CertMetaInfoL(certInfo);
				iCertMetaInfo.AppendL(metaInfo);
				if (metaInfo.iIsMandatory)		
					{
					iMandatoryCertDNCount++;
					DEBUG_CODE_SECTION(			
					DEBUG_PRINTF2(_L("Security Manager - SWI certstore contains mandatory certificate '%S'"),
						&(certInfo.Label()));
						);
					}
				if (metaInfo.iIsSystemUpgrade)		
					{
					DEBUG_CODE_SECTION(			
					DEBUG_PRINTF2(_L("Security Manager - SWI certstore contains system upgrade certificate '%S'"),
						&(certInfo.Label()));
						);
					}
				}
			// After we get here, we have retrieved mandatory certificates
			// We may have an unsigned sis file in which case we do not want to verify the block
			// since there isn't one, so bail here with the correct error
			if (iChains.Count() == 0)
					{
					if (iMandatoryCertDNCount == 0)
						{
						*iResult = ESignatureNotPresent; // No mandatory certs, so just say unsigned
						}
					else
						{
						*iResult = EMandatorySignatureMissing; // more important error code overrides
						}
					
					User::RequestComplete(iClientStatus, KErrNone);							
					break;
					}
	
		
			VerifyBlockL(iCurrentChain);			
			}
			break;
			
		case EValidatingChain :
			{
			CPKIXValidationResultBase* result = (*iValidationResultsOut)[iCurrentChain];
			::TValidationStatus resultStatus = result->Error(); 	

			DEBUG_PRINTF3(_L8("Security Manager - Certificate validation result was %d. (If applicable, error on certificate %d.)"),
				resultStatus.iReason, resultStatus.iCert);

			iCurrentChain++; // Next chain to validate

			if (resultStatus.iReason != EValidatedOK)
				{
				// we can discard the invalid cert chain
				delete iCurrentPkixChain;
				iCurrentPkixChain = NULL;
				
				// ooops
				if (iChains.Count() > iCurrentChain)   // This is the ValidateANY policy
					{
					VerifyBlockL(iCurrentChain);
					break;						
					}
				}
			else
				{
				iHasValidated = ETrue; // At least one chain has been validated! Hurrah!
				
				// check if a chain has validated that is not self signed
				if (*iResult != ESignatureSelfSigned)
					{
					iHasValidatedTrusted = ETrue;
					}
				
			    // From here on the chain has been validated successfully
						
			    iTotalCapabilitiesOut->Union(iCurrentCapabilities); // Update the total capabilities
			
			    // Reduce the count from the list of mandatory certs
			    UpdateListOfMissingRequiredCertsL(iCurrentPkixChain->Cert(iCurrentPkixChain->Count()-1));
				
				// Updage the System Upgrade status of the Certificate
				UpdateSystemUpgradeCertStatusL(iCurrentPkixChain->Cert(iCurrentPkixChain->Count()-1));
				
			    User::LeaveIfError(iValidPkixChains.Append(iCurrentPkixChain));
			    // this chain will be used for OCSP checking if required, so record it.
			    iController->AddChainIndex(iCurrentChain-1);
			    
			    iCurrentPkixChain = NULL;
				}

			if (iCurrentChain == iChains.Count())
				{
				if (iMandatoryCertDNCount == 0)
					{
					// Mandatory certs prerequisites met				

					if (iHasValidated)
						{
						//Build the certificate chain constraints
						CCertChainConstraints* certChainConstraints(0);
						TRAPD(err, certChainConstraints=CCertChainConstraints::NewL(iValidPkixChains));
						if (err)
							{
							*iResult = ECertificateValidationError;
							User::RequestComplete(iClientStatus, KErrNone);
							return;
							}

						*iResult = iHasValidatedTrusted ? EValidationSucceeded : ESignatureSelfSigned;
						
						//Pass the certificate chain constraints ownership to controller
						Sis::CController* controller=const_cast<Sis::CController*>(iController);
						controller->SetCertChainConstraints(certChainConstraints);

						//Set the dev cert found flag if dev cert first time found
						if (iDevCertWarningState==ENoDevCerts && 
							(certChainConstraints->SIDsAreConstrained() 
							||certChainConstraints->VIDsAreConstrained()
							||certChainConstraints->DeviceIDsAreConstrained()
							||certChainConstraints->CapabilitiesAreConstrained()))
							{
							DEBUG_PRINTF(_L("Security Manager - At least one certificate chain contains a devcert."));
							iDevCertWarningState=EFoundDevCerts;
							}

						//Check if the device id is contrained and the device Id has been retrieved
						if (certChainConstraints->DeviceIDsAreConstrained() && iDeviceIDs.Count()==0)
							{
				 			//Retrieve and Save the device ID list then complete
				 			RDeviceInfo deviceInfo;
				 			User::LeaveIfError(deviceInfo.Connect());
				 			CleanupClosePushL(deviceInfo);
 							const RPointerArray<HBufC>& tempbuf=deviceInfo.DeviceIdsL();
				 			for (TInt i=0;i<tempbuf.Count();i++)
					 			{
					 			HBufC* element=tempbuf[i]->AllocLC();
				  				iDeviceIDs.AppendL(element);
				  				CleanupStack::Pop(element);	 				
					 			}
					 		CleanupStack::PopAndDestroy(&deviceInfo);					
							}						
						iState = EChecksDone;
						TRequestStatus* status = &iStatus;
						User::RequestComplete(status, KErrNone);
						SetActive();
						return;
						}
					}

				if (iMandatoryCertDNCount != 0)
					{
					*iResult = EMandatorySignatureMissing;  // We did not meet mandatory cert requirements!
					}
				else
					{
					if (!iHasValidated) 
						{
						*iResult = ECertificateValidationError; // No chain ever validated 
						}
					}
	
				User::RequestComplete(iClientStatus, KErrNone);							
				}
			else
				{
				VerifyBlockL(iCurrentChain);
				return;					
				}
			}
			break;

		case ERevalidatingChain:
			{
			TInt chainIndex = (*iChainListIndices)[iCurrentChain];
			
			CPKIXValidationResultBase* result = (*iValidationResultsOut)[iCurrentChain];
			::TValidationStatus resultStatus = result->Error(); 	

			DEBUG_PRINTF3(_L8("Security Manager - Certificate validation result was %d. (If applicable, error on certificate %d.)"),
				resultStatus.iReason, resultStatus.iCert);
				
			iCurrentChain++; // Next chain to validate

			if (resultStatus.iReason != EValidatedOK)
				{
				// We can discard the invalid chain.
				delete iCurrentPkixChain;
				iCurrentPkixChain = NULL;
				
				// ooops
				if (iChains.Count() > chainIndex)  	
					{ // This is the ValidateANY policy
					VerifyBlockL(chainIndex);
					break;						
					}
				}
			else
				{
				iHasValidated = ETrue; // At least one chain has been validated! Hurrah!
				
				// check if a chain has validated that is not self signed
				if (*iResult != ESignatureSelfSigned)
					{
					iHasValidatedTrusted = ETrue;
					}
				
				// From here on the chain has been validated successfully
						
				iTotalCapabilitiesOut->Union(iCurrentCapabilities); // Update the total capabilities
			
				// Reduce the count from the list of mandatory certs
				UpdateListOfMissingRequiredCertsL(iCurrentPkixChain->Cert(iCurrentPkixChain->Count()-1));
			
				// Updage the System Upgrade status of the Certificate
				UpdateSystemUpgradeCertStatusL(iCurrentPkixChain->Cert(iCurrentPkixChain->Count()-1));
				
				iValidPkixChains.AppendL(iCurrentPkixChain);
					
				iCurrentPkixChain = NULL;
				}

			if (chainIndex == iChains.Count())
				{
				if (iHasValidated)
					{
					//Build the certificate chain constraints
					CCertChainConstraints* certChainConstraints(0);
					TRAPD(err, certChainConstraints=CCertChainConstraints::NewL(iValidPkixChains));
					if (err)
						{
						*iResult = ECertificateValidationError;
						User::RequestComplete(iClientStatus, KErrNone);
						return;
						}
					else
						{
						*iResult = iHasValidatedTrusted ? EValidationSucceeded : ESignatureSelfSigned;								
						}

					//Pass the certificate chain constraints ownership to controller
					Sis::CController* controller=const_cast<Sis::CController*>(iController);
					controller->SetCertChainConstraints(certChainConstraints);
					
					//Check if the device id is contrained and the device Id has been retrieved
					if (certChainConstraints->DeviceIDsAreConstrained() && iDeviceIDs.Count()==0)
						{
				 		//Retrieve and Save the device ID list then complete
				 		RDeviceInfo deviceInfo;
				 		User::LeaveIfError(deviceInfo.Connect());
				 		CleanupClosePushL(deviceInfo);
 						const RPointerArray<HBufC>& tempbuf=deviceInfo.DeviceIdsL();
				 		for (TInt i=0;i<tempbuf.Count();i++)
					 		{
							HBufC* element=tempbuf[i]->AllocLC();
			  				iDeviceIDs.AppendL(element);
			  				CleanupStack::Pop(element);	 				
					 		}
					 	CleanupStack::PopAndDestroy(&deviceInfo);					
						}						
					iState = EChecksDone;
					TRequestStatus* status = &iStatus;
					User::RequestComplete(status, KErrNone);
					SetActive();
					return;
					}

				else
					{
					*iResult = ECertificateValidationError; // No chain ever validated 
					}

				User::RequestComplete(iClientStatus, KErrNone);							
				}
			else
				{
				VerifyBlockL(chainIndex);
				return;					
				}
			}
			break;

		case EOCSPCheck:
			{
			iState = EChecksDone;
			ProcessOcspOutcomesL();
			
			TRequestStatus* status = &iStatus;
			User::RequestComplete(status, KErrNone);
			SetActive();			
			}
			break;
			
		case EChecksDone:
			{
			// If the validation is done by certs passed by third party(API user)
			// instead of cert-store then even on successful validation
			// the result of validation is ESignatureSelfSigned. 
			// But if iUntrustedRoots count is not zero then the result is
			// really ESignatureSelfSigned.
			if (NULL != iRootCerts && iHasValidated)
				{
				*iResult = EValidationSucceeded;
				}
			// Complete the client and be happy
			User::RequestComplete(iClientStatus, KErrNone);			
			}
			break;
			
		default:
			{
			User::Panic(KSecurityManagerName, 1); 		
			}
		}
	}


void CSecurityManager::ProcessOcspOutcomesL() 
	{
	
	 // Identify the worst outcome overall, and the worst in each chain.
 	// The OCSP outcomes are in the same order as the valid chains and
 	// certificates within them, with the number of outcomes per chain
 	// one fewer than the number of certs in the chain.
 

	TInt numOutcomes=iRevocationHandler->TransactionCount();
	TInt currentChain = 0;
 	TInt chainErrorLevel = 0;
 
 	// Since the number of outcomes is 1 fewer than the number of certs in the
 	// PkixChain, the index for the last outcome of the first chain will be
 	// 2 lower than the number of certs in the chain.
 	TInt chainLastOutcomeIndex = iValidPkixChains[0]->Count() - 2;
 	TInt chainWorstOutcome = 0;
 	
	// This variable is used to record the most serious error condition.
	TInt errorLevel = 0;

	DEBUG_PRINTF2(_L8("Security Manager - OCSP check complete. %d OCSP outcomes."), numOutcomes);

	for (TInt i=0; i<numOutcomes; i++)
		{
		const TOCSPOutcome& outcome=iRevocationHandler->Outcome(i);
		
		DEBUG_PRINTF4(_L8("Security Manager - OCSP outcome %d; Status: %d, Result: %d."),
			i, outcome.iStatus, outcome.iResult);
		
		if (outcome.iResult == OCSP::ERevoked)
			{
			// If any certificate is revoked then installation must be aborted
 			// but we still want to identify the worst case for each chain for
 			// the purposes of the error dialog.

			*iRevocationMessage = ECertificateStatusIsRevoked;
			errorLevel = 6;
 			chainWorstOutcome = i;
 			
 			// Skip the rest of the outcomes for this chain.
 			i = chainLastOutcomeIndex;
			}
		else if (outcome.iResult != OCSP::EGood)
			{			
			switch (outcome.iStatus)			
				{
				// permanent errors, no user interaction is required
				case OCSP::ENoServerSpecified:
				case OCSP::EClientInternalError:
				case OCSP::EMalformedRequest:
				case OCSP::EUnknownResponseType:
				case OCSP::EClientUnauthorised:
				case OCSP::EUnknownCriticalExtension:
				case OCSP::EMissingCertificates:
				case OCSP::ESignatureRequired:				
				case OCSP::EThisUpdateTooLate:
				case OCSP::EThisUpdateTooEarly:
				case OCSP::ENextUpdateTooEarly:
				case OCSP::ECertificateNotValidAtValidationTime:				
				case OCSP::ENonceMismatch:
					if (errorLevel < 5)
						{
						errorLevel = 5;
						*iRevocationMessage = EInvalidCertificateStatusInformation;
						}
					if (chainErrorLevel < 5)
 						{
 						chainErrorLevel = 5;
 						chainWorstOutcome = i;
 						}
					break;
				case OCSP::EResponseSignatureValidationFailure:
					if (errorLevel < 4)
						{
						errorLevel = 4;
						*iRevocationMessage = EResponseSignatureValidationFailure;			
						}	
					if (chainErrorLevel < 4)
 						{
 						chainErrorLevel = 4;
 						chainWorstOutcome = i;
						}
					break;
				// permanent errors, ask the user	
				case OCSP::EValid:
					if (errorLevel < 3)
						{
						errorLevel = 3;
						*iRevocationMessage = ECertificateStatusIsUnknown;
						}	
					if (chainErrorLevel < 3)
 						{
 						chainErrorLevel = 3;
 						chainWorstOutcome = i;
 						}
					break;					
				// transient errors - user can retry
				case OCSP::EInvalidURI:					
					if (errorLevel < 2)
						{
						errorLevel = 2;
						*iRevocationMessage = EInvalidRevocationServerUrl;
						}
					if (chainErrorLevel < 2)
 						{
 						chainErrorLevel = 2;
 						chainWorstOutcome = i;
						}
					break;
				case OCSP::ETryLater:
				case OCSP::ETransportError:
				case OCSP::EServerInternalError:
				case OCSP::EMissingNonce:
					if (errorLevel < 1)
						{
						errorLevel = 1;
						*iRevocationMessage = EUnableToObtainCertificateStatus;
						}	
 					if (chainErrorLevel < 1)
 						{
 						chainErrorLevel = 1;
 						chainWorstOutcome = i;
 						}

					break;	
				default:
					// All possible OCSP responses should be checked
					__ASSERT_ALWAYS(EFalse, User::Leave(KErrArgument));
					break;
				}	
			}

 		// If this is the end of the chain, store the worst outcome for the
 		// chain in the output outcome array.
 		if (i == chainLastOutcomeIndex)
 			{
 			TOCSPOutcome* chainOutcome=new(ELeave) TOCSPOutcome(iRevocationHandler->Outcome(chainWorstOutcome));
 			CleanupStack::PushL(chainOutcome);
 			iOcspOutcomeOut->AppendL(chainOutcome);
 			CleanupStack::Pop(chainOutcome);
 
 			// If there are more chains to consider, get set to identify the
 			// worst outcome in the next chain.
 			if (++currentChain < iValidPkixChains.Count())
 				{
 				chainErrorLevel = 0;
 				chainWorstOutcome = i + 1;
 				chainLastOutcomeIndex += iValidPkixChains[currentChain]->Count() - 1;
 				}
 			}

		}
	}

void CSecurityManager::DoCancel()
	{
	DEBUG_PRINTF2(_L8("Security Manager - Cancelling in state %d."), iState);
	
	switch (iState)
		{
		case ERetrievedTrustedRoots:
			{
			iCertificateRetriever->Cancel();
			}
			break;
			
		case EValidatingChain:
		case ERevalidatingChain:
			{
			iChainValidator->Cancel();		
			}
			break;
			
		case EOCSPCheck:
			{
			iRevocationHandler->Cancel();
			}
			break;
			
		case EChecksDone:	
		default: 
			{
			// Do nothing
			}
		}
	if(iClientStatus)
		{
		User::RequestComplete(iClientStatus,KErrCancel);		
		}
	}

TInt CSecurityManager::RunError(TInt aError)
	{
	DEBUG_PRINTF2(_L8("Security Manager - RunError(). Error code %d."), aError);
	
	User::RequestComplete(iClientStatus, aError);			
	return KErrNone; 
	}


//
// Business methods
//

EXPORT_C void CSecurityManager::PerformOcspL(const TDesC8& aOcspUri, 
											 TUint32& aIap,
											 TRevocationDialogMessage* aRevocationMessageOut, 
											 RPointerArray<TOCSPOutcome>& aOcspOutcomeOut, 
											 RPointerArray<CX509Certificate>& aCertOut,
											 TRequestStatus& aStatus)
	{	
	CleanupResetAndDestroyPushL(aCertOut);
	Cancel();
	
	DEBUG_PRINTF2(_L8("Security Manager - Performing OCSP with revocation server at %S."),
		&aOcspUri);

	// Reset and re-populate certificate list to contain only end certificates
 	// from valid chains, so that the end cert list can be correlated with the
 	// list of outcomes.
 	aCertOut.ResetAndDestroy();
 	TInt numChains = iValidPkixChains.Count();
 	for (TInt index = 0; index < numChains; index++)
 		{
 		CX509Certificate* certOut = CX509Certificate::NewLC(iValidPkixChains[index]->Cert(0));
 		aCertOut.AppendL(certOut);
 		CleanupStack::Pop(certOut);
 		}
 	
 	DEBUG_PRINTF2(_L8("Security Manager - Validating %d certificate chains for this controller."), numChains);

	iClientStatus = &aStatus;
	*iClientStatus = KRequestPending;

	iOcspOutcomeOut = &aOcspOutcomeOut;
	iRevocationMessage = aRevocationMessageOut;
	
	// Make sure to delete earlier object
	if (iRevocationHandler)
		{
		delete iRevocationHandler;
		iRevocationHandler = NULL;
		}	
	iRevocationHandler = CRevocationHandler::NewL(*iCertStore);
	iRevocationHandler->SetDefaultURIL(aOcspUri);
	iState = EOCSPCheck;
	
	iRevocationHandler->SendRequestL(iValidPkixChains, aIap, iStatus);
	
	CleanupStack::Pop(&aCertOut);
	SetActive();
	}

EXPORT_C void CSecurityManager::VerifyControllerL(
				TDesC8& aRawController,
				const Sis::CController& aController, 
				TSignatureValidationResult* aResultOut, 
				RPointerArray<CPKIXValidationResultBase>& aPkixResultsOut,
				RPointerArray<CX509Certificate>& aCertsOut,
				TCapabilitySet* aCapabilitySetOut,
				TBool& aAllowUnsigned,
				TBool& aIsEmbeddedController,
				TRequestStatus& aStatus,
				TBool aCheckDateAndTime)
	{
	DEBUG_PRINTF(_L8("Security Manager - Validating Controller"));
	
	iResult = aResultOut;
	iChains = aController.SignatureCertificateChains();	
	iController = &aController;
	iEndCertificatesOut = &aCertsOut;
	iValidationResultsOut = &aPkixResultsOut;
	iTotalCapabilitiesOut = aCapabilitySetOut;
	iIsEmbeddedController = aIsEmbeddedController;
	iCheckDateAndTime = aCheckDateAndTime;
	
	// Initialy assume that the chain is not validated
	iHasValidated = EFalse;
	iHasValidatedTrusted = EFalse;
	
	*iResult = ESignatureNotPresent;
	aAllowUnsigned=iSecPolicy->AllowUnsigned();
	
	
	iClientStatus = &aStatus;  // Store caller's request status
	*iClientStatus = KRequestPending;
		
	if (iChains.Count() == 0)
		{
		User::RequestComplete(iClientStatus, KErrNone);	
		return;	
		}
	
	iRawController.Set(aRawController);
	iCurrentChain = 0;
	
	// First retrieve all CA certificates

	delete iCertificateRetriever;
	iCertificateRetriever = NULL;
	iCertificateRetriever = CCertificateRetriever::NewL(*iCertStore);
	
	delete iChainValidator;
	iChainValidator = NULL;
	iChainValidator = CChainValidator::NewL(*iCertStore, *iSecPolicy);
	
	iState = ERetrievedTrustedRoots;
	SetActive();

	// No need to fetch certificates from the certstore if the root certs
	// are already provided by the caller. or
	// The root Certificates already fetched from certstore in case of embedded packages

	if(NULL == iRootCerts && iTrustedRoots.Count() == 0)
		{
		iCertificateRetriever->RetrieveCACertificates(
			iTrustedRoots, iStatus);
		}
	else
		{
		TRequestStatus* tempStatus = &iStatus;
		User::RequestComplete(tempStatus, KErrNone);
		}
	}
	
EXPORT_C void CSecurityManager::ReverifyControllerL(
				TDesC8& aRawController,
				const Sis::CController& aController, 
				const RArray<TInt>& aChainListIndices,
				TSignatureValidationResult* aResultOut, 
				RPointerArray<CPKIXValidationResultBase>& aPkixResultsOut,
				RPointerArray<CX509Certificate>& aCertsOut,
				TCapabilitySet* aCapabilitySetOut,
				TBool& aAllowUnsigned,
				TRequestStatus& aStatus)
	{
	DEBUG_PRINTF(_L8("Security Manager - Re-Verifying Controller"));
	
	iResult = aResultOut;
	iChains = aController.SignatureCertificateChains();	
	iController = &aController;
	iChainListIndices = &aChainListIndices;
	iEndCertificatesOut = &aCertsOut;
	iValidationResultsOut = &aPkixResultsOut;
	iTotalCapabilitiesOut = aCapabilitySetOut;
	iIsEmbeddedController = ETrue;
	

	*iResult = ESignatureNotPresent;
	aAllowUnsigned = iSecPolicy->AllowUnsigned();
	
	iClientStatus = &aStatus;  // Store caller's request status
	*iClientStatus = KRequestPending;
		
	if (iChains.Count() == 0)
		{
		// This is an unsigned SISX file
		User::RequestComplete(iClientStatus, KErrNone);	
		return;	
		}
	
	iRawController.Set(aRawController);
    iCurrentChain = 0;
    
    delete iCertificateRetriever;
    iCertificateRetriever = NULL;
    iCertificateRetriever = CCertificateRetriever::NewL(*iCertStore);
    
    delete iChainValidator;
    iChainValidator = NULL;
	iChainValidator = CChainValidator::NewL(*iCertStore, *iSecPolicy);

	// We may have an unsigned sis file in which case we do not want to verify the block
	// since there isn't one, so bail here with the correct error
	if (iChains.Count() == 0)
		{
		User::RequestComplete(iClientStatus, KErrNone);							
		}
	else 
		{
		TInt currentChain = (*iChainListIndices)[iCurrentChain];
		VerifyBlockL(currentChain);		
		}	
	}
		
TInt CSecurityManager::SignedSize(TInt iChainsToAdd) const
	{
	TInt64 size = 0;

	const Sis::CInfo& info = iController->Info();	
	size += info.Length() + info.HeaderSize() + info.PaddingSize();

	const Sis::CSupportedOptions& options = iController->SupportedOptions();
	size += options.Length() + options.HeaderSize() + options.PaddingSize();

	const Sis::CSupportedLanguages& languages = iController->SupportedLanguages();
	size += languages.Length() + languages.HeaderSize() + languages.PaddingSize();

	const Sis::CPrerequisites& prerequisites = iController->Prerequisites();
	size += prerequisites.Length() + prerequisites.HeaderSize() + prerequisites.PaddingSize();

	const Sis::CProperties& props = iController->Properties();
	size += props.Length() + props.HeaderSize() + props.PaddingSize();

	const Sis::CLogo* logo = iController->Logo();
	if (logo != NULL)
		{
		size += logo->Length() + logo->HeaderSize() + logo->PaddingSize();		
		}
		
	const Sis::CInstallBlock& installblock = iController->InstallBlock();
	size += installblock.Length() + installblock.HeaderSize() + installblock.PaddingSize();

	for (TInt k = 0; k < iChainsToAdd; k++)
		{
		size += iChains[k]->Length() + iChains[k]->HeaderSize() + iChains[k]->PaddingSize();
		}

	return I64INT(size);	
	}

void CSecurityManager::VerifyBlockL(TInt aChainIndex)
	{		
	DEBUG_PRINTF2(_L8("Security Manager - Validating certificate chain %d."), aChainIndex);

	Sis::CSignatureCertificateChain& chain = *iChains[aChainIndex];	
	
	// First verify the signature
	const RPointerArray<Sis::CSignature> signatures = chain.Signatures();
	
	// Determine the size of the controller data which was signed
	TInt size = SignedSize(aChainIndex);

	const Sis::CCertificateChain& certChain = chain.CertificateChain();
	const TPtrC8 data = certChain.Data();
	
	TInt pos = 0;
	TInt end = data.Length();
	iUntrustedRoots.ResetAndDestroy();
	
	// Find all the self-signed certificates in the chain as possible root candidates
	
	while (pos < end)
		{
		CX509Certificate* decoded = CX509Certificate::NewLC(data, pos);
		if (decoded->IsSelfSignedL())
			{
			// If the root isn't in the trusted list, append it to the untrusted roots
			TBool found = EFalse;
			if(NULL == iRootCerts)
				{
				for (TInt i = 0; i < iTrustedRoots.Count(); i++)
					{
					CCTCertInfo* root = iTrustedRoots[i];
					if (root->SubjectKeyId() == decoded->KeyIdentifierL()) 
						{
						found = ETrue;
						break;
						}
					}
				}
			else
				{
				for (TInt i = iRootCerts->Count() - 1; i >= 0; --i)
					{
					CX509Certificate* root = (*iRootCerts)[i];
					if (root->KeyIdentifierL() == decoded->KeyIdentifierL()) 
						{
						found = ETrue;
						break;
						}
					}
				}
			
			if (!found)
				{
				iUntrustedRoots.AppendL(decoded);
				CleanupStack::Pop(decoded);
				}
			else
				{
				CleanupStack::PopAndDestroy(decoded);	
				}
			}
		else
			{
			CleanupStack::PopAndDestroy(decoded);
			}	
		}
	
	DEBUG_PRINTF2(_L8("Security Manager - Found %d non-trusted candidate root certificates"), iUntrustedRoots.Count());
		
	// If we have self signed roots, don't look in the certstore for a further root
	
	TBool hasUntrustedRoot = (iUntrustedRoots.Count() > 0);
	
	if (NULL != iRootCerts)
		{
		DEBUG_PRINTF(_L8("Security Manager - Validating chain using user provided roots"));
		iCurrentPkixChain = CPKIXCertChainBase::NewL(*iCertStore, data, *iRootCerts);
		hasUntrustedRoot = ETrue; // These root certs are considered untrusted.
		}
	else if(hasUntrustedRoot)
		{
		DEBUG_PRINTF(_L8("Security Manager - Validating chain using untrusted roots"));
		iCurrentPkixChain = CPKIXCertChainBase::NewL(*iCertStore, data, iUntrustedRoots);
		}
	else
		{
		DEBUG_PRINTF(_L8("Security Manager - Validating chain using trusted roots from store"));
		iCurrentPkixChain = CPKIXCertChainBase::NewL(*iCertStore, data, KSwiApplicabilityUid);	
		}
	

	DEBUG_PRINTF2(_L8("Security Manager - Certificate chain contains %d certificates."), iCurrentPkixChain->Count());

	const CX509Certificate& clientCert = iCurrentPkixChain->Cert(0); // This is the ee certificate
	CX509Certificate* endCertOut = CX509Certificate::NewLC(clientCert);
	iEndCertificatesOut->AppendL(endCertOut);
	CleanupStack::Pop(endCertOut);
	
	DEBUG_CODE_SECTION(HBufC* issuer = clientCert.IssuerL(); DEBUG_PRINTF2(_L("Security Manager - End Entity Certificate Issuer: '%S'."), issuer); delete issuer;);
	DEBUG_CODE_SECTION(HBufC* subject = clientCert.SubjectL(); DEBUG_PRINTF2(_L("Security Manager - End Entity Certificate Subject: '%S'."), subject); delete subject;);
	
	const CSubjectPublicKeyInfo& publicKey=  clientCert.PublicKey();
	
	DEBUG_PRINTF2(_L8("Security Manager - SIS file signed %d times with this certificate."), signatures.Count());
	
	CSignatureVerifier* verifier = CSignatureVerifier::NewLC();
	TBool result = EFalse;
	for (TInt k = 0; k < signatures.Count(); k++)
		{
		const TDesC& algorithmOID = signatures[k]->Algorithm().AlgorithmIdentifier().Data();
		TRAP_IGNORE(result = verifier->VerifySignatureL(algorithmOID, publicKey, iRawController.Mid((iIsEmbeddedController ? 4 : iController->HeaderSize()), size), signatures[k]->Data()));
		if (result) 
 			{
			// We have a verify any policy on signatures (inside a given SISSignatureCertificateChain)
			// Cf. SGL.GT0188.251 Section 4.3
			break;   
			}
		}

	CleanupStack::PopAndDestroy(verifier);

	if (!result) // Signature not verified: something is WRONG. Abort.
		{
		DEBUG_PRINTF(_L8("Security Manager - No signature validated."));
		
		*iResult = ESignatureCouldNotBeValidated;
		User::RequestComplete(iClientStatus, KErrNone);	
		return;			
		}
			
	// The signature is good, validate the certificate chain
	
	CPKIXValidationResultBase* validationResult = CPKIXValidationResultBase::NewLC();
	iValidationResultsOut->Append(validationResult);
	CleanupStack::Pop(validationResult);
	
	iState = EValidatingChain;
	iCurrentPkixChain->SetValidityPeriodCheckFatal(iCheckDateAndTime);
	iChainValidator->ValidateChainL(*iCurrentPkixChain, *iResult, *validationResult, 
									iCurrentCapabilities, hasUntrustedRoot, iStatus);
	SetActive();	
	}

EXPORT_C HBufC8* CSecurityManager::CalculateHashLC(MSisDataProvider& aDataProvider, CMessageDigest::THashId aAlgorithm)
	{
	CMessageDigest* digest = CMessageDigestFactory::NewDigestLC(aAlgorithm);
		
	HBufC8* aBuffer = HBufC8::NewMaxLC(KFileBufferSize); 
	TPtr8 aBufferPtr(aBuffer->Des());

	User::LeaveIfError(aDataProvider.Read(aBufferPtr));

	while (aBuffer->Length() != 0)
		{
		digest->Update(*aBuffer);
		User::LeaveIfError(aDataProvider.Read(aBufferPtr));		
		}
	TPtrC8 hash = digest->Final();
	HBufC8* hashBuffer = hash.AllocL();
	CleanupStack::PopAndDestroy(2, digest); // aBuffer, digest
	
	CleanupStack::PushL(hashBuffer);
	return hashBuffer;
	}

EXPORT_C TBool CSecurityManager::VerifyFileHashL(MSisDataProvider& aDataProvider, const CHashContainer& aDigest)
	{
	HBufC8* hashBuffer = CalculateHashLC(aDataProvider, aDigest.Algorithm());

	TBool result = EFalse;

	TPtrC8 hash = hashBuffer->Des();
	if (hash.Compare(aDigest.Data()) == 0)
		{
		result = ETrue;
		}

	CleanupStack::PopAndDestroy(hashBuffer);
	return result;
	}
	
void CSecurityManager::UpdateListOfMissingRequiredCertsL(const CCertificate& aCertificate) 
	{
	// Find aCertificate in iTrustedRoots and marked as nonmandatory for temporary!
	TInt count = iTrustedRoots.Count();
	for (TInt k = 0; k < count; k++)
		{
		CCTCertInfo* certInfo = iTrustedRoots[k];
		if (iCertMetaInfo[k].iIsMandatory && !certInfo->SubjectKeyId().Compare(aCertificate.KeyIdentifierL()))
			{
			DEBUG_PRINTF2(_L("Security Manager - Mandatory certificate '%S' satisfied."), &(certInfo->Label()));
			// set the Mandatory is false so that next time this certificate won't be compared
			iCertMetaInfo[k].iIsMandatory = 0;
			iMandatoryCertDNCount--;
			break;
			}
		}
	}

void CSecurityManager::UpdateSystemUpgradeCertStatusL(const CCertificate& aCertificate)
	{	
	// Find aCertificate in iTrustedRoots and update the system upgrade trust status
	TInt count = iTrustedRoots.Count();
	for (TInt k = 0; k < count; k++)
		{
		CCTCertInfo* certInfo = iTrustedRoots[k];
		if (iCertMetaInfo[k].iIsSystemUpgrade && certInfo->SubjectKeyId() == aCertificate.KeyIdentifierL())
			{
			DEBUG_PRINTF2(_L("Security Manager - System upgrade certificate '%S' satisfied."), &(certInfo->Label()));
			// create a modifyable reference to the current controller
			Sis::CController& controller = const_cast <Sis::CController&>(*iController);
			// set the SuCert validation status
			controller.SetSignedBySuCert(ETrue);
			break;
			}
		}
	
	}

EXPORT_C void CSecurityManager::GetCertificatesFromControllerL(
	const Sis::CController& aController,
	RPointerArray<CX509Certificate>& aCerts)
	{
	CleanupResetAndDestroyPushL(aCerts);
	// Go through all SIS chains and extract end certificates from them.
	const RPointerArray<Sis::CSignatureCertificateChain>& chains=
		aController.SignatureCertificateChains();
	for (TInt i=0; i<chains.Count(); i++)
		{
		Sis::CSignatureCertificateChain& sigCertChain=*chains[i];
		const Sis::CCertificateChain& certChain=
			sigCertChain.CertificateChain();
		// Construct PKIX cert chain from raw data in the controller.
		CPKIXCertChainBase* pkixChain=CPKIXCertChainBase::NewLC(*iCertStore,
			certChain.Data(), KSwiApplicabilityUid);
		
		// Extract end entity certificate and store it in the member array.
		const CX509Certificate& endCert=pkixChain->Cert(0);
		CX509Certificate* endCertCopy=CX509Certificate::NewLC(endCert);
		User::LeaveIfError(aCerts.Append(endCertCopy));
		CleanupStack::Pop(endCertCopy);
		
		// Cleanup.
		CleanupStack::PopAndDestroy(pkixChain);
		}
	CleanupStack::Pop(&aCerts);
	}

EXPORT_C void CSecurityManager::FillCertInfoArrayL(
	const RPointerArray<CX509Certificate>& aCertificates,
	RPointerArray<CCertificateInfo>& aCertInfos)
	{
	for (TInt i=0; i<aCertificates.Count(); i++)
		{
		CCertificateInfo* certInfo=CCertificateInfo::NewLC(*aCertificates[i]);
		aCertInfos.AppendL(certInfo);
		CleanupStack::Pop(certInfo);
		}
	}

EXPORT_C const RPointerArray<HBufC>& CSecurityManager::DeviceIDsInfo() const
 	{
 	return iDeviceIDs;
 	}

EXPORT_C void CSecurityManager::ResetValidCertChains() 
	{
	iValidPkixChains.ResetAndDestroy();
	}
	
EXPORT_C void CSecurityManager::SetDevCertWarningState(TInt aDevCertWarningState)
	{
	iDevCertWarningState=static_cast<TDevCertWarningState>(aDevCertWarningState);	
	}

EXPORT_C TInt CSecurityManager::GetDevCertWarningState()
	{
	return iDevCertWarningState;
	}


EXPORT_C void CSecurityManager::SetRootCerts(RPointerArray<CX509Certificate>* aX509CertArray)
	{
	iRootCerts = aX509CertArray;
	}

	
	



