cryptoservices/certificateandkeymgmt/pkixcertbase/pkixcertchainao.cpp
changeset 0 2c201484c85f
child 8 35751d3474b7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cryptoservices/certificateandkeymgmt/pkixcertbase/pkixcertchainao.cpp	Wed Jul 08 11:25:26 2009 +0100
@@ -0,0 +1,613 @@
+/*
+* Copyright (c) 1998-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: 
+*
+*/
+
+
+#include "pkixcertchainao.h"
+#include "pkixCons.h"
+#include <x509keys.h>
+#include <pkixcertchain.h>
+#include <x509constraintext.h>
+
+CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore,
+										 CPKIXCertChainBase &aPKIXCertChain,
+										 const RPointerArray<CX509Certificate>& aRootCerts)
+	{
+	CPKIXCertChainAO* self = new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain);
+	CleanupStack::PushL(self);
+	self->ConstructL(aRootCerts);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore, 
+										 CPKIXCertChainBase &aPKIXCertChain,
+										 const TUid aClient)
+	{
+	return new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain, aClient);
+	}
+
+CPKIXCertChainAO::~CPKIXCertChainAO()
+	{
+	Cancel();
+
+	delete iRoots;
+	delete iBuilder;
+	delete iCertsFromStoreRoots;
+	}
+
+CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore,
+								   CPKIXCertChainBase &aPKIXCertChain)
+	: CActive(EPriorityNormal), iCertStore(&aCertStore), iPKIXCertChain(aPKIXCertChain)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore, 
+								   CPKIXCertChainBase &aPKIXCertChain,
+								   const TUid aClient)
+	: CActive(EPriorityNormal), iCertStore(&aCertStore),
+	  iPKIXCertChain(aPKIXCertChain), iClient(aClient)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CPKIXCertChainAO::ConstructL(const RPointerArray<CX509Certificate>& aRootCerts)
+	{
+	CPKIXCertsFromClient* roots = CPKIXCertsFromClient::NewLC(aRootCerts);
+	iRoots = CPKIXChainBuilder::NewL();
+	iRoots->AddSourceL(roots);
+	CleanupStack::Pop(roots);
+	}
+
+void CPKIXCertChainAO::RunL()
+	{
+	User::LeaveIfError(iStatus.Int());
+
+	switch (iState)
+		{
+		case EAddRoots:
+			HandleEAddRootsL();
+			break;
+
+		case ERootsInitialized:
+			HandleERootsInitializedL();
+			break;
+
+		case EBuildChainStart:
+			HandleEBuildChainStartL();
+			break;
+
+		case EBuildChainAddCandidateEnd:
+			HandleEBuildChainAddCandidateEndL();
+			break;
+
+		case EBuildChainCertsFromStoreBegin:
+			HandleEBuildChainCertsFromStoreBeginL();
+			break;
+
+		case EBuildChainCertsFromStoreEnd:
+			HandleEBuildChainCertsFromStoreEndL();
+			break;
+
+		case EAddCandidateIntermediateCertsEnd:
+			HandleEAddCandidateIntermediateCertsEndL();
+			break;
+
+		case EValidateEnd:
+			HandleEValidateEndL();
+			break;
+
+		default:
+			User::Panic(_L("CPKIXCertChainAO"), 1);
+			break;
+		}
+	}
+
+TInt CPKIXCertChainAO::RunError(TInt aError)
+{
+	iPKIXCertChain.RemoveLastCerts(iNumberOfAddedCertificates);
+	iNumberOfAddedCertificates = 0;
+
+	delete iRoots;
+	iRoots = 0;
+		
+	delete iBuilder;
+	iBuilder = 0;
+	delete iCertsFromStoreRoots;
+	iCertsFromStoreRoots = 0;
+
+	iValidationResult->RemovePolicies();
+
+	User::RequestComplete(iOriginalRequestStatus, aError);
+	return KErrNone;
+}
+
+/**
+ * Creates a list of all the certificates retrieved from the store based on the filter passed.  
+ */
+
+void CPKIXCertChainAO::HandleEAddRootsL()
+	{
+	__ASSERT_DEBUG(!iCertsFromStoreRoots, User::Panic(_L("CPKICCertChainAO"), 1));
+	iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore, iClient);
+	iCertsFromStoreRoots->Initialize(iStatus);
+	iState = ERootsInitialized;	
+	SetActive();
+	}
+
+/**
+ * Adds the list of certificates retrieved from the store, iRoots (CPKIXChainBuilder)
+ * maintains a templatized list of all the certificates in MPKIXCertSource format.
+ */
+
+void CPKIXCertChainAO::HandleERootsInitializedL()
+	{
+	iRoots->AddSourceL(iCertsFromStoreRoots);
+	// Ownership has been passed to iRoots
+	iCertsFromStoreRoots = 0;
+	iState = EBuildChainStart;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEBuildChainStartL()
+	{
+	if ( false == iPKIXCertChain.ChainHasRoot())
+		{
+		if (iPKIXCertChain.Chain().Count() == 0)
+			{
+			iState = EValidateEnd;
+			TRequestStatus* status = &iStatus;
+			User::RequestComplete(status, KErrNone);
+			}
+		else
+			{
+			//1) look for an issuer that's a root
+			iRoots->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus);
+			iState = EBuildChainAddCandidateEnd;
+			}
+		}
+	else
+		{
+		// This is the correct state as at this point the chain of certificate has been build upto a
+		// root certificate.
+		iState = EValidateEnd;
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		}
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEBuildChainAddCandidateEndL()
+	{
+	if (iAddIssuerResult)
+		{
+		iPKIXCertChain.SetChainHasRoot(ETrue);
+		iState = EValidateEnd;
+		}
+	else
+		{
+		//2) look for a non-root issuer in intermediate certs
+		iBuilder = CPKIXChainBuilder::NewL();
+		
+		CPKIXCertsFromClient* serverCerts = CPKIXCertsFromClient::NewLC(iPKIXCertChain.IntermediateCerts());
+		iBuilder->AddSourceL(serverCerts);
+		CleanupStack::Pop(serverCerts);
+
+		iState = EBuildChainCertsFromStoreBegin;
+		}
+
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreBeginL()
+	{
+	//3) look for a non-root issuer in the store
+	iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore);
+	iCertsFromStoreRoots->Initialize(iStatus);
+	iState = EBuildChainCertsFromStoreEnd;
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreEndL()
+	{
+	iBuilder->AddSourceL(iCertsFromStoreRoots);
+	iCertsFromStoreRoots = 0;
+
+	iBuilder->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus);
+	iState = EAddCandidateIntermediateCertsEnd;
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEAddCandidateIntermediateCertsEndL()
+	{
+	if (iAddIssuerResult)
+		{
+		// cert is a pointer to something we don't own
+		CX509Certificate* cert = iPKIXCertChain.Chain().At(iPKIXCertChain.Chain().Count() - 1);
+		
+		/* If the issuer is not a self signed certificate then it cannot be trusted anchor for the chain 
+		 * validation process, this means that we restart the certification validation process.
+		 */ 
+		
+		if (!(cert->IsSelfSignedL()))
+			{
+			iState = EBuildChainStart;	
+			}
+		else
+			{
+			iState = EValidateEnd;			
+			}
+		}
+	else
+		{
+		iState = EValidateEnd;	
+		}
+
+	delete iBuilder;
+	iBuilder = 0;
+
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CPKIXCertChainAO::HandleEValidateEndL()
+	{
+	InitParamsL();
+	
+	__ASSERT_DEBUG(iValidationResult, User::Panic(_L("CPKICCertChainAO"), 1));
+	DoValidateL(*iValidationResult, iValidationTime, iInitialPolicies);
+
+	User::RequestComplete(iOriginalRequestStatus, KErrNone);
+	}
+
+void CPKIXCertChainAO::DoCancel()
+	{
+	delete iRoots;
+	iRoots = 0;
+
+	delete iBuilder;
+	iBuilder = 0;
+
+	delete iCertsFromStoreRoots;
+	iCertsFromStoreRoots = 0;
+
+	User::RequestComplete(iOriginalRequestStatus, KErrCancel);
+	}
+
+void CPKIXCertChainAO::ValidateL(CPKIXValidationResultBase& aValidationResult,
+								 const TTime& aValidationTime,								 
+								 const CArrayPtr<HBufC>* aInitialPolicies,
+								 TRequestStatus& aStatus)
+	{
+	aValidationResult.Reset();
+	iValidationResult = &aValidationResult;
+	iValidationTime = aValidationTime;
+	iInitialPolicies = aInitialPolicies;
+	iOriginalRequestStatus = &aStatus;
+	iNumberOfAddedCertificates = 0;
+
+	__ASSERT_ALWAYS(!IsActive(), User::Panic(_L("CPKICCertChainAO"), 1));
+
+	if (!iRoots)
+		{
+		// If iRoots is 0, it means that the caller gave a uid and that
+		// we must retrieve the trusted certificates from the different
+		// stores
+		iRoots = CPKIXChainBuilder::NewL();
+		iState = EAddRoots;
+		}
+	else
+		{
+		// The caller gave a set of certificates it trusts,
+		// so we don't have to retrieve anything from the stores
+		iState = EBuildChainStart;
+		}
+
+	aStatus = KRequestPending;
+	TRequestStatus *status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CPKIXCertChainAO::CancelValidate()
+	{
+	Cancel();
+	}
+
+void CPKIXCertChainAO::InitParamsL()
+/*
+this function initialises signing key parameters for the certificates
+-only DSA needs these at present
+-we get the signing key, from the spki of the issuer
+-if it's dsa, we look for params here
+	-if we find them we initialise the cert with them
+	-otherwise, we look in the issuer's issuer
+		-if we don't find them there we give up.
+*/
+	{
+	
+	// If the root is DSA signed, set its parameters
+
+	TInt count = iPKIXCertChain.Chain().Count();
+	
+	CX509Certificate* current = iPKIXCertChain.Chain().At(count-1);
+	TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm();
+	
+	if (signingAlgorithm == EDSA)
+		{
+		
+		const CSubjectPublicKeyInfo& key = current->PublicKey();
+		SetParamsL(*current, key.EncodedParams());
+		
+		}
+	
+	// Also the rest of the chain
+	
+	for (TInt i = count - 2; i >= 0; i--)
+		{
+		
+		current = iPKIXCertChain.Chain().At(i);
+		TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm();
+		
+		if (signingAlgorithm == EDSA)
+			{
+			
+			// Look down the chain for parameters
+			
+			for (TInt j = i+1; j < count; j++)
+				{
+				
+				CX509Certificate* issuer = iPKIXCertChain.Chain().At(j);
+				const CSubjectPublicKeyInfo& key = issuer->PublicKey();
+				if (key.EncodedParams() != KNullDesC8 && key.AlgorithmId() == EDSA)
+					{
+					SetParamsL(*current, key.EncodedParams());
+					break;
+					}
+				
+				}
+			
+			}
+		
+		}
+	}
+
+void CPKIXCertChainAO::SetParamsL(CX509Certificate& aCert, const TPtrC8& aEncodedParams)
+	{
+	TX509KeyFactory factory;
+	CDSAParameters* theDSAParams = factory.DSAParametersL(aEncodedParams);
+	CleanupStack::PushL(theDSAParams);
+	
+	CSigningKeyParameters* params = CSigningKeyParameters::NewLC();
+	params->SetDSAParamsL(*theDSAParams);
+	
+	aCert.SetParametersL(*params);
+
+	CleanupStack::PopAndDestroy(2, theDSAParams);
+	}
+
+void CPKIXCertChainAO::DoValidateL(CPKIXValidationResultBase& aValidationResult,
+								 const TTime& aValidationTime, 
+								 const CArrayPtr<HBufC>* aInitialPolicies)
+	{
+	if (!iPKIXCertChain.ChainHasRoot())
+		{
+		aValidationResult.SetError(EChainHasNoRoot, 0);
+		}
+	else
+		{
+		CPKIXValidationState* state = CPKIXValidationState::NewLC(aValidationTime, iPKIXCertChain.Chain().Count(), aInitialPolicies);
+		TRAPD(err, ProcessCertsL(*state, aValidationResult));
+		//a leave here means either:
+		//	-a validation error, in which case we've set the error field in result, or
+		//	-some other error (e.g. OOM) in which case error is still EChainHasNoRoot
+		if ((err != KErrNone) && ((aValidationResult.Error().iReason) == EChainHasNoRoot))
+			//then we left with a non-validation-related error, so leave again...
+			{
+			User::Leave(err);
+			}
+		CleanupStack::PopAndDestroy(state);
+		}
+	}
+
+// ProcessCertsL: This function validates a complete certificate 
+//                chain. If an error occurs in this function the function
+//                SetErrorAndLeaveL must be called. 
+// 
+// Note Do not use SetErrorAndLeaveL with EChainHasNoRoot (see TRAP code in 
+// CPKIXCertChainAO::DoValidateL )             
+void CPKIXCertChainAO::ProcessCertsL(CPKIXValidationState& aState,
+									 CPKIXValidationResultBase& aResult) const
+	{
+	TPKIXPolicyConstraint policy(aState, aResult);
+	TPKIXNameConstraint name(aState, aResult);
+	TPKIXBasicConstraint basic(aState, aResult);
+	TPKIXKeyUsageConstraint keyUsage(aState, aResult);
+	for (; aState.iPos >= 0; aState.iPos--)
+		{
+		aState.iMaxPathLength--;
+		if (aState.iMaxPathLength < aState.iPos)
+			{
+			aResult.SetErrorAndLeaveL(EPathTooLong, aState.iPos);
+			}
+		const CX509Certificate* current = iPKIXCertChain.Chain().At(aState.iPos);
+		CCertificateValidationWarnings* certWarnings = CCertificateValidationWarnings::NewLC(aState.iPos);
+		aResult.AppendCertificateValidationObjectL(*certWarnings);
+		CleanupStack::Pop(certWarnings);
+		CriticalExtsL(aState, *current);
+		CheckCriticalExtsL(aState, aResult);
+		CheckSignatureAndNameL(*current, aState, aResult);
+		//!!!!NO!!checks for revocation at this time!!
+		
+		if (!(current->ValidityPeriod().Valid(aState.iValidationTime)))
+			{
+			//validity period invalid, now check how to report this
+			if (iPKIXCertChain.ValidityPeriodCheckFatal())
+				{
+				aResult.SetErrorAndLeaveL(EDateOutOfRange, aState.iPos);
+				}
+			else
+				{
+				aResult.AppendWarningL(TValidationStatus(EDateOutOfRange, aState.iPos));
+				}
+			}
+		
+		policy.CheckCertPoliciesL(*current);
+		name.CheckNameConstraintsL(*current);
+		keyUsage.CheckKeyUsageL(*current);
+		if (aState.iPos < (iPKIXCertChain.Chain().Count() - 1))
+			{
+			basic.CheckCertSubjectTypeL(*current);
+			}
+		basic.UpdatePathLengthConstraintsL(*current);
+		name.UpdateNameConstraintsL(*current);
+		policy.UpdatePolicyConstraintsL(*current);
+		aState.iCriticalExts->Reset();
+		}
+	policy.FinishPolicyCheckL();
+	//*copy* all policies from aState.iAuthorityConstrainedPolicies into aResult.iPolicies
+	TInt policyCount = aState.iAuthorityConstrainedPolicies->Count();
+	for (TInt i = 0; i < policyCount; i ++)
+		{
+		CX509CertPolicyInfo* policyInfo = CX509CertPolicyInfo::NewLC(*(aState.iAuthorityConstrainedPolicies->At(i)));
+		aResult.AppendPolicyL(*policyInfo);
+		CleanupStack::Pop(policyInfo);
+		}
+
+	aResult.SetError(EValidatedOK, 0);
+	}
+
+void CPKIXCertChainAO::CriticalExtsL(CPKIXValidationState& aState, 
+									 const CX509Certificate& aCert) const
+	{
+	const CArrayPtrFlat<CX509CertExtension>& exts = aCert.Extensions();
+	TInt count = exts.Count();
+	for (TInt i = 0; i < count; i++)
+		{
+		CX509CertExtension* ext = exts.At(i);
+		if (ext->Critical())
+			{
+			aState.iCriticalExts->AppendL(ext);
+			}
+		}	 
+	}
+
+void CPKIXCertChainAO::CheckSignatureAndNameL(const CX509Certificate& aCert, CPKIXValidationState& aState, 
+											CPKIXValidationResultBase& aResult) const
+	{
+	TInt issuerPos = aState.iPos + 1;
+	if (issuerPos == iPKIXCertChain.Chain().Count())
+		//then it's the root
+		{
+		if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
+			//then it claims to be self signed, sig must verify
+			{
+			if (!(aCert.VerifySignatureL(aCert.PublicKey().KeyData())))
+				{
+				aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos);
+				}
+			}
+		else
+			//we generate a warning
+			{
+			aResult.AppendWarningL(TValidationStatus(ERootCertNotSelfSigned, aState.iPos));
+			}
+		}
+	else
+		//then it isn't the root: so names must chain & sigs must verify
+		{
+		const CX509Certificate* issuer = iPKIXCertChain.Chain().At(issuerPos);
+		if (!(aCert.IssuerName().ExactMatchL(issuer->SubjectName())))
+			{
+			aResult.SetErrorAndLeaveL(ENamesDontChain, aState.iPos);
+			}
+		if (!(aCert.VerifySignatureL(issuer->PublicKey().KeyData())))
+			{
+			aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos);
+			}
+		}
+	}
+
+void CPKIXCertChainAO::CheckCriticalExtsL(CPKIXValidationState& aState, CPKIXValidationResultBase& aResult) const
+	{
+	TBool foundUnrecognisedCritExt;
+	
+	// retrieve the supported list of critical extensions. If a critical extension is found whose OID matches an 
+	// element in this set then certificate validation shall treat this as a warning instead of an error.
+	const RPointerArray<TDesC>& supportedCritExt = iPKIXCertChain.SupportedCriticalExtensions();
+	
+	TInt count = aState.iCriticalExts->Count();
+	TInt supportedCount = supportedCritExt.Count();
+	for (TInt i = 0; i < count; i++)
+		{
+		foundUnrecognisedCritExt = ETrue;
+		const CX509CertExtension* ext = aState.iCriticalExts->At(i);
+		const TPtrC& extName = ext->Id();
+		
+		for (TInt j = 0; j < supportedCount; ++j)
+			{
+			if (extName == *supportedCritExt[j])
+				{
+				foundUnrecognisedCritExt = EFalse;
+				HBufC* oid = extName.AllocLC();
+				aResult.AppendCriticalExtensionWarningL(*oid);
+				CleanupStack::Pop(oid);
+				break;
+				}
+			}			
+		
+		if (extName == KExtendedKeyUsage)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalExtendedKeyUsage, aState.iPos));
+			}
+		else if (extName == KPolicyMapping)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalPolicyMapping, aState.iPos));
+			}
+		else if (extName == KInhibitAnyPolicy)
+			{
+			//ignore this in the same way
+			}
+		else if (extName == KDeviceIdListConstraint)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalDeviceId, aState.iPos));
+			}
+		else if(extName == KSidListConstraint)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalSid, aState.iPos));
+			}
+		else if(extName == KVidListConstraint)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalVid, aState.iPos));
+			}
+		else if(extName == KCapabilitiesConstraint)
+			{
+			aResult.AppendWarningL(TValidationStatus(ECriticalCapabilities, aState.iPos));
+			}
+		
+		if (foundUnrecognisedCritExt)
+			{
+			aResult.SetErrorAndLeaveL(EUnrecognizedCriticalExtension, aState.iPos);
+			}			
+		
+		}
+	}