1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/security/cryptoservices/certificateandkeymgmt/wtlscert/wtlscertchainao.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,535 @@
1.4 +/*
1.5 +* Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description:
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +#include "wtlscertchainao.h"
1.23 +#include <asymmetric.h>
1.24 +#include <bigint.h>
1.25 +#include <ccertattributefilter.h>
1.26 +#include <cctcertinfo.h>
1.27 +
1.28 +CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
1.29 + CWTLSCertChain& aWTLSCertChain,
1.30 + const CArrayPtr<CWTLSCertificate>& aRootCerts)
1.31 + {
1.32 + CWTLSCertChainAO* self = new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain);
1.33 + CleanupStack::PushL(self);
1.34 + self->ConstructL(aRootCerts);
1.35 + CleanupStack::Pop(self);
1.36 + return self;
1.37 + }
1.38 +
1.39 +CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
1.40 + CWTLSCertChain& aWTLSCertChain,
1.41 + const TUid aClient)
1.42 + {
1.43 + return new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain, aClient);
1.44 + }
1.45 +
1.46 +CWTLSCertChainAO::~CWTLSCertChainAO()
1.47 + {
1.48 + Cancel();
1.49 + delete iCertStoreManager;
1.50 + delete iFilter;
1.51 + delete iEncodedCertTemp;
1.52 + iRootSubjectClientHashList.ResetAndDestroy();
1.53 + iRootSubjectStoreHashList.Close();
1.54 + iCertInfos.Close(); //In an RMPointerArray Close deletes all elements as
1.55 + //well as any personal allocated space
1.56 + iRootsFromStore.ResetAndDestroy();
1.57 + iRootsFromStore.Close();
1.58 + iRootsFromClient.ResetAndDestroy();
1.59 + }
1.60 +
1.61 +CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
1.62 + CWTLSCertChain& aWTLSCertChain)
1.63 + : CActive(EPriorityNormal), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iEncodedCert(NULL, 0)
1.64 + {
1.65 + CActiveScheduler::Add(this);
1.66 + }
1.67 +
1.68 +CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
1.69 + CWTLSCertChain& aWTLSCertChain,
1.70 + const TUid aClient)
1.71 + : CActive(0), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iClient(aClient),
1.72 + iEncodedCert(NULL, 0)
1.73 + {
1.74 + CActiveScheduler::Add(this);
1.75 + }
1.76 +
1.77 +void CWTLSCertChainAO::ConstructL(const CArrayPtr<CWTLSCertificate>& aRootCerts)
1.78 + {
1.79 + for(TInt i=0; i< aRootCerts.Count(); i++)
1.80 + {
1.81 + CWTLSCertificate* root = CWTLSCertificate::NewLC(*(aRootCerts[i]));
1.82 + User::LeaveIfError( iRootsFromClient.Append(root) );
1.83 + CleanupStack::Pop(); //root
1.84 + }
1.85 + }
1.86 +
1.87 +void CWTLSCertChainAO::RunL()
1.88 + {
1.89 + //If any of my active objects complete with errors then we don't
1.90 + //want to proceed
1.91 + User::LeaveIfError(iStatus.Int());
1.92 +
1.93 + switch (iState)
1.94 + {
1.95 + case EStoreManagerInitialization:
1.96 + HandleEStoreManagerInitializationL();
1.97 + break;
1.98 +
1.99 + case EStoreManagerInitialized:
1.100 + HandleEStoreManagerInitializedL();
1.101 + break;
1.102 +
1.103 + case EGetCertHashes:
1.104 + HandleEGetCertHashesL();
1.105 + break;
1.106 +
1.107 + case EPruneList:
1.108 + HandleEPruneListL();
1.109 + break;
1.110 +
1.111 + case EPruneListDone:
1.112 + HandleEPruneListDoneL();
1.113 + break;
1.114 +
1.115 + case ECheckTCA:
1.116 + HandleECheckTCAL();
1.117 + break;
1.118 +
1.119 + case EIsChainSelfSigned:
1.120 + HandleEIsChainSelfSignedL();
1.121 + break;
1.122 +
1.123 + case ERetrieveRoots:
1.124 + HandleERetrieveRootsL();
1.125 + break;
1.126 +
1.127 + case EAddRootToList:
1.128 + HandleEAddRootToListL();
1.129 + break;
1.130 +
1.131 + case EFindRoot:
1.132 + HandleEFindRootL();
1.133 + break;
1.134 +
1.135 + case EValidateEnd:
1.136 + HandleEValidateEndL();
1.137 + break;
1.138 +
1.139 + default:
1.140 + __ASSERT_DEBUG(EFalse, User::Panic(_L("CWTLSCertChainAO"), 1));
1.141 + User::Leave(KErrArgument);
1.142 + break;
1.143 + }
1.144 + }
1.145 +
1.146 +TInt CWTLSCertChainAO::RunError(TInt aError)
1.147 + {
1.148 + User::RequestComplete(iOriginalRequestStatus, aError);
1.149 +
1.150 + delete iCertStoreManager;
1.151 + iCertStoreManager = 0;
1.152 +
1.153 + return 0;
1.154 + }
1.155 +
1.156 +void CWTLSCertChainAO::DoCancel()
1.157 + {
1.158 + TRequestStatus* status = &iStatus;
1.159 + User::RequestComplete(status, KErrCancel);
1.160 + if (iOriginalRequestStatus)
1.161 + {
1.162 + User::RequestComplete(iOriginalRequestStatus, KErrCancel);
1.163 + }
1.164 + }
1.165 +
1.166 +void CWTLSCertChainAO::Validate(CWTLSValidationResult& aValidationResult,
1.167 + const TTime& aValidationTime,
1.168 + TRequestStatus& aStatus)
1.169 + {
1.170 + iValidationResult = &aValidationResult;
1.171 + iValidationResult->Reset();
1.172 + iValidationTime = &aValidationTime;
1.173 + iOriginalRequestStatus = &aStatus;
1.174 + aStatus = KRequestPending;
1.175 +
1.176 + __ASSERT_DEBUG(!IsActive(), User::Panic(_L("CWTLSCertChainAO"), 1));
1.177 + __ASSERT_DEBUG(!iCertStoreManager, User::Panic(_L("CWTLSCertChainAO"), 1));
1.178 +
1.179 + iState = EStoreManagerInitialization;
1.180 + TRequestStatus *status = &iStatus;
1.181 + User::RequestComplete(status, KErrNone);
1.182 + SetActive();
1.183 + }
1.184 +
1.185 +TBool CWTLSCertChainAO::CheckSignatureAndNameL(const CWTLSCertificate& aCert,
1.186 + CWTLSValidationResult& aResult,
1.187 + TInt aPos) const
1.188 + {
1.189 + TInt issuerPos = aPos + 1;
1.190 + TBool res = EFalse;
1.191 + if (issuerPos == iWTLSCertChain.iChain->Count())
1.192 + //then it's the root
1.193 + {
1.194 + if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
1.195 + //then it claims to be self signed, sig must verify
1.196 + {
1.197 + if (aCert.VerifySignatureL(aCert.PublicKey().KeyData()))
1.198 + {
1.199 + res = ETrue;
1.200 + }
1.201 + else
1.202 + {
1.203 + aResult.SetError(ESignatureInvalid, aPos);
1.204 + }
1.205 + }
1.206 + else
1.207 + {
1.208 + aResult.AppendWarningL(TWTLSValidationStatus(ERootCertNotSelfSigned, aPos));
1.209 + res = ETrue; //if its a warning we continue the validation process with the
1.210 + //warning duly noted so we can check for further warn/errors
1.211 + }
1.212 + }
1.213 + else
1.214 + //then it isn't the root: so names must chain & sigs must verify
1.215 + {
1.216 + const CWTLSCertificate* issuer = iWTLSCertChain.iChain->At(issuerPos);
1.217 + TBool subject = EFalse;
1.218 + TBool signature = EFalse;
1.219 + subject = aCert.IssuerName().ExactMatchL(issuer->SubjectName());
1.220 + if( !subject )
1.221 + {
1.222 + aResult.SetError(ENamesDontChain, aPos);
1.223 + return EFalse;
1.224 + }
1.225 + signature = aCert.VerifySignatureL(issuer->PublicKey().KeyData());
1.226 + if( !signature )
1.227 + {
1.228 + aResult.SetError(ESignatureInvalid, aPos);
1.229 + return EFalse;
1.230 + }
1.231 + res = subject && signature;
1.232 + }
1.233 + return res;
1.234 + }
1.235 +
1.236 +TBool CWTLSCertChainAO::CheckValidityPeriod(const CWTLSCertificate& aCert,
1.237 + CWTLSValidationResult& aResult,
1.238 + const TTime aTime,
1.239 + TInt aPos) const
1.240 + {
1.241 + if (aCert.ValidityPeriod().Valid(aTime))
1.242 + {
1.243 + return ETrue;
1.244 + }
1.245 + aResult.SetError(EDateOutOfRange, aPos);
1.246 + return EFalse;
1.247 + }
1.248 +
1.249 +void CWTLSCertChainAO::HandleEStoreManagerInitializationL()
1.250 + {
1.251 + iFilter = CCertAttributeFilter::NewL();
1.252 + iFilter->SetFormat(EWTLSCertificate);
1.253 + iFilter->SetUid(iClient);
1.254 + iFilter->SetOwnerType(ECACertificate);
1.255 +
1.256 + iCertStoreManager = CUnifiedCertStore::NewL(iFs, EFalse);
1.257 + iCertStoreManager->Initialize(iStatus);
1.258 +
1.259 + iState = EStoreManagerInitialized;
1.260 + SetActive();
1.261 + }
1.262 +
1.263 +void CWTLSCertChainAO::HandleEStoreManagerInitializedL()
1.264 + {
1.265 + iCertStoreManager->List(iCertInfos, *iFilter, iStatus);
1.266 +
1.267 + iState = EGetCertHashes;
1.268 + SetActive();
1.269 + }
1.270 +
1.271 +void CWTLSCertChainAO::HandleEGetCertHashesL()
1.272 + {
1.273 + for(TInt i=0; i<iRootsFromClient.Count(); i++)
1.274 + {
1.275 + HBufC8* hash = &GeneratePublicKeyHashL( *(iRootsFromClient[i]));
1.276 + CleanupStack::PushL(hash);
1.277 + User::LeaveIfError( iRootSubjectClientHashList.Append(hash) );
1.278 + CleanupStack::Pop(); //hash
1.279 + }
1.280 + for(TInt j=0; j < iCertInfos.Count(); j++ )
1.281 + {
1.282 + User::LeaveIfError( iRootSubjectStoreHashList.Append( &((iCertInfos[j])->SubjectKeyId()) ) );
1.283 + }
1.284 +
1.285 + iPruned = EFalse;
1.286 + iPrunedChainLength = iWTLSCertChain.iChain->Count();
1.287 + iIndex = -1;
1.288 +
1.289 + iState = EPruneList;
1.290 + TRequestStatus* status = &iStatus;
1.291 + User::RequestComplete(status, KErrNone);
1.292 + SetActive();
1.293 + }
1.294 +
1.295 +/* Walk through the canadiate list and compare the hash of the subjects with the previously
1.296 + * computed subject hash of certs from the CertStore and certs supplied by the client
1.297 + */
1.298 +void CWTLSCertChainAO::HandleEPruneListL()
1.299 + {
1.300 + iIndex++;
1.301 + if(iIndex < iWTLSCertChain.iChain->Count() )
1.302 + {
1.303 + CWTLSCertificate* cert = iWTLSCertChain.iChain->At(iIndex);
1.304 + HBufC8* hash = &GeneratePublicKeyHashL(*cert);
1.305 + CleanupStack::PushL(hash);
1.306 +
1.307 + for(TInt i=0; i < iRootSubjectClientHashList.Count(); i++)
1.308 + {
1.309 + if( (iRootSubjectClientHashList[i])->Compare(*hash) == 0 )
1.310 + {
1.311 + iPrunedChainLength = iIndex;
1.312 + iPruned = ETrue;
1.313 + break;
1.314 + }
1.315 + }
1.316 + if(!iPruned)
1.317 + {
1.318 + for(TInt j=0; j<iRootSubjectStoreHashList.Count(); j++)
1.319 + {
1.320 + if( (iRootSubjectStoreHashList[j])->Compare(*hash) == 0 )
1.321 + {
1.322 + iPrunedChainLength = iIndex;
1.323 + iPruned = ETrue;
1.324 + break;
1.325 + }
1.326 + }
1.327 + }
1.328 + CleanupStack::PopAndDestroy(hash);
1.329 + if(iPruned)
1.330 + {
1.331 + iState = EPruneListDone;
1.332 + }
1.333 + else
1.334 + {
1.335 + iState = EPruneList;
1.336 + }
1.337 + }
1.338 + else
1.339 + {
1.340 + iState = EPruneListDone;
1.341 + }
1.342 + TRequestStatus* status = &iStatus;
1.343 + User::RequestComplete(status, KErrNone);
1.344 + SetActive();
1.345 + }
1.346 +
1.347 +void CWTLSCertChainAO::HandleEPruneListDoneL()
1.348 + {
1.349 + if(iPruned)
1.350 + {
1.351 + TInt count = iWTLSCertChain.iChain->Count();
1.352 + for( TInt i=count - 1; i > iPrunedChainLength; i-- )
1.353 + {
1.354 + delete iWTLSCertChain.iChain->At(i);
1.355 + iWTLSCertChain.iChain->Delete(i);
1.356 + }
1.357 + iWTLSCertChain.iChain->Compress();
1.358 + }
1.359 + iState = ECheckTCA;
1.360 + TRequestStatus* status = &iStatus;
1.361 + User::RequestComplete(status, KErrNone);
1.362 + SetActive();
1.363 + }
1.364 +
1.365 +//checks to see if each certificate in a chain has the authority to sign other certificates
1.366 +void CWTLSCertChainAO::HandleECheckTCAL()
1.367 + {
1.368 + TBool validChain = ETrue;
1.369 + for( TInt i = 1; i < iWTLSCertChain.iChain->Count(); i++ )
1.370 + //all intermediate certs (ie not EE certs and not self signed) need
1.371 + // to have a field T=ca indicating that they can sign other certs
1.372 + {
1.373 + if( (iWTLSCertChain.iChain)->At(i)->IsTCAL() == EFalse &&
1.374 + (iWTLSCertChain.iChain)->At(i)->IsSelfSignedL() == EFalse )
1.375 + {
1.376 + iValidationResult->SetError(ENotCACert, i);
1.377 + User::RequestComplete(iOriginalRequestStatus, KErrNone);
1.378 + validChain = EFalse;
1.379 + break;
1.380 + }
1.381 + }
1.382 + if(validChain && iPruned)
1.383 + {
1.384 + //if we've pruned the list and the chain we have is valid,
1.385 + //then our chain already has a root that we trust.
1.386 + //therefore there is no need to retrieve one :)
1.387 + //therefore goto validation
1.388 + iState = EValidateEnd;
1.389 + TRequestStatus *status = &iStatus;
1.390 + User::RequestComplete(status, KErrNone);
1.391 + SetActive();
1.392 + }
1.393 + else if(validChain) // ie && !iPruned
1.394 + {
1.395 + //if we haven't pruned but chain is valid then we're back a square one.
1.396 + iState = EIsChainSelfSigned;
1.397 + TRequestStatus *status = &iStatus;
1.398 + User::RequestComplete(status, KErrNone);
1.399 + SetActive();
1.400 + }
1.401 + }
1.402 +
1.403 +void CWTLSCertChainAO::HandleEIsChainSelfSignedL()
1.404 + {
1.405 +
1.406 + TInt last = iWTLSCertChain.iChain->Count() - 1;
1.407 + if( iWTLSCertChain.iChain->At(last)->IsSelfSignedL() )
1.408 + {
1.409 + //if chained is self signed, and no earlier cert in the sequence was trusted
1.410 + //then this is going to fail validation
1.411 + //This is just an optimisation to avoid retrieving all the roots from the store
1.412 +
1.413 + iValidationResult->SetError(EChainHasNoRoot, last);
1.414 + User::RequestComplete(iOriginalRequestStatus, KErrNone);
1.415 + }
1.416 + else
1.417 + {
1.418 + //standard chain -> need to find the appropriate trusted root for chain if it exists
1.419 + iState = ERetrieveRoots;
1.420 + iIndex = -1;
1.421 + TRequestStatus* status = &iStatus;
1.422 + User::RequestComplete(status, KErrNone);
1.423 + SetActive();
1.424 + }
1.425 + }
1.426 +
1.427 +void CWTLSCertChainAO::HandleERetrieveRootsL()
1.428 + {
1.429 + iIndex++;
1.430 + if(iIndex < iCertInfos.Count() )
1.431 + {
1.432 + if( iEncodedCertTemp != NULL )
1.433 + {
1.434 + delete iEncodedCertTemp;
1.435 + }
1.436 + iEncodedCertTemp = HBufC8::NewMaxL( (iCertInfos[iIndex])->Size() );
1.437 + iEncodedCert.Set( iEncodedCertTemp->Des() );
1.438 + iCertStoreManager->Retrieve( *(iCertInfos[iIndex]), iEncodedCert, iStatus );
1.439 + iState = EAddRootToList;
1.440 + }
1.441 + else
1.442 + {
1.443 + iState = EFindRoot;
1.444 + TRequestStatus* status = &iStatus;
1.445 + User::RequestComplete(status, KErrNone);
1.446 + }
1.447 + SetActive();
1.448 + }
1.449 +
1.450 +void CWTLSCertChainAO::HandleEAddRootToListL()
1.451 + {
1.452 + //are we guarenteed that a cert from the store is a valid WTLScert?
1.453 + //ie is this going to leave for reasons other than OOM?
1.454 + CWTLSCertificate *cert = CWTLSCertificate::NewL( iEncodedCert );
1.455 + User::LeaveIfError( iRootsFromStore.Append(cert) );
1.456 +
1.457 + iState = ERetrieveRoots;
1.458 + TRequestStatus* status = &iStatus;
1.459 + User::RequestComplete(status, KErrNone);
1.460 + SetActive();
1.461 + }
1.462 +
1.463 +void CWTLSCertChainAO::HandleEFindRootL()
1.464 + {
1.465 + TInt last = iWTLSCertChain.iChain->Count() - 1;
1.466 + const CWTLSName* issuerName = &(iWTLSCertChain.iChain->At(last)->IssuerName());
1.467 +
1.468 + iFoundRoot = EFalse;
1.469 + for(TInt i=0; i<iRootsFromClient.Count(); i++)
1.470 + {
1.471 + if( issuerName->ExactMatchL( (iRootsFromClient[i])->SubjectName() ) )
1.472 + {
1.473 + iFoundRoot = ETrue;
1.474 + CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromClient[i]) );
1.475 + iWTLSCertChain.iChain->AppendL( cert );
1.476 + CleanupStack::Pop(cert);
1.477 + break;
1.478 + }
1.479 + }
1.480 + if(!iFoundRoot)
1.481 + {
1.482 + for(TInt j=0; j<iRootsFromStore.Count(); j++)
1.483 + {
1.484 + if( issuerName->ExactMatchL( (iRootsFromStore[j])->SubjectName() ) )
1.485 + {
1.486 + iFoundRoot = ETrue;
1.487 + CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromStore[j]) );
1.488 + iWTLSCertChain.iChain->AppendL( cert );
1.489 + CleanupStack::Pop(cert);
1.490 + break;
1.491 + }
1.492 + }
1.493 + }
1.494 + if(!iFoundRoot)
1.495 + {
1.496 + iValidationResult->SetError(EChainHasNoRoot, last);
1.497 + User::RequestComplete(iOriginalRequestStatus, KErrNone);
1.498 + }
1.499 + else
1.500 + {
1.501 + iState = EValidateEnd;
1.502 + TRequestStatus* status = &iStatus;
1.503 + User::RequestComplete(status, KErrNone);
1.504 + SetActive();
1.505 + }
1.506 + }
1.507 +
1.508 +void CWTLSCertChainAO::HandleEValidateEndL()
1.509 + {
1.510 + TInt i = iWTLSCertChain.iChain->Count() -1;//we can guarantee that chain has at least 1 cert
1.511 + for (; i >= 0; i--)
1.512 + {
1.513 + const CWTLSCertificate* current = iWTLSCertChain.iChain->At(i);
1.514 + if ((!CheckSignatureAndNameL(*current, *iValidationResult, i)) ||
1.515 + (!CheckValidityPeriod(*current, *iValidationResult, *iValidationTime, i)))
1.516 + {
1.517 + //these functions set the error internally if there is one
1.518 + break;
1.519 + }
1.520 + }
1.521 +
1.522 + User::RequestComplete(iOriginalRequestStatus, KErrNone);
1.523 + }
1.524 +
1.525 +HBufC8& CWTLSCertChainAO::GeneratePublicKeyHashL(const CWTLSCertificate& aCert) const
1.526 + {
1.527 + TWTLSKeyFactory keyFactory;
1.528 + CRSAPublicKey* key = keyFactory.RSAPublicKeyL( aCert.PublicKey().KeyData() );
1.529 + CleanupStack::PushL(key);
1.530 + HBufC8* modulusBuffer = key->N().BufferLC();
1.531 + CSHA1* sha1 = CSHA1::NewL();
1.532 + CleanupStack::PushL(sha1);
1.533 + TPtrC8 hash = sha1->Final(*modulusBuffer);
1.534 + HBufC8* permHash = hash.AllocL();
1.535 + CleanupStack::PopAndDestroy(3); //sha1, modulusBuffer, key
1.536 + return *permHash;
1.537 + }
1.538 +