/*
* Copyright (c) 2001-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 "unifiedkeystore.h"
#include <ecom/ecom.h>
#include <random.h>
#include <pbedata.h>
#include <asnpkcs.h>
#include "mctcertappinterface.h"
#include <mctkeystoreuids.h>

_LIT(KUnifiedKeyStore, "UnifiedKeyStore");

/////////////////////////////////////////////////////////////////////////////////////
//CUnifiedKeyStore
/////////////////////////////////////////////////////////////////////////////////////

EXPORT_C CUnifiedKeyStore* CUnifiedKeyStore::NewL(RFs& aFs)
	{
	CUnifiedKeyStore* self = CUnifiedKeyStore::NewLC(aFs);
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CUnifiedKeyStore* CUnifiedKeyStore::NewLC(RFs& aFs)
	{
	CUnifiedKeyStore* self = new(ELeave) CUnifiedKeyStore(aFs);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

EXPORT_C CUnifiedKeyStore::~CUnifiedKeyStore()
	{
	Cancel();
	Cleanup();

	iKeyStoresHolder.ResetAndDestroy();
	iKeyStoresHolder.Close();

	REComSession::FinalClose();
	}

void CUnifiedKeyStore::DoInitializeL()
{//	We want the list of all token types that support a keystore interface
	RArray<TUid> uidArray;
	CleanupClosePushL(uidArray);
	
	User::LeaveIfError(uidArray.Append(TUid::Uid(KInterfaceKeyStore)));

	TCTFindTokenTypesByInterface filter(uidArray.Array());
	CCTTokenTypeInfo::ListL(iTokenTypes, filter);
	
	CleanupStack::PopAndDestroy();	// uidArray
}

EXPORT_C void CUnifiedKeyStore::Initialize(TRequestStatus& aStatus)
{// The following assertion checks that we didn't call Initialize twice
	__ASSERT_DEBUG((iKeyStoresHolder.Count()==0), User::Panic(KUnifiedKeyStore, EUnexpectedInitialise));

	TRAPD(err, DoInitializeL());
	if (err != KErrNone)
		{
		TRequestStatus* status = &aStatus;
		User::RequestComplete(status, err);
		return;
		}
	
	iIndexTokenTypes = -1;
	StartAsyncOperation(EInitializeGetTokenList, aStatus);

	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
}

EXPORT_C void CUnifiedKeyStore::CancelInitialize()
	{
	if (iState == EInitializeGetTokenList ||
		iState == EInitializeGetToken ||
		iState == EInitialiseGetKeyManagerInterface ||
		iState == EInitializeGetKeyUserInterface ||
		iState == EInitializeGetKeyUserInterfaceFinished ||
		iState == EInitializeFinished)
		{
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::CreateKey(TInt aKeyStoreIndex, TKeyUsagePKCS15 aUsage,TUint aSize, 
										  const TDesC& aLabel, CCTKeyInfo::EKeyAlgorithm aAlgorithm, 
										  TInt aAccessType, TTime aStartDate, TTime aEndDate, 
										  CCTKeyInfo*& aKeyInfoOut, TRequestStatus& aStatus)
	{
	StartAsyncOperation(ECreateKey, aStatus);
	TRAPD(err, PrepareToCreateKeyL(aKeyStoreIndex, aUsage, aSize, aLabel, aAlgorithm, aAccessType,
								   aStartDate, aEndDate, aStatus));
	if (KErrNone != err)
		{
		Complete(err);
		return;
		}
	
	iKeyInfoOut = &aKeyInfoOut;
	aKeyInfoOut = NULL;
	iKeyStoreManager->CreateKey(iKeyInfo, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelCreateKey()
	{
	if (iState == ECreateKey)
		{			
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::ImportKey(TInt aKeyStoreIndex, const TDesC8& aKeyData,
										  TKeyUsagePKCS15 aUsage, const TDesC& aLabel, 
										  TInt aAccessType, TTime aStartDate, TTime aEndDate, 
										  CCTKeyInfo*& aKeyInfoOut, TRequestStatus& aStatus)
	{
	TBool isEncrypted = TASN1DecPKCS8::IsEncryptedPKCS8Data(aKeyData);
	StartAsyncOperation(isEncrypted ? EImportKeyEncrypted : EImportKey, aStatus);

	ASSERT(!iKeyData);
	iKeyData = aKeyData.Alloc();
	if (!iKeyData)	//	OOM or some other catastrophe
		{
		Complete(KErrNoMemory);
		return;
		}
	
	TRAPD(err, PrepareToCreateKeyL(aKeyStoreIndex, aUsage, 0, aLabel, CCTKeyInfo::EInvalidAlgorithm, aAccessType,
								   aStartDate, aEndDate, aStatus));
	if (KErrNone != err)
		{
		Complete(err);
		return;
		}

	iKeyInfoOut = &aKeyInfoOut;
	aKeyInfoOut = NULL;

	if (isEncrypted)
		{
		iKeyStoreManager->ImportEncryptedKey(*iKeyData, iKeyInfo, iStatus);
		}
	else
		{
		iKeyStoreManager->ImportKey(*iKeyData, iKeyInfo, iStatus);
		}
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelImportKey()
	{
	if (iState == EImportKey ||
		iState == EImportKeyEncrypted)
		{
		Cancel();
		}
	}

void CUnifiedKeyStore::PrepareToCreateKeyL(	TInt aKeyStoreIndex,
											TKeyUsagePKCS15 aUsage, TUint aSize, 
											const TDesC& aLabel, 
											CCTKeyInfo::EKeyAlgorithm aAlgorithm,
											TInt aAccessType, 
											TTime aStartTime, TTime aEndTime,
											TRequestStatus& aStatus)
	{
	ASSERT(!iKeyStoreManager);
	
	//	These values are filled in by the server when the key is created
	TKeyIdentifier keyID;
	keyID.MaxSize();
	keyID.FillZ(keyID.MaxSize());
	TInt keyHandle = 0;

	// Get the secure ID of the current process
	RProcess thisProcess;
	User::LeaveIfError(thisProcess.Open(thisProcess.Id()));
	TSecureId creatorId = thisProcess.SecureId();
	thisProcess.Close();

	// Default management policy: resict to creating process
	TSecurityPolicy managementPolicy(creatorId);

	// Default use policy: also resict to creating process
	TSecurityPolicy usePolicy(creatorId);

	HBufC* label = aLabel.AllocLC();

	// Panics if keystore manager index invalid
	MCTKeyStoreManager& keystore = KeyStoreManager(aKeyStoreIndex);

	iKeyInfo = CCTKeyInfo::NewL(keyID, aUsage, aSize, NULL, label, keystore.Token(),
								keyHandle, usePolicy, managementPolicy, aAlgorithm,
								aAccessType, ETrue, aStartTime, aEndTime);
	CleanupStack::Pop(label);

	iKeyStoreManager = &keystore;
	iOriginalRequestStatus = &aStatus;
	aStatus = KRequestPending;
	}

//	************************************************************************
//	MKeyStore
//	************************************************************************

void CUnifiedKeyStore::List(RMPointerArray<CCTKeyInfo>& aKeys, 
							const TCTKeyAttributeFilter& aFilter, 
							TRequestStatus& aStatus)
	{
	StartAsyncOperation(EList, aStatus);

	iKeyInfos = &aKeys;

	delete iFilter;
	iFilter = new TCTKeyAttributeFilter(aFilter);
	if (!iFilter)
		{
		Complete(KErrNoMemory);
		return;
		}
	
	iIndex = -1;

	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
}

void CUnifiedKeyStore::CancelList()
	{
	if (iState == EList)
		{
		Cancel();
		}
	}

void CUnifiedKeyStore::GetKeyInfo(TCTTokenObjectHandle aHandle, 
										   CCTKeyInfo*& aKeyInfo,
										   TRequestStatus& aStatus)
	{
	StartAsyncOperation(EGetKeyInfo, aStatus);
	
	ASSERT(!iKeyStore);	
	iKeyStore = FindKeyStore(aHandle);
	if (!iKeyStore)
		{
		Complete(KErrNotFound);
		return;
		}

	iKeyStore->GetKeyInfo(aHandle, aKeyInfo, iStatus);
	SetActive();
	}

void CUnifiedKeyStore::CancelGetKeyInfo()
	{
	if (iState == EGetKeyInfo)
		{
		Cancel();
		}
	}

// Implementation for most of the Open() method
TBool CUnifiedKeyStore::DoOpen(const TCTTokenObjectHandle& aHandle, 
							   TRequestStatus& aStatus)
	{
	StartAsyncOperation(EOpen, aStatus);
	
	ASSERT(!iKeyStore);	
	iKeyStore = FindKeyStore(aHandle);
	if (!iKeyStore)
		{
		Complete(KErrNotFound);
		return EFalse;
		} 

	SetActive();
	return ETrue;
	}

void CUnifiedKeyStore::Open(const TCTTokenObjectHandle& aHandle, 
							MRSASigner*& aSigner,
							TRequestStatus& aStatus)
	{
	if (DoOpen(aHandle, aStatus))
		{
		iKeyStore->Open(aHandle, aSigner, iStatus);
		}
	}

void CUnifiedKeyStore::Open(const TCTTokenObjectHandle& aHandle, 
							MDSASigner*& aSigner, 
							TRequestStatus& aStatus)
	{
	if (DoOpen(aHandle, aStatus))
		{			
		iKeyStore->Open(aHandle, aSigner, iStatus);
		}
	}

void CUnifiedKeyStore::Open(const TCTTokenObjectHandle& aHandle, 
							MCTDecryptor*& aDecryptor,
							TRequestStatus& aStatus)
	{
	if (DoOpen(aHandle, aStatus))
		{
		iKeyStore->Open(aHandle, aDecryptor, iStatus);
		}
	}

void CUnifiedKeyStore::Open(const TCTTokenObjectHandle& aHandle, 
							MCTDH*& aDH, TRequestStatus& aStatus)
	{	
	if (DoOpen(aHandle, aStatus))
		{
		iKeyStore->Open(aHandle, aDH, iStatus);
		}
	}

void CUnifiedKeyStore::CancelOpen()
	{
	if (iState == EOpen)
		{
		Cancel();
		}
	}

/** Returns the public key in DER-encoded ASN-1 */
void CUnifiedKeyStore::ExportPublic(const TCTTokenObjectHandle& aHandle,
									HBufC8*& aPublicKey,
									TRequestStatus& aStatus)
	{
	StartAsyncOperation(EExportPublic, aStatus);

	iKeyStore = FindKeyStore(aHandle);
	if (!iKeyStore)
		{
		Complete(KErrNotFound);
		return;
		}
		
	iKeyStore->ExportPublic(aHandle, aPublicKey, iStatus);
	SetActive();
	}

void CUnifiedKeyStore::CancelExportPublic()
	{
	if (iState == EExportPublic)
		{
		Cancel();
		}
	}

//	************************************************************************
//	MKeyStoreManager
//	************************************************************************
	
EXPORT_C void CUnifiedKeyStore::ExportKey(TCTTokenObjectHandle aHandle, 
										  HBufC8*& aKey, TRequestStatus& aStatus)
	{
	StartAsyncOperation(EExportKey, aStatus);

	ASSERT(!iKeyStoreManager);
	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		} 

	iKeyStoreManager->ExportKey(aHandle, aKey, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelExportKey()
	{
	if (iState == EExportKey)
		{
		Cancel();
		}
	} 

EXPORT_C void CUnifiedKeyStore::ExportEncryptedKey(TCTTokenObjectHandle aHandle, 
												   const CPBEncryptParms& aEncryptParams,
												   HBufC8*& aKey, TRequestStatus& aStatus)
	{
	StartAsyncOperation(EExportEncryptedKey, aStatus);
	
	ASSERT(!iKeyStoreManager);	
	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		} 
		
	iKeyStoreManager->ExportEncryptedKey(aHandle, aEncryptParams, aKey, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelExportEncryptedKey()
	{
	if (iState == EExportEncryptedKey)
		{
		Cancel();
		}	
	}

EXPORT_C void CUnifiedKeyStore::DeleteKey(TCTTokenObjectHandle aHandle, 
										  TRequestStatus& aStatus)
	{
	StartAsyncOperation(EDeleteKey, aStatus);

	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		}
	
	iKeyStoreManager->DeleteKey(aHandle, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelDeleteKey()
	{
	if (iState == EDeleteKey)
		{
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::SetUsePolicy(TCTTokenObjectHandle aHandle, 
											 const TSecurityPolicy& aPolicy,
											 TRequestStatus& aStatus)
	{
	StartAsyncOperation(ESetUsePolicy, aStatus);

	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		}
	
	iKeyStoreManager->SetUsePolicy(aHandle, aPolicy, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelSetUsePolicy()
	{
	if (iState == ESetUsePolicy)
		{
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::SetManagementPolicy(TCTTokenObjectHandle aHandle, 
													const TSecurityPolicy& aPolicy,
													TRequestStatus& aStatus)
	{
	StartAsyncOperation(ESetManagementPolicy, aStatus);

	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		}
	
	iKeyStoreManager->SetManagementPolicy(aHandle, aPolicy, iStatus);
	SetActive();
	}

EXPORT_C void CUnifiedKeyStore::CancelSetManagementPolicy()
	{
	if (iState == ESetManagementPolicy)
		{
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::SetPassphraseTimeout(TInt aTimeout, 
													 TRequestStatus& aStatus)
	{
	StartAsyncOperation(ESetPassphraseTimeout, aStatus);

	iIndex = -1;
	iNewTimeout = aTimeout;
	SetActive();
	
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
	}

EXPORT_C void CUnifiedKeyStore::CancelSetPassphraseTimeout()
	{
	if (iState == ESetPassphraseTimeout)
		{
		Cancel();
		}
	}

EXPORT_C void CUnifiedKeyStore::Relock(TRequestStatus& aStatus)
	{
	StartAsyncOperation(ERelock, aStatus);

	iIndex = -1;
	SetActive();
	
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
	}

EXPORT_C void CUnifiedKeyStore::CancelRelock()
	{
	if (iState == ERelock)
		{
		Cancel();
		}
	}

//	************************************************************************
//	Other exports
//	************************************************************************

EXPORT_C TInt CUnifiedKeyStore::KeyStoreCount() const
{
	return (iKeyStoresHolder.Count());
}

EXPORT_C MCTKeyStore& CUnifiedKeyStore::KeyStore(TInt aIndex)
{
	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iKeyStoresHolder.Count(),
					User::Panic(KUnifiedKeyStore, EArrayAccessOutOfBounds));
	
	MCTKeyStore* keyStore = static_cast<MCTKeyStore*>(iKeyStoresHolder[aIndex]->KeyStore());
	return (*keyStore);
}

EXPORT_C TInt CUnifiedKeyStore::KeyStoreManagerCount() const
	{
	TInt result = 0;
	for (TInt i = 0 ; i < iKeyStoresHolder.Count() ; ++i)
		{
		if (iKeyStoresHolder[i]->IsKeyManager())
			{
			++result;
			}
		}
	return result;
	}

EXPORT_C MCTKeyStoreManager& CUnifiedKeyStore::KeyStoreManager(TInt aIndex)
	{
	__ASSERT_ALWAYS(aIndex >= 0, User::Panic(KUnifiedKeyStore, EArrayAccessOutOfBounds));
	TInt managerIndex = 0;
	MCTKeyStoreManager* result = NULL;
	for (TInt i = 0 ; i < iKeyStoresHolder.Count() ; ++i)
		{
		if (iKeyStoresHolder[i]->IsKeyManager())
			{
			if (managerIndex == aIndex)
				{
				result = static_cast<MCTKeyStoreManager*>(iKeyStoresHolder[i]->KeyStore());
				break;
				}
			++managerIndex;
			}
		}
	__ASSERT_ALWAYS(result != NULL, User::Panic(KUnifiedKeyStore, EArrayAccessOutOfBounds));
	return *result;
	}

#ifdef SYMBIAN_AUTH_SERVER
	
EXPORT_C void CUnifiedKeyStore::CreateKey(	TInt aKeyStoreIndex, TKeyUsagePKCS15 aUsage,TUint aSize, 
								const TDesC& aLabel, CCTKeyInfo::EKeyAlgorithm aAlgorithm, 
								TInt aAccessType, TTime aStartDate, TTime aEndDate, 
								const TDesC& aAuthenticationString, TInt aFreshness,
								CCTKeyInfo*& aKeyInfoOut, TRequestStatus& aStatus)
		{
		
		StartAsyncOperation(ECreateKey, aStatus);
		TRAPD(err, PrepareToCreateKeyL(aKeyStoreIndex, aUsage, aSize, aLabel, aAlgorithm, aAccessType,
									   aStartDate, aEndDate, aStatus));
		if (KErrNone != err)
			{
			Complete(err);
			return;
			}
		
		iKeyInfoOut = &aKeyInfoOut;
		aKeyInfoOut = NULL;
		iKeyStoreManager->CreateKey(aAuthenticationString, aFreshness, iKeyInfo, iStatus);
		SetActive();
		
		}


EXPORT_C void CUnifiedKeyStore::ImportKey(	TInt aKeyStoreIndex, const TDesC8& aKeyData,
								TKeyUsagePKCS15 aUsage, const TDesC& aLabel, 
								TInt aAccessType, TTime aStartDate, TTime aEndDate, 
								const TDesC& aAuthenticationString, TInt aFreshness,
								CCTKeyInfo*& aKeyInfoOut, TRequestStatus& aStatus)
		{
		TBool isEncrypted = TASN1DecPKCS8::IsEncryptedPKCS8Data(aKeyData);
		StartAsyncOperation(isEncrypted ? EImportKeyEncrypted : EImportKey, aStatus);

		ASSERT(!iKeyData);
		iKeyData = aKeyData.Alloc();
		if (!iKeyData)	//	OOM or some other catastrophe
			{
			Complete(KErrNoMemory);
			return;
			}
		
		TRAPD(err, PrepareToCreateKeyL(aKeyStoreIndex, aUsage, 0, aLabel, CCTKeyInfo::EInvalidAlgorithm, aAccessType,
									   aStartDate, aEndDate, aStatus));
		if (KErrNone != err)
			{
			Complete(err);
			return;
			}

		iKeyInfoOut = &aKeyInfoOut;
		aKeyInfoOut = NULL;

		if (isEncrypted)
			{
			iKeyStoreManager->ImportEncryptedKey(*iKeyData, aAuthenticationString, aFreshness, iKeyInfo, iStatus);
			}
		else
			{
			iKeyStoreManager->ImportKey(*iKeyData, aAuthenticationString, aFreshness, iKeyInfo, iStatus);
			}
		SetActive();
		}

EXPORT_C void CUnifiedKeyStore::SetAuthenticationPolicy(	const TCTTokenObjectHandle aHandle, 
															const TDesC& aAuthenticationString,
															TInt aFreshness,					
															TRequestStatus& aStatus)
	{
	StartAsyncOperation(ESetAuthenticationPolicy, aStatus);
		
	ASSERT(!iKeyStoreManager);	
	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		} 
		
	iKeyStoreManager->SetAuthenticationPolicy(aHandle, aAuthenticationString, aFreshness, iStatus);
	SetActive();
		
	}

EXPORT_C void CUnifiedKeyStore::GetAuthenticationPolicy(	const TCTTokenObjectHandle aHandle, 
															HBufC*& aAuthenticationString,
															TInt& aFreshness,					
															TRequestStatus& aStatus)
	{
	StartAsyncOperation(EGetAuthenticationPolicy, aStatus);
		
	ASSERT(!iKeyStoreManager);	
	iKeyStoreManager = FindKeyStoreManager(aHandle);
	if (!iKeyStoreManager)
		{
		Complete(KErrNotFound);
		return;
		} 
		
	iKeyStoreManager->GetAuthenticationPolicy(aHandle, aAuthenticationString, aFreshness, iStatus);
	SetActive();
		
	}

#endif // SYMBIAN_AUTH_SERVER

CUnifiedKeyStore::CUnifiedKeyStore(RFs& aFs)
	:	CActive(EPriorityNormal), iFs(aFs), iState(EIdle)
{//	Currently defaults to always try for key store manager interface
//	This may change (add parameter to NewL for required interface)
	iRequestUid.iUid = KInterfaceKeyStoreManager;
	CActiveScheduler::Add(this);
}

void CUnifiedKeyStore::ConstructL()
{}

void CUnifiedKeyStore::StartAsyncOperation(TState aState, TRequestStatus& aStatus)
	{
	ASSERT(iState == EIdle);
	ASSERT(iOriginalRequestStatus == NULL);
	iState = aState;
	iOriginalRequestStatus = &aStatus;
	aStatus = KRequestPending;
	}

MCTKeyStore* CUnifiedKeyStore::FindKeyStore(const TCTTokenObjectHandle& aHandle)
	{
	for (TInt index = 0 ; index < iKeyStoresHolder.Count() ; ++index)
		{
		MCTTokenInterface* store = iKeyStoresHolder[index]->KeyStore();
		ASSERT(store);
		if (store->Token().Handle() == aHandle.iTokenHandle)
			{
			return static_cast<MCTKeyStoreManager*>(store);
			}
		}
	return NULL;
	}
	
MCTKeyStoreManager* CUnifiedKeyStore::FindKeyStoreManager(const TCTTokenObjectHandle& aHandle)
	{
	for (TInt index = 0 ; index < iKeyStoresHolder.Count() ; ++index)
		{
		MCTTokenInterface* store = iKeyStoresHolder[index]->KeyStore();
		ASSERT(store);
		if (store->Token().Handle() == aHandle.iTokenHandle && iKeyStoresHolder[index]->IsKeyManager())
			{
			return static_cast<MCTKeyStoreManager*>(store);
			}
		}
	return NULL;
	}
	
void CUnifiedKeyStore::RunL()
{
	if (EInitializeGetKeyUserInterfaceFinished != iState &&
		EInitializeGetKeyUserInterface != iState && 
		EInitializeGetToken != iState)
	{
		User::LeaveIfError(iStatus.Int());
	}

	switch (iState)
	{
		case EInitializeGetTokenList:
		{//	Try to get a list of Tokens for each of the Token Types
			iIndexTokenTypes++;
			if (iIndexTokenTypes < iTokenTypes.Count())
			{
				__ASSERT_DEBUG(!iTokenType, User::Panic(KUnifiedKeyStore, EArrayAccessOutOfBounds));
				iTokenType = MCTTokenType::NewL(*iTokenTypes[iIndexTokenTypes], iFs);
				__ASSERT_DEBUG(iTokens.Count()==0, User::Panic(KUnifiedKeyStore, ETokensArrayAlreadyInUse));
				iTokenType->List(iTokens, iStatus);
				iIndexTokens = -1;
				iState = EInitializeGetToken;
			}
			else
			{// We don't need the list of Token Types anymore
				iTokenTypes.ResetAndDestroy();
				iTokenTypes.Close();
				iState = EInitializeFinished;
				TRequestStatus* status = &iStatus;
				User::RequestComplete(status, KErrNone);
			}
			SetActive();
			break;
		}
		case EInitializeGetToken:
		{
			if (iStatus.Int() == KErrHardwareNotAvailable)
				{
				// If the hardware corresponding to this
				// TokenType has been removed then just skip it
				// but DO NOT leave!
				++iIndexTokens;
				iState = EInitializeGetToken;
				TRequestStatus* status = &iStatus;
				User::RequestComplete(status, KErrNone);
				}
            else
				{
				User::LeaveIfError(iStatus.Int());
    			iIndexTokens++;		
		
				if (iIndexTokens < iTokens.Count())
					{
					iTokenType->OpenToken(*iTokens[iIndexTokens], iToken, iStatus);
					iRequestUid.iUid = KInterfaceKeyStoreManager;
					iState = EInitialiseGetKeyManagerInterface;
					}
				else
					{// Don't need the iTokenType anymore
					iTokenType->Release();
					iTokenType = 0;

					iTokens.Close();	// Don't need the list of Tokens anymore
					iState = EInitializeGetTokenList;
					TRequestStatus* status = &iStatus;
					User::RequestComplete(status, KErrNone);
					}
				}
			SetActive();
			break;
		}
		case EInitialiseGetKeyManagerInterface:
		{// First try to get a manager interface to the store, if
		//	unsuccessful, try once to get a user interface
			if (iToken)
			{
				iRequestUid.iUid = KInterfaceKeyStoreManager;			
				iToken->GetInterface(iRequestUid, iTokenInterface, iStatus);
				iState = EInitializeGetKeyUserInterface;
				SetActive();			
			}
			else
			{//	No token
				User::Leave(KErrNotReady);
			}
			break;
		}
		case EInitializeGetKeyUserInterface:
		{//	Did we get a manager interface?
			if (iStatus==KErrNoMemory)
			{
				User::Leave(KErrNoMemory);
			}

			if (iRequestUid.iUid==KInterfaceKeyStoreManager)
			{
				if (KErrNone==iStatus.Int())
				{//	Success! Store it and finish up
					CKeyStoreIF* keyStore = new (ELeave) CKeyStoreIF(iTokenInterface, ETrue);
					CleanupStack::PushL(keyStore);
					User::LeaveIfError(iKeyStoresHolder.Append(keyStore));
					CleanupStack::Pop(keyStore);

					iTokenInterface = 0;
					iToken->Release();
					iToken = 0;
					iState = EInitializeGetToken;
					TRequestStatus* status = &iStatus;
					User::RequestComplete(status, KErrNone);
				}
				else
				{//	No luck getting a manager, so try getting a user
					iRequestUid.iUid = KInterfaceKeyStore;			
					iToken->GetInterface(iRequestUid, iTokenInterface, iStatus);
					iState = EInitializeGetKeyUserInterfaceFinished;					
				}
			}
			else if (iRequestUid.iUid==KInterfaceKeyStore) 
			{//	We were trying for user IF								
				if (iStatus==KErrNone)
				{
					if (iToken)
					{
						iRequestUid.iUid = KInterfaceKeyStore;			
						iToken->GetInterface(iRequestUid, iTokenInterface, iStatus);
						iState = EInitializeGetKeyUserInterfaceFinished;	
					}
					else
					{
						User::Leave(KErrNotReady);
					}
				}
				else
				{//	Couldn't even get a user IF
					User::Leave(iStatus.Int());
				}				
			}

			SetActive();
			break;
		}
		case EInitializeGetKeyUserInterfaceFinished:
		{
			if (iStatus==KErrNone)
			{
				CKeyStoreIF* keyStore = new (ELeave) CKeyStoreIF(iTokenInterface, EFalse);
				CleanupStack::PushL(keyStore);
				User::LeaveIfError(iKeyStoresHolder.Append(keyStore));
				CleanupStack::Pop(keyStore);

				iTokenInterface = 0;
				iToken->Release();
				iToken = 0;
				iState = EInitializeGetToken;
				TRequestStatus* status = &iStatus;
				User::RequestComplete(status, KErrNone);
			}
			else if (iStatus == KErrNoMemory)
			{
				User::Leave(KErrNoMemory);
			}
			else
			{
				iState = EInitializeGetToken;
				TRequestStatus* status = &iStatus;
				User::RequestComplete(status,iStatus.Int());
			}
			
			SetActive();
			break;
		}
		case EInitializeFinished:
			Complete(KErrNone);
			break;
			
		case EList:
		{//	iIndex has been initialized in List function
			++iIndex;
			if (iIndex < iKeyStoresHolder.Count())
				{
				iKeyStore = static_cast<MCTKeyStore*>(iKeyStoresHolder[iIndex]->KeyStore());
				ASSERT(iKeyStore);
				iKeyStore->List(*iKeyInfos, *iFilter, iStatus);
				SetActive();
				}
			else
				{
				Complete(KErrNone);
				}
			break;
		}
		
	    case EGetKeyInfo:
			Complete(KErrNone);
			break;
			
		case ECreateKey:
			*iKeyInfoOut = iKeyInfo;
			iKeyInfo = NULL; // Release ownership
			Complete(KErrNone);
			break;
			
		case EImportKey:
		case EImportKeyEncrypted:
			*iKeyInfoOut = iKeyInfo;
			iKeyInfo = NULL; // Release ownership
			Complete(KErrNone);
			break;
			
		case EExportKey:
		case EExportEncryptedKey:
			Complete(KErrNone);
			break;		

	    case ERelock:
			++iIndex;
			
			// Find next key store manager
			while (iIndex < iKeyStoresHolder.Count() && !iKeyStoresHolder[iIndex]->IsKeyManager())
				++iIndex;
			
			if (iIndex < iKeyStoresHolder.Count())
				{
				iKeyStoreManager = static_cast<MCTKeyStoreManager*>(iKeyStoresHolder[iIndex]->KeyStore());
				ASSERT(iKeyStoreManager);
				iKeyStoreManager->Relock(iStatus);
				SetActive();
				}
			else
				{
				Complete(KErrNone);
				}
			break;

	    case ESetPassphraseTimeout:
			++iIndex;
			
			// Find next key store manager
			while (iIndex < iKeyStoresHolder.Count() && !iKeyStoresHolder[iIndex]->IsKeyManager())
				++iIndex;
			
			if (iIndex < iKeyStoresHolder.Count())
				{
				iKeyStoreManager = static_cast<MCTKeyStoreManager*>(iKeyStoresHolder[iIndex]->KeyStore());
				ASSERT(iKeyStoreManager);
				iKeyStoreManager->SetPassphraseTimeout(iNewTimeout, iStatus);
				SetActive();
				}
			else
				{
				Complete(KErrNone);
				}
			break;

	    case EOpen:
		case EExportPublic:
	    case EDeleteKey:
	    case ESetUsePolicy:
	    case ESetManagementPolicy:
	    case EGetAuthenticationPolicy:
	    case ESetAuthenticationPolicy:
			Complete(KErrNone);
			break;
		default:
			User::Panic(KUnifiedKeyStore, EUnrecognisedState);
			break;
	}
}

TInt CUnifiedKeyStore::RunError(TInt aError)
	{
	Complete(aError); 
	return KErrNone;
	}

void CUnifiedKeyStore::DoCancel()
	{
	// If the current state is the last state involved in handling a request, we
	// check to see if we have already been completed - in this case we can
	// simply complete the client with iStatus (this may be KErrNone).  If we
	// have not we cancel the outstanding request and pass the resulting iStatus
	// back to the client - this too may indicate a successful completion if the
	// cancel arrived after the request was executed.
	//
	// For more complex cases, where there are more states to go through before
	// we finish servicing the client request, we cancel any outstanding
	// request, and return KErrCancel to the client.

	switch (iState)
		{
		case EInitializeFinished:
		case EGetKeyInfo:
		case ECreateKey:
		case EImportKey:
		case EImportKeyEncrypted:
		case EExportKey:
		case EExportEncryptedKey:
	    case EOpen:
		case EExportPublic:
	    case EDeleteKey:
	    case ESetUsePolicy:
	    case ESetManagementPolicy:
			if (iStatus == KRequestPending)
				{
				// Attempt to cancel outstanding request and pass status back to
				// client
				CancelOutstandingRequest();
				Complete(iStatus.Int());
				}
			else
				{
				// We've already been completed - call RunL() to process results
				// and complete client
				TRAPD(err, RunL());
				if (err != KErrNone)
					{
					RunError(err);
					}
				}
			break;

		default:
			CancelOutstandingRequest();
			Complete(KErrCancel);
			break;
		}
	}

void CUnifiedKeyStore::CancelOutstandingRequest()
	{
	switch (iState)
		{
		case EInitializeGetTokenList:
		case EInitializeGetToken:
		case EInitialiseGetKeyManagerInterface:
		case EInitializeGetKeyUserInterface:
		case EInitializeGetKeyUserInterfaceFinished:
		case EInitializeFinished:
			// Don't have to cancel initialisation stuff - this happens when we
			// release the objects in Cleanup().
			iStatus = KErrCancel;
			break;

		case EList:
			if (iKeyStore)
				{
				iKeyStore->CancelList();
				}
			break;

		case EGetKeyInfo:
			ASSERT(iKeyStore);
			iKeyStore->CancelGetKeyInfo();
			break;

		case EOpen:
			ASSERT(iKeyStore);
			iKeyStore->CancelOpen();
			break;

		case EExportPublic:
			ASSERT(iKeyStore);
			iKeyStore->CancelExportPublic();
			break;			

		case ECreateKey:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelCreateKey();
			break;

		case EImportKey:
		case EImportKeyEncrypted:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelImportKey();
			break;
			
		case EExportKey:
		case EExportEncryptedKey:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelExportKey();
			break;
			
		case EDeleteKey:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelDeleteKey();
			break;

		case ERelock:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelRelock();
			break;

		case ESetPassphraseTimeout:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelSetPassphraseTimeout();
			break;
		
	    case ESetUsePolicy:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelSetUsePolicy();
			break;

	    case ESetManagementPolicy:
			ASSERT(iKeyStoreManager);
			iKeyStoreManager->CancelSetManagementPolicy();
			break;

		default:
			User::Panic(KUnifiedKeyStore, EUnrecognisedState);
			break;
		}
	}


void CUnifiedKeyStore::Complete(TInt aError)
	{
	Cleanup();
	if (iOriginalRequestStatus)
		{
		User::RequestComplete(iOriginalRequestStatus, aError);
		}
	}

void CUnifiedKeyStore::Cleanup()
	{
	// If we have a key info, we want to release it
	if (iKeyInfo)
	{
		iKeyInfo->Release();
		iKeyInfo = NULL;
	}

	delete iKeyData;
	iKeyData = NULL;

	delete iFilter;
	iFilter = NULL;

	delete iPbeParams;
	iPbeParams = NULL;

	iTokenTypes.Close();

	if (iTokenType)
		{
		iTokenType->Release();
		iTokenType = 0;
		}

	iTokens.Close();

	if (iToken)
		{
		iToken->Release();
		iToken = 0;
		}

	if (iTokenInterface)
		{
		iTokenInterface->Release();
		iTokenInterface = 0;
		}
	
	iKeyInfoOut = NULL;
	iKeyStore = NULL;
	iKeyStoreManager = NULL;
	
	iState = EIdle;
	}

CUnifiedKeyStore::CKeyStoreIF::CKeyStoreIF(MCTTokenInterface* aKeyStore, TBool aIsKeyManager)
:	iKeyStore(aKeyStore), iIsKeyManager(aIsKeyManager)
{}

CUnifiedKeyStore::CKeyStoreIF::~CKeyStoreIF()
{
	if (iKeyStore)
	{
		iKeyStore->Release();
		iKeyStore = NULL;
	}
}

