os/security/cryptoservices/certificateandkeymgmt/wtlscert/wtlscertchainao.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /*
     2 * Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: 
    15 *
    16 */
    17 
    18 
    19 #include "wtlscertchainao.h"
    20 #include <asymmetric.h>
    21 #include <bigint.h>
    22 #include <ccertattributefilter.h>
    23 #include <cctcertinfo.h>
    24 
    25 CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs, 
    26 										 CWTLSCertChain& aWTLSCertChain,
    27 										 const CArrayPtr<CWTLSCertificate>& aRootCerts)
    28 	{
    29 	CWTLSCertChainAO* self = new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain);
    30 	CleanupStack::PushL(self);
    31 	self->ConstructL(aRootCerts);
    32 	CleanupStack::Pop(self);
    33 	return self;
    34 	}
    35 
    36 CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs, 
    37 										 CWTLSCertChain& aWTLSCertChain,
    38 										 const TUid aClient)
    39 	{
    40 	return new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain, aClient);
    41 	}
    42 
    43 CWTLSCertChainAO::~CWTLSCertChainAO()
    44 	{
    45 	Cancel();
    46 	delete iCertStoreManager;
    47 	delete iFilter;
    48 	delete iEncodedCertTemp;
    49 	iRootSubjectClientHashList.ResetAndDestroy();
    50 	iRootSubjectStoreHashList.Close();
    51 	iCertInfos.Close(); //In an RMPointerArray Close deletes all elements as 
    52 						//well as any personal allocated space
    53 	iRootsFromStore.ResetAndDestroy();
    54 	iRootsFromStore.Close();
    55 	iRootsFromClient.ResetAndDestroy();
    56 	}
    57 
    58 CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs, 
    59 								   CWTLSCertChain& aWTLSCertChain)
    60 	: CActive(EPriorityNormal), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iEncodedCert(NULL, 0)
    61 	{
    62 	CActiveScheduler::Add(this);
    63 	}
    64 
    65 CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs, 
    66 								   CWTLSCertChain& aWTLSCertChain,
    67 								   const TUid aClient)
    68 	: CActive(0), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iClient(aClient),
    69 		iEncodedCert(NULL, 0)
    70 	{
    71 	CActiveScheduler::Add(this);
    72 	}
    73 
    74 void CWTLSCertChainAO::ConstructL(const CArrayPtr<CWTLSCertificate>& aRootCerts)
    75 	{
    76 	for(TInt i=0; i< aRootCerts.Count(); i++)
    77 		{
    78 		CWTLSCertificate* root = CWTLSCertificate::NewLC(*(aRootCerts[i]));
    79 		User::LeaveIfError( iRootsFromClient.Append(root) );
    80 		CleanupStack::Pop(); //root
    81 		}
    82 	}
    83 
    84 void CWTLSCertChainAO::RunL()
    85 	{
    86 	//If any of my active objects complete with errors then we don't
    87 	//want to proceed
    88 	User::LeaveIfError(iStatus.Int());
    89 
    90 	switch (iState)
    91 		{
    92 		case EStoreManagerInitialization:
    93 			HandleEStoreManagerInitializationL();
    94 			break;
    95 
    96 		case EStoreManagerInitialized:
    97 			HandleEStoreManagerInitializedL();
    98 			break;
    99 
   100 		case EGetCertHashes:
   101 			HandleEGetCertHashesL();
   102 			break;
   103 
   104 		case EPruneList:
   105 			HandleEPruneListL();
   106 			break;
   107 
   108 		case EPruneListDone:
   109 			HandleEPruneListDoneL();
   110 			break;
   111 
   112 		case ECheckTCA:
   113 			HandleECheckTCAL();
   114 			break;
   115 
   116 		case EIsChainSelfSigned:
   117 			HandleEIsChainSelfSignedL();
   118 			break;
   119 
   120 		case ERetrieveRoots:
   121 			HandleERetrieveRootsL();
   122 			break;
   123 
   124 		case EAddRootToList:
   125 			HandleEAddRootToListL();
   126 			break;
   127 
   128 		case EFindRoot:
   129 			HandleEFindRootL();
   130 			break;
   131 
   132 		case EValidateEnd:
   133 			HandleEValidateEndL();
   134 			break;
   135 		
   136 		default:
   137 			__ASSERT_DEBUG(EFalse, User::Panic(_L("CWTLSCertChainAO"), 1));
   138 			User::Leave(KErrArgument);
   139 			break;
   140 		}
   141 	}
   142 
   143 TInt CWTLSCertChainAO::RunError(TInt aError)
   144 	{
   145 	User::RequestComplete(iOriginalRequestStatus, aError);
   146 
   147 	delete iCertStoreManager;
   148 	iCertStoreManager = 0;
   149 
   150 	return 0;
   151 	}
   152 
   153 void CWTLSCertChainAO::DoCancel()
   154 	{
   155 	TRequestStatus* status = &iStatus;
   156 	User::RequestComplete(status, KErrCancel);
   157 	if (iOriginalRequestStatus)
   158 		{
   159 		User::RequestComplete(iOriginalRequestStatus, KErrCancel);
   160 		}
   161 	}
   162 
   163 void CWTLSCertChainAO::Validate(CWTLSValidationResult& aValidationResult,
   164 								const TTime& aValidationTime,								 
   165 								TRequestStatus& aStatus)
   166 	{
   167 	iValidationResult = &aValidationResult;
   168 	iValidationResult->Reset();
   169 	iValidationTime = &aValidationTime;
   170 	iOriginalRequestStatus = &aStatus;
   171 	aStatus = KRequestPending;
   172 
   173 	__ASSERT_DEBUG(!IsActive(), User::Panic(_L("CWTLSCertChainAO"), 1));
   174 	__ASSERT_DEBUG(!iCertStoreManager, User::Panic(_L("CWTLSCertChainAO"), 1));
   175 	
   176 	iState = EStoreManagerInitialization;
   177 	TRequestStatus *status = &iStatus;
   178 	User::RequestComplete(status, KErrNone);
   179 	SetActive();
   180 	}
   181 
   182 TBool CWTLSCertChainAO::CheckSignatureAndNameL(const CWTLSCertificate& aCert, 
   183 											   CWTLSValidationResult& aResult,
   184 											   TInt aPos) const
   185 	{
   186 	TInt issuerPos = aPos + 1;
   187 	TBool res = EFalse;
   188 	if (issuerPos == iWTLSCertChain.iChain->Count())
   189 		//then it's the root
   190 		{
   191 		if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
   192 			//then it claims to be self signed, sig must verify
   193 			{
   194 			if (aCert.VerifySignatureL(aCert.PublicKey().KeyData()))
   195 				{
   196 				res = ETrue;
   197 				}
   198 			else 
   199 				{
   200 				aResult.SetError(ESignatureInvalid, aPos);
   201 				}
   202 			}
   203 		else
   204 			{
   205 			aResult.AppendWarningL(TWTLSValidationStatus(ERootCertNotSelfSigned, aPos));
   206 			res = ETrue; //if its a warning we continue the validation process with the 
   207 						//warning duly noted so we can check for further warn/errors
   208 			}
   209 		}
   210 	else
   211 		//then it isn't the root: so names must chain & sigs must verify
   212 		{
   213 		const CWTLSCertificate* issuer = iWTLSCertChain.iChain->At(issuerPos);
   214 		TBool subject = EFalse;
   215 		TBool signature = EFalse;
   216 		subject = aCert.IssuerName().ExactMatchL(issuer->SubjectName());
   217 		if( !subject ) 
   218 			{
   219 			aResult.SetError(ENamesDontChain, aPos);
   220 			return EFalse;
   221 			}
   222 		signature = aCert.VerifySignatureL(issuer->PublicKey().KeyData());
   223 		if( !signature )
   224 			{
   225 			aResult.SetError(ESignatureInvalid, aPos);
   226 			return EFalse;
   227 			}
   228 		res = subject && signature;
   229 		}
   230 	return res;
   231 	}
   232 
   233 TBool CWTLSCertChainAO::CheckValidityPeriod(const CWTLSCertificate& aCert,
   234 											CWTLSValidationResult& aResult, 
   235 											const TTime aTime,
   236 											TInt aPos) const
   237 	{
   238 	if (aCert.ValidityPeriod().Valid(aTime))
   239 		{
   240 		return ETrue;
   241 		}
   242 	aResult.SetError(EDateOutOfRange, aPos);
   243 	return EFalse;
   244 	}
   245 
   246 void CWTLSCertChainAO::HandleEStoreManagerInitializationL()
   247 	{
   248 	iFilter = CCertAttributeFilter::NewL();
   249 	iFilter->SetFormat(EWTLSCertificate);
   250 	iFilter->SetUid(iClient);
   251 	iFilter->SetOwnerType(ECACertificate);
   252 
   253 	iCertStoreManager = CUnifiedCertStore::NewL(iFs, EFalse);
   254 	iCertStoreManager->Initialize(iStatus);
   255 
   256 	iState = EStoreManagerInitialized;
   257 	SetActive();
   258 	}
   259 
   260 void CWTLSCertChainAO::HandleEStoreManagerInitializedL()
   261 	{
   262 	iCertStoreManager->List(iCertInfos, *iFilter, iStatus);
   263 
   264 	iState = EGetCertHashes;
   265 	SetActive();
   266 	}
   267 
   268 void CWTLSCertChainAO::HandleEGetCertHashesL()
   269 	{
   270 	for(TInt i=0; i<iRootsFromClient.Count(); i++)
   271 		{
   272 		HBufC8* hash = &GeneratePublicKeyHashL( *(iRootsFromClient[i]));
   273 		CleanupStack::PushL(hash);
   274 		User::LeaveIfError( iRootSubjectClientHashList.Append(hash) );
   275 		CleanupStack::Pop(); //hash
   276 		}
   277 	for(TInt j=0; j < iCertInfos.Count(); j++ )
   278 		{
   279 		User::LeaveIfError( iRootSubjectStoreHashList.Append( &((iCertInfos[j])->SubjectKeyId()) ) );
   280 		}
   281 
   282 	iPruned = EFalse;	
   283 	iPrunedChainLength = iWTLSCertChain.iChain->Count();
   284 	iIndex = -1;
   285 
   286 	iState = EPruneList;
   287 	TRequestStatus* status = &iStatus;
   288 	User::RequestComplete(status, KErrNone);
   289 	SetActive();
   290 	}
   291 
   292 /* Walk through the canadiate list and compare the hash of the subjects with the previously
   293  * computed subject hash of certs from the CertStore and certs supplied by the client
   294  */
   295 void CWTLSCertChainAO::HandleEPruneListL()
   296 	{
   297 	iIndex++;
   298 	if(iIndex < iWTLSCertChain.iChain->Count() )
   299 		{
   300 		CWTLSCertificate* cert = iWTLSCertChain.iChain->At(iIndex);
   301 		HBufC8* hash = &GeneratePublicKeyHashL(*cert);
   302 		CleanupStack::PushL(hash);
   303 
   304 		for(TInt i=0; i < iRootSubjectClientHashList.Count(); i++)
   305 			{
   306 			if( (iRootSubjectClientHashList[i])->Compare(*hash) == 0 )
   307 				{
   308 				iPrunedChainLength = iIndex;
   309 				iPruned = ETrue;
   310 				break;
   311 				}
   312 			}
   313 		if(!iPruned)
   314 			{
   315 			for(TInt j=0; j<iRootSubjectStoreHashList.Count(); j++) 
   316 				{
   317 				if( (iRootSubjectStoreHashList[j])->Compare(*hash) == 0 )
   318 					{
   319 					iPrunedChainLength = iIndex;
   320 					iPruned = ETrue;
   321 					break;
   322 					}
   323 				}
   324 			}
   325 		CleanupStack::PopAndDestroy(hash);
   326 		if(iPruned)
   327 			{
   328 			iState = EPruneListDone;
   329 			}
   330 		else 
   331 			{
   332 			iState = EPruneList;
   333 			}
   334 		}
   335 	else
   336 		{
   337 		iState = EPruneListDone;
   338 		}
   339 	TRequestStatus* status = &iStatus;
   340 	User::RequestComplete(status, KErrNone);
   341 	SetActive();
   342 	}
   343 
   344 void CWTLSCertChainAO::HandleEPruneListDoneL()
   345 	{
   346 	if(iPruned) 
   347 		{
   348 		TInt count = iWTLSCertChain.iChain->Count();
   349 		for( TInt i=count - 1; i > iPrunedChainLength; i-- )
   350 			{
   351 			delete iWTLSCertChain.iChain->At(i);
   352 			iWTLSCertChain.iChain->Delete(i);
   353 			}
   354 		iWTLSCertChain.iChain->Compress();
   355 		}
   356 	iState = ECheckTCA;
   357 	TRequestStatus* status = &iStatus;
   358 	User::RequestComplete(status, KErrNone);
   359 	SetActive();
   360 	}
   361 
   362 //checks to see if each certificate in a chain has the authority to sign other certificates
   363 void CWTLSCertChainAO::HandleECheckTCAL()
   364 	{
   365 	TBool validChain = ETrue;
   366 	for( TInt i = 1; i < iWTLSCertChain.iChain->Count(); i++ ) 
   367 		//all intermediate certs (ie not EE certs and not self signed) need
   368 		// to have a field T=ca indicating that they can sign other certs
   369 		{
   370 		if( (iWTLSCertChain.iChain)->At(i)->IsTCAL() == EFalse && 
   371 			(iWTLSCertChain.iChain)->At(i)->IsSelfSignedL() == EFalse ) 
   372 			{
   373 			iValidationResult->SetError(ENotCACert, i);
   374 			User::RequestComplete(iOriginalRequestStatus, KErrNone);
   375 			validChain = EFalse;
   376 			break;
   377 			}
   378 		}
   379 	if(validChain && iPruned) 
   380 		{
   381 		//if we've pruned the list and the chain we have is valid,
   382 		//then our chain already has a root that we trust.
   383 		//therefore there is no need to retrieve one :)
   384 		//therefore goto validation
   385 		iState = EValidateEnd;
   386 		TRequestStatus *status = &iStatus;
   387 		User::RequestComplete(status, KErrNone);
   388 		SetActive();
   389 		}
   390 	else if(validChain) // ie && !iPruned
   391 		{
   392 		//if we haven't pruned but chain is valid then we're back a square one.
   393 		iState = EIsChainSelfSigned;
   394 		TRequestStatus *status = &iStatus;
   395 		User::RequestComplete(status, KErrNone);
   396 		SetActive();
   397 		}
   398 	}
   399 
   400 void CWTLSCertChainAO::HandleEIsChainSelfSignedL()
   401 	{
   402 
   403 	TInt last = iWTLSCertChain.iChain->Count() - 1;
   404 	if( iWTLSCertChain.iChain->At(last)->IsSelfSignedL() )
   405 		{
   406 		//if chained is self signed, and no earlier cert in the sequence was trusted
   407 		//then this is going to fail validation
   408 		//This is just an optimisation to avoid retrieving all the roots from the store
   409 
   410 		iValidationResult->SetError(EChainHasNoRoot, last);
   411 		User::RequestComplete(iOriginalRequestStatus, KErrNone);
   412 		}
   413 	else 
   414 		{
   415 		//standard chain -> need to find the appropriate trusted root for chain if it exists
   416 		iState = ERetrieveRoots;
   417 		iIndex = -1;
   418 		TRequestStatus* status = &iStatus;
   419 		User::RequestComplete(status, KErrNone);
   420 		SetActive();
   421 		}
   422 	}
   423 
   424 void CWTLSCertChainAO::HandleERetrieveRootsL()
   425 	{
   426 	iIndex++;	
   427 	if(iIndex < iCertInfos.Count() )
   428 		{
   429 		if( iEncodedCertTemp != NULL ) 
   430 			{
   431 			delete iEncodedCertTemp;
   432 			}
   433 		iEncodedCertTemp = HBufC8::NewMaxL( (iCertInfos[iIndex])->Size() );
   434 		iEncodedCert.Set( iEncodedCertTemp->Des() );
   435 		iCertStoreManager->Retrieve( *(iCertInfos[iIndex]), iEncodedCert, iStatus );
   436 		iState = EAddRootToList;
   437 		}
   438 	else 
   439 		{
   440 		iState = EFindRoot;
   441 		TRequestStatus* status = &iStatus;
   442 		User::RequestComplete(status, KErrNone);
   443 		}	
   444 	SetActive();
   445 	}
   446 
   447 void CWTLSCertChainAO::HandleEAddRootToListL()
   448 	{
   449 	//are we guarenteed that a cert from the store is a valid WTLScert?
   450 	//ie is this going to leave for reasons other than OOM?
   451 	CWTLSCertificate *cert = CWTLSCertificate::NewL( iEncodedCert );
   452 	User::LeaveIfError( iRootsFromStore.Append(cert) );
   453 
   454 	iState = ERetrieveRoots;
   455 	TRequestStatus* status = &iStatus;
   456 	User::RequestComplete(status, KErrNone);
   457 	SetActive();
   458 	}
   459 
   460 void CWTLSCertChainAO::HandleEFindRootL()
   461 	{
   462 	TInt last = iWTLSCertChain.iChain->Count() - 1;
   463 	const CWTLSName* issuerName = &(iWTLSCertChain.iChain->At(last)->IssuerName());
   464 
   465 	iFoundRoot = EFalse;
   466 	for(TInt i=0; i<iRootsFromClient.Count(); i++) 
   467 		{
   468 		if( issuerName->ExactMatchL( (iRootsFromClient[i])->SubjectName() ) )
   469 			{
   470 			iFoundRoot = ETrue;
   471 			CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromClient[i]) );
   472 			iWTLSCertChain.iChain->AppendL( cert ); 
   473 			CleanupStack::Pop(cert);
   474 			break;
   475 			}
   476 		}
   477 	if(!iFoundRoot) 
   478 		{
   479 		for(TInt j=0; j<iRootsFromStore.Count(); j++)
   480 			{
   481 			if( issuerName->ExactMatchL( (iRootsFromStore[j])->SubjectName() ) )
   482 				{
   483 				iFoundRoot = ETrue;
   484 				CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromStore[j]) );
   485 				iWTLSCertChain.iChain->AppendL( cert );
   486 				CleanupStack::Pop(cert);
   487 				break;
   488 				}
   489 			}
   490 		}
   491 	if(!iFoundRoot)
   492 		{
   493 		iValidationResult->SetError(EChainHasNoRoot, last);
   494 		User::RequestComplete(iOriginalRequestStatus, KErrNone);
   495 		}
   496 	else
   497 		{
   498 		iState = EValidateEnd;
   499 		TRequestStatus* status = &iStatus;
   500 		User::RequestComplete(status, KErrNone);
   501 		SetActive();
   502 		}
   503 	}
   504 
   505 void CWTLSCertChainAO::HandleEValidateEndL()
   506 	{
   507 	TInt i = iWTLSCertChain.iChain->Count() -1;//we can guarantee that chain has at least 1 cert
   508 	for (; i >= 0; i--)
   509 		{
   510 		const CWTLSCertificate* current = iWTLSCertChain.iChain->At(i);
   511 		if ((!CheckSignatureAndNameL(*current, *iValidationResult, i))	||
   512 			(!CheckValidityPeriod(*current, *iValidationResult, *iValidationTime, i)))
   513 			{
   514 			//these functions set the error internally if there is one
   515 			break;
   516 			}
   517 		}		
   518 
   519 	User::RequestComplete(iOriginalRequestStatus, KErrNone);
   520 	}
   521 
   522 HBufC8& CWTLSCertChainAO::GeneratePublicKeyHashL(const CWTLSCertificate& aCert) const
   523 	{
   524 	TWTLSKeyFactory keyFactory;
   525 	CRSAPublicKey* key = keyFactory.RSAPublicKeyL( aCert.PublicKey().KeyData() );
   526 	CleanupStack::PushL(key);
   527 	HBufC8* modulusBuffer = key->N().BufferLC();
   528 	CSHA1* sha1 = CSHA1::NewL();
   529 	CleanupStack::PushL(sha1);
   530 	TPtrC8 hash = sha1->Final(*modulusBuffer);
   531 	HBufC8* permHash = hash.AllocL();
   532 	CleanupStack::PopAndDestroy(3); //sha1, modulusBuffer, key
   533 	return *permHash;
   534 	}
   535