cryptoservices/certificateandkeymgmt/wtlscert/wtlscertchainao.cpp
changeset 0 2c201484c85f
child 8 35751d3474b7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cryptoservices/certificateandkeymgmt/wtlscert/wtlscertchainao.cpp	Wed Jul 08 11:25:26 2009 +0100
@@ -0,0 +1,534 @@
+/*
+* 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 "wtlscertchainao.h"
+#include <asymmetric.h>
+#include <bigint.h>
+#include <ccertattributefilter.h>
+#include <cctcertinfo.h>
+
+CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs, 
+										 CWTLSCertChain& aWTLSCertChain,
+										 const CArrayPtr<CWTLSCertificate>& aRootCerts)
+	{
+	CWTLSCertChainAO* self = new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain);
+	CleanupStack::PushL(self);
+	self->ConstructL(aRootCerts);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs, 
+										 CWTLSCertChain& aWTLSCertChain,
+										 const TUid aClient)
+	{
+	return new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain, aClient);
+	}
+
+CWTLSCertChainAO::~CWTLSCertChainAO()
+	{
+	Cancel();
+	delete iCertStoreManager;
+	delete iFilter;
+	delete iEncodedCertTemp;
+	iRootSubjectClientHashList.ResetAndDestroy();
+	iRootSubjectStoreHashList.Close();
+	iCertInfos.Close(); //In an RMPointerArray Close deletes all elements as 
+						//well as any personal allocated space
+	iRootsFromStore.ResetAndDestroy();
+	iRootsFromStore.Close();
+	iRootsFromClient.ResetAndDestroy();
+	}
+
+CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs, 
+								   CWTLSCertChain& aWTLSCertChain)
+	: CActive(EPriorityNormal), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iEncodedCert(NULL, 0)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs, 
+								   CWTLSCertChain& aWTLSCertChain,
+								   const TUid aClient)
+	: CActive(0), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iClient(aClient),
+		iEncodedCert(NULL, 0)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CWTLSCertChainAO::ConstructL(const CArrayPtr<CWTLSCertificate>& aRootCerts)
+	{
+	for(TInt i=0; i< aRootCerts.Count(); i++)
+		{
+		CWTLSCertificate* root = CWTLSCertificate::NewLC(*(aRootCerts[i]));
+		User::LeaveIfError( iRootsFromClient.Append(root) );
+		CleanupStack::Pop(); //root
+		}
+	}
+
+void CWTLSCertChainAO::RunL()
+	{
+	//If any of my active objects complete with errors then we don't
+	//want to proceed
+	User::LeaveIfError(iStatus.Int());
+
+	switch (iState)
+		{
+		case EStoreManagerInitialization:
+			HandleEStoreManagerInitializationL();
+			break;
+
+		case EStoreManagerInitialized:
+			HandleEStoreManagerInitializedL();
+			break;
+
+		case EGetCertHashes:
+			HandleEGetCertHashesL();
+			break;
+
+		case EPruneList:
+			HandleEPruneListL();
+			break;
+
+		case EPruneListDone:
+			HandleEPruneListDoneL();
+			break;
+
+		case ECheckTCA:
+			HandleECheckTCAL();
+			break;
+
+		case EIsChainSelfSigned:
+			HandleEIsChainSelfSignedL();
+			break;
+
+		case ERetrieveRoots:
+			HandleERetrieveRootsL();
+			break;
+
+		case EAddRootToList:
+			HandleEAddRootToListL();
+			break;
+
+		case EFindRoot:
+			HandleEFindRootL();
+			break;
+
+		case EValidateEnd:
+			HandleEValidateEndL();
+			break;
+		
+		default:
+			__ASSERT_DEBUG(EFalse, User::Panic(_L("CWTLSCertChainAO"), 1));
+			User::Leave(KErrArgument);
+			break;
+		}
+	}
+
+TInt CWTLSCertChainAO::RunError(TInt aError)
+	{
+	User::RequestComplete(iOriginalRequestStatus, aError);
+
+	delete iCertStoreManager;
+	iCertStoreManager = 0;
+
+	return 0;
+	}
+
+void CWTLSCertChainAO::DoCancel()
+	{
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrCancel);
+	if (iOriginalRequestStatus)
+		{
+		User::RequestComplete(iOriginalRequestStatus, KErrCancel);
+		}
+	}
+
+void CWTLSCertChainAO::Validate(CWTLSValidationResult& aValidationResult,
+								const TTime& aValidationTime,								 
+								TRequestStatus& aStatus)
+	{
+	iValidationResult = &aValidationResult;
+	iValidationResult->Reset();
+	iValidationTime = &aValidationTime;
+	iOriginalRequestStatus = &aStatus;
+	aStatus = KRequestPending;
+
+	__ASSERT_DEBUG(!IsActive(), User::Panic(_L("CWTLSCertChainAO"), 1));
+	__ASSERT_DEBUG(!iCertStoreManager, User::Panic(_L("CWTLSCertChainAO"), 1));
+	
+	iState = EStoreManagerInitialization;
+	TRequestStatus *status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+TBool CWTLSCertChainAO::CheckSignatureAndNameL(const CWTLSCertificate& aCert, 
+											   CWTLSValidationResult& aResult,
+											   TInt aPos) const
+	{
+	TInt issuerPos = aPos + 1;
+	TBool res = EFalse;
+	if (issuerPos == iWTLSCertChain.iChain->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()))
+				{
+				res = ETrue;
+				}
+			else 
+				{
+				aResult.SetError(ESignatureInvalid, aPos);
+				}
+			}
+		else
+			{
+			aResult.AppendWarningL(TWTLSValidationStatus(ERootCertNotSelfSigned, aPos));
+			res = ETrue; //if its a warning we continue the validation process with the 
+						//warning duly noted so we can check for further warn/errors
+			}
+		}
+	else
+		//then it isn't the root: so names must chain & sigs must verify
+		{
+		const CWTLSCertificate* issuer = iWTLSCertChain.iChain->At(issuerPos);
+		TBool subject = EFalse;
+		TBool signature = EFalse;
+		subject = aCert.IssuerName().ExactMatchL(issuer->SubjectName());
+		if( !subject ) 
+			{
+			aResult.SetError(ENamesDontChain, aPos);
+			return EFalse;
+			}
+		signature = aCert.VerifySignatureL(issuer->PublicKey().KeyData());
+		if( !signature )
+			{
+			aResult.SetError(ESignatureInvalid, aPos);
+			return EFalse;
+			}
+		res = subject && signature;
+		}
+	return res;
+	}
+
+TBool CWTLSCertChainAO::CheckValidityPeriod(const CWTLSCertificate& aCert,
+											CWTLSValidationResult& aResult, 
+											const TTime aTime,
+											TInt aPos) const
+	{
+	if (aCert.ValidityPeriod().Valid(aTime))
+		{
+		return ETrue;
+		}
+	aResult.SetError(EDateOutOfRange, aPos);
+	return EFalse;
+	}
+
+void CWTLSCertChainAO::HandleEStoreManagerInitializationL()
+	{
+	iFilter = CCertAttributeFilter::NewL();
+	iFilter->SetFormat(EWTLSCertificate);
+	iFilter->SetUid(iClient);
+	iFilter->SetOwnerType(ECACertificate);
+
+	iCertStoreManager = CUnifiedCertStore::NewL(iFs, EFalse);
+	iCertStoreManager->Initialize(iStatus);
+
+	iState = EStoreManagerInitialized;
+	SetActive();
+	}
+
+void CWTLSCertChainAO::HandleEStoreManagerInitializedL()
+	{
+	iCertStoreManager->List(iCertInfos, *iFilter, iStatus);
+
+	iState = EGetCertHashes;
+	SetActive();
+	}
+
+void CWTLSCertChainAO::HandleEGetCertHashesL()
+	{
+	for(TInt i=0; i<iRootsFromClient.Count(); i++)
+		{
+		HBufC8* hash = &GeneratePublicKeyHashL( *(iRootsFromClient[i]));
+		CleanupStack::PushL(hash);
+		User::LeaveIfError( iRootSubjectClientHashList.Append(hash) );
+		CleanupStack::Pop(); //hash
+		}
+	for(TInt j=0; j < iCertInfos.Count(); j++ )
+		{
+		User::LeaveIfError( iRootSubjectStoreHashList.Append( &((iCertInfos[j])->SubjectKeyId()) ) );
+		}
+
+	iPruned = EFalse;	
+	iPrunedChainLength = iWTLSCertChain.iChain->Count();
+	iIndex = -1;
+
+	iState = EPruneList;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+/* Walk through the canadiate list and compare the hash of the subjects with the previously
+ * computed subject hash of certs from the CertStore and certs supplied by the client
+ */
+void CWTLSCertChainAO::HandleEPruneListL()
+	{
+	iIndex++;
+	if(iIndex < iWTLSCertChain.iChain->Count() )
+		{
+		CWTLSCertificate* cert = iWTLSCertChain.iChain->At(iIndex);
+		HBufC8* hash = &GeneratePublicKeyHashL(*cert);
+		CleanupStack::PushL(hash);
+
+		for(TInt i=0; i < iRootSubjectClientHashList.Count(); i++)
+			{
+			if( (iRootSubjectClientHashList[i])->Compare(*hash) == 0 )
+				{
+				iPrunedChainLength = iIndex;
+				iPruned = ETrue;
+				break;
+				}
+			}
+		if(!iPruned)
+			{
+			for(TInt j=0; j<iRootSubjectStoreHashList.Count(); j++) 
+				{
+				if( (iRootSubjectStoreHashList[j])->Compare(*hash) == 0 )
+					{
+					iPrunedChainLength = iIndex;
+					iPruned = ETrue;
+					break;
+					}
+				}
+			}
+		CleanupStack::PopAndDestroy(hash);
+		if(iPruned)
+			{
+			iState = EPruneListDone;
+			}
+		else 
+			{
+			iState = EPruneList;
+			}
+		}
+	else
+		{
+		iState = EPruneListDone;
+		}
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CWTLSCertChainAO::HandleEPruneListDoneL()
+	{
+	if(iPruned) 
+		{
+		TInt count = iWTLSCertChain.iChain->Count();
+		for( TInt i=count - 1; i > iPrunedChainLength; i-- )
+			{
+			delete iWTLSCertChain.iChain->At(i);
+			iWTLSCertChain.iChain->Delete(i);
+			}
+		iWTLSCertChain.iChain->Compress();
+		}
+	iState = ECheckTCA;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+//checks to see if each certificate in a chain has the authority to sign other certificates
+void CWTLSCertChainAO::HandleECheckTCAL()
+	{
+	TBool validChain = ETrue;
+	for( TInt i = 1; i < iWTLSCertChain.iChain->Count(); i++ ) 
+		//all intermediate certs (ie not EE certs and not self signed) need
+		// to have a field T=ca indicating that they can sign other certs
+		{
+		if( (iWTLSCertChain.iChain)->At(i)->IsTCAL() == EFalse && 
+			(iWTLSCertChain.iChain)->At(i)->IsSelfSignedL() == EFalse ) 
+			{
+			iValidationResult->SetError(ENotCACert, i);
+			User::RequestComplete(iOriginalRequestStatus, KErrNone);
+			validChain = EFalse;
+			break;
+			}
+		}
+	if(validChain && iPruned) 
+		{
+		//if we've pruned the list and the chain we have is valid,
+		//then our chain already has a root that we trust.
+		//therefore there is no need to retrieve one :)
+		//therefore goto validation
+		iState = EValidateEnd;
+		TRequestStatus *status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		SetActive();
+		}
+	else if(validChain) // ie && !iPruned
+		{
+		//if we haven't pruned but chain is valid then we're back a square one.
+		iState = EIsChainSelfSigned;
+		TRequestStatus *status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		SetActive();
+		}
+	}
+
+void CWTLSCertChainAO::HandleEIsChainSelfSignedL()
+	{
+
+	TInt last = iWTLSCertChain.iChain->Count() - 1;
+	if( iWTLSCertChain.iChain->At(last)->IsSelfSignedL() )
+		{
+		//if chained is self signed, and no earlier cert in the sequence was trusted
+		//then this is going to fail validation
+		//This is just an optimisation to avoid retrieving all the roots from the store
+
+		iValidationResult->SetError(EChainHasNoRoot, last);
+		User::RequestComplete(iOriginalRequestStatus, KErrNone);
+		}
+	else 
+		{
+		//standard chain -> need to find the appropriate trusted root for chain if it exists
+		iState = ERetrieveRoots;
+		iIndex = -1;
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		SetActive();
+		}
+	}
+
+void CWTLSCertChainAO::HandleERetrieveRootsL()
+	{
+	iIndex++;	
+	if(iIndex < iCertInfos.Count() )
+		{
+		if( iEncodedCertTemp != NULL ) 
+			{
+			delete iEncodedCertTemp;
+			}
+		iEncodedCertTemp = HBufC8::NewMaxL( (iCertInfos[iIndex])->Size() );
+		iEncodedCert.Set( iEncodedCertTemp->Des() );
+		iCertStoreManager->Retrieve( *(iCertInfos[iIndex]), iEncodedCert, iStatus );
+		iState = EAddRootToList;
+		}
+	else 
+		{
+		iState = EFindRoot;
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		}	
+	SetActive();
+	}
+
+void CWTLSCertChainAO::HandleEAddRootToListL()
+	{
+	//are we guarenteed that a cert from the store is a valid WTLScert?
+	//ie is this going to leave for reasons other than OOM?
+	CWTLSCertificate *cert = CWTLSCertificate::NewL( iEncodedCert );
+	User::LeaveIfError( iRootsFromStore.Append(cert) );
+
+	iState = ERetrieveRoots;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();
+	}
+
+void CWTLSCertChainAO::HandleEFindRootL()
+	{
+	TInt last = iWTLSCertChain.iChain->Count() - 1;
+	const CWTLSName* issuerName = &(iWTLSCertChain.iChain->At(last)->IssuerName());
+
+	iFoundRoot = EFalse;
+	for(TInt i=0; i<iRootsFromClient.Count(); i++) 
+		{
+		if( issuerName->ExactMatchL( (iRootsFromClient[i])->SubjectName() ) )
+			{
+			iFoundRoot = ETrue;
+			CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromClient[i]) );
+			iWTLSCertChain.iChain->AppendL( cert ); 
+			CleanupStack::Pop(cert);
+			break;
+			}
+		}
+	if(!iFoundRoot) 
+		{
+		for(TInt j=0; j<iRootsFromStore.Count(); j++)
+			{
+			if( issuerName->ExactMatchL( (iRootsFromStore[j])->SubjectName() ) )
+				{
+				iFoundRoot = ETrue;
+				CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromStore[j]) );
+				iWTLSCertChain.iChain->AppendL( cert );
+				CleanupStack::Pop(cert);
+				break;
+				}
+			}
+		}
+	if(!iFoundRoot)
+		{
+		iValidationResult->SetError(EChainHasNoRoot, last);
+		User::RequestComplete(iOriginalRequestStatus, KErrNone);
+		}
+	else
+		{
+		iState = EValidateEnd;
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		SetActive();
+		}
+	}
+
+void CWTLSCertChainAO::HandleEValidateEndL()
+	{
+	TInt i = iWTLSCertChain.iChain->Count() -1;//we can guarantee that chain has at least 1 cert
+	for (; i >= 0; i--)
+		{
+		const CWTLSCertificate* current = iWTLSCertChain.iChain->At(i);
+		if ((!CheckSignatureAndNameL(*current, *iValidationResult, i))	||
+			(!CheckValidityPeriod(*current, *iValidationResult, *iValidationTime, i)))
+			{
+			//these functions set the error internally if there is one
+			break;
+			}
+		}		
+
+	User::RequestComplete(iOriginalRequestStatus, KErrNone);
+	}
+
+HBufC8& CWTLSCertChainAO::GeneratePublicKeyHashL(const CWTLSCertificate& aCert) const
+	{
+	TWTLSKeyFactory keyFactory;
+	CRSAPublicKey* key = keyFactory.RSAPublicKeyL( aCert.PublicKey().KeyData() );
+	CleanupStack::PushL(key);
+	HBufC8* modulusBuffer = key->N().BufferLC();
+	CSHA1* sha1 = CSHA1::NewL();
+	CleanupStack::PushL(sha1);
+	TPtrC8 hash = sha1->Final(*modulusBuffer);
+	HBufC8* permHash = hash.AllocL();
+	CleanupStack::PopAndDestroy(3); //sha1, modulusBuffer, key
+	return *permHash;
+	}