cryptoservices/filebasedcertificateandkeystores/test/tcryptotokenhai/tcryptotokenhai.cpp
changeset 15 da2ae96f639b
child 19 cd501b96611d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cryptoservices/filebasedcertificateandkeystores/test/tcryptotokenhai/tcryptotokenhai.cpp	Mon Oct 12 10:17:04 2009 +0300
@@ -0,0 +1,622 @@
+/*
+* Copyright (c) 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: 
+* This class implements the reference Crypto Token Hardware Abstraction 
+* Interface (HAI). It is just intended to show how operations using 
+* device keys can be performed using crypto token framework. In the 
+* real world scenario, this HAI should be replaced by device drivers 
+* by the licensees. In such a case, all the operations performed by 
+* the replacing class would be performed in Kernel Space.
+*
+*/
+
+
+#include "tcryptotokenhai.h"
+#include "tkeydetails.h"
+#include "cryptosignatureapi.h"
+#include "keys.h"
+
+#include <cryptospi/cryptoparams.h>
+#include <cryptospi/cryptospidef.h>
+
+EXPORT_C CCryptoTokenHai* CCryptoTokenHai::NewLC(MCTToken* aToken)
+	{
+	CCryptoTokenHai* instance = new(ELeave) CCryptoTokenHai(*aToken);
+	CleanupStack::PushL(instance);
+	instance->ConstructL();
+	return instance;
+	}
+
+EXPORT_C CCryptoTokenHai* CCryptoTokenHai::NewL(MCTToken* aToken)
+	{
+	CCryptoTokenHai* instance = CCryptoTokenHai::NewLC(aToken);
+	CleanupStack::Pop(instance);
+	return instance;
+	}
+
+void CCryptoTokenHai::ConstructL()
+	{
+	User::LeaveIfError(iFs.Connect());
+	OpenStoreL();
+	}
+
+CCryptoTokenHai::CCryptoTokenHai(MCTToken& aToken)
+	:iToken(aToken)
+	{}
+
+EXPORT_C CCryptoTokenHai::~CCryptoTokenHai()
+	{
+	if(iFileStore)
+        {
+        CompactStore();
+        delete iFileStore;
+        }
+	
+	iFs.Close();
+	iKeys.ResetAndDestroy();
+    iKeys.Close();
+	}
+
+/**
+ * Performs the decryption operation.
+ * 
+ * This API gets called when the decryption is supposed to be done in  
+ * the hardware.
+ * 
+ * @param aHandle The key handle
+ * @param aCiphertext The cipher text. This is not being used presently 
+ * due to decryption logic used in this function.
+ * @param aPlainText Output param. The decrypted plain text. Ownership 
+ * of the pointer lies with the caller.
+ * 
+ * @leave This function can leave with following error codes:-
+ * - KErrNotFound - If the key corresponding to given handle is not 
+ * found.
+ * - Any other error code returned by AllocL().
+ *
+ * @note This function does not actually implement ECC decryption. It 
+ * just intends to show that the key is with this class and it can 
+ * do actual ECC decryption here. This function just returns the 
+ * private key as decrypted text. The caller can verify the decryption 
+ * by ensuring that test case has same public and private keys and then 
+ * comparing the decrypted text with public key. 
+ */
+EXPORT_C void CCryptoTokenHai::DecryptL(	TInt aHandle,	
+											const TDesC8& /* aCiphertext */,
+											HBufC8*& aPlainText )
+	{
+    TInt keyIndex = KeyPresent(aHandle);
+    if(keyIndex == KErrNotFound)
+        {
+        User::Leave(KErrNotFound);
+        }
+    
+    ExportPrivateKeyL(aHandle, aPlainText);
+	}
+
+/**
+ * Performs the signing operation.
+ * 
+ * This API gets called when the signing is supposed to be done inside 
+ * the hardware.
+ * 
+ * @param aHandle The key handle
+ * @param aPlaintext The text which has to be signed. This is not being 
+ * used due to signing logic used in this function.
+ * @param aSignature Output param. The signature in HBufC8 format.  
+ * Ownership of the pointer lies with the caller. This should be 
+ * converted to CCryptoParams by the crypto token reference plugin. 
+ * 
+ * @leave This function can leave with following error codes:-
+ * - KErrNotFound - If the key corresponding to given handle is not 
+ * found.
+ * - Any other error code returned by AllocL().
+ * 
+ * @note This function does not actually implement ECC signing. It 
+ * just intends to show that the key is with this class and it can 
+ * do actual ECC signing here. Currently this function just returns 
+ * the private key as output signature. The caller can verify the 
+ * signature by ensuring that test case has same public and private 
+ * keys and then comparing the signature with public key.
+ */
+EXPORT_C void CCryptoTokenHai::SignL( 	TInt aHandle,
+										const TDesC8& /* aPlaintext */,
+										HBufC8*& aSignature )
+	{
+	TInt keyIndex = KeyPresent(aHandle);
+	if(keyIndex == KErrNotFound)
+	    {
+	    User::Leave(KErrNotFound);
+	    }
+	
+	ExportPrivateKeyL(aHandle, aSignature);
+	}
+
+/**
+ * Returns the index of the key whose handle is given.
+ * 
+ * @param aHandle Handle of the key. This is used to search the key.
+ * 
+ * @return index of the key if search is successful, KErrNotFound 
+ * otherwise.
+ */
+EXPORT_C TInt CCryptoTokenHai::KeyPresent( TInt aHandle )
+	{
+	int keysCount = iKeys.Count();
+	for(TInt i=0; i < keysCount; ++i)
+		{
+		if(iKeys[i]->Handle() == aHandle)
+			{
+			return i;
+			}
+		}
+	return KErrNotFound;
+	}
+
+/**
+ * Extracts the private key.
+ * 
+ * @param aHandle Handle of the private key to be extracted.
+ * @param aKey Output Parameter. Stores the private key on success. 
+ * Ownership of pointer is with the caller.
+ * 
+ * @leave Following leave codes possible:-
+ * - Any leave code returned by AllocL().
+ * - KErrNotFound - If key corresponding to the given handle is not 
+ * found.
+ * 
+ * @note In the actual implementation, licensees should ensure that 
+ * this function can be called only in Kernel space. In the reference 
+ * implementation, this function gets called only by CCryptoSpiHai, 
+ * which is assumed to operate in kernel space. This would ensure that 
+ * the private key always stays inside the hardware.
+ */
+EXPORT_C void CCryptoTokenHai::ExportPrivateKeyL( TInt aHandle, HBufC8*& aKey )
+	{
+	int keysCount = iKeys.Count();
+	for(int i = 0; i < keysCount; ++i)
+		{
+		if(iKeys[i]->Handle() == aHandle)
+			{
+			aKey = iKeys[i]->PrivateKey()->AllocL();
+			return;
+			}
+		}
+	User::Leave(KErrNotFound);
+	}
+
+/**
+ * Extracts the public key.
+ * 
+ * @param aHandle Handle of the public key to be extracted.
+ * @param aKey Output Parameter. Stores the public key on success.
+ * Ownership of pointer is with the caller.
+ * 
+ * @leave Following leave codes possible:-
+ * - Any leave code returned by AllocL().
+ * - KErrNotFound - If key corresponding to the given handle is not 
+ * found.
+ */
+EXPORT_C void CCryptoTokenHai::ExportPublicKeyL( TInt aHandle, HBufC8*& aKey )
+    {
+    int keysCount = iKeys.Count();
+    for(int i = 0; i < keysCount; ++i)
+        {
+        if(iKeys[i]->Handle() == aHandle)
+            {
+            aKey = iKeys[i]->PublicKey()->AllocL();
+            return;
+            }
+        }
+    User::Leave(KErrNotFound);
+    }
+
+/**
+ * Stores the key with given details.
+ * 
+ * @param aLabel Label of the key.
+ * @param aPrivateKey Private component of the key.
+ * @param aPublicKey Public component of the key.
+ * 
+ * @leave Following leave codes possible:-
+ * - KErrAlreadyExists If there is already a key with the inputted
+ * label.
+ * - Any other leave code returned by NewL() or AppendL().
+ *  
+ * @note In the present reference implementation this function is not 
+ * being used, since device keys are pre-provisioned by the licensees. 
+ * Hence licensees may decide not to implement this function in their 
+ * real implementation. 
+ */
+EXPORT_C void CCryptoTokenHai::ImportKeyL(const TDesC& aLabel, 
+        const TDesC8& aPrivateKey, const TDesC8& aPublicKey)
+	{
+	int keysCount = iKeys.Count();
+	for(int i = 0; i < keysCount; ++i)
+		{
+		if(iKeys[i]->Label() == aLabel)
+			{
+			User::Leave(KErrAlreadyExists);
+			}
+		}
+	CKeyDetails* keyDetails = CKeyDetails::NewL(keysCount+1,aLabel,aPrivateKey,aPublicKey);
+	iKeys.AppendL(keyDetails);
+	}
+
+/**
+ * Populates the string containing full RAM path of file containing 
+ * keys.
+ */
+void CCryptoTokenHai::MakePrivateFilenameL(RFs& aFs, const TDesC& aLeafName, TDes& aNameOut)
+    {
+    aNameOut.SetLength(0);  
+    aNameOut.Append(RFs::GetSystemDriveChar());
+
+    aNameOut.Append(':');
+
+    // Get private path
+    TBuf<20> privatePath;
+    User::LeaveIfError(aFs.PrivatePath(privatePath));
+    aNameOut.Append(privatePath);
+    
+    aNameOut.Append(aLeafName);
+    }
+
+/**
+ * Creates the corresponding directory, if it does not exist.
+ */
+void CCryptoTokenHai::EnsurePathL(RFs& aFs, const TDesC& aFile)
+    {
+    TInt err = aFs.MkDirAll(aFile);
+    if (err != KErrNone && err != KErrAlreadyExists)
+        {
+        User::Leave(err);
+        }
+    }
+
+/**
+ * Populates the string containing full ROM path of the keys file.
+ */
+void CCryptoTokenHai::MakePrivateROMFilenameL(RFs& aFs, const TDesC& aLeafName, TDes& aNameOut)
+    {
+    _LIT(KFileStoreROMDrive, "Z:");
+    
+    aNameOut.Copy(KFileStoreROMDrive);
+
+    // Get private path
+    TBuf<20> privatePath;
+    User::LeaveIfError(aFs.PrivatePath(privatePath)); 
+    aNameOut.Append(privatePath);
+    aNameOut.Append(aLeafName);
+    }
+
+/**
+ * Copies the contents of source file to destination file.
+ * 
+ * This is typically used to copy the keys file from ROM to RAM.
+ */
+void CCryptoTokenHai::CopyL(RFs& aFs, const TDesC& aSouce, const TDesC& aDest)
+    {
+    RFileReadStream in;
+    User::LeaveIfError(in.Open(aFs, aSouce, EFileRead | EFileShareReadersOnly));
+    CleanupClosePushL(in);
+
+    RFileWriteStream out;
+    User::LeaveIfError(out.Replace(aFs, aDest, EFileWrite | EFileShareExclusive));
+    CleanupClosePushL(out);
+
+    in.ReadL(out);  
+    CleanupStack::PopAndDestroy(2, &in);
+    }
+
+/**
+ * Keys corresponding to this store are present in hwkeys.dat. 
+ * In the production code written by licensees, this would be the path 
+ * where device keys are stored.
+ */
+_LIT(KKeyStoreFilename,"hwkeys.dat");
+
+/**
+ * Opens a store containing hardware keys.
+ * 
+ * This function uses the following logic to open the store:-
+ * -# Try to open the store from the private directory.
+ * -# If this fails copy the file from ROM to RAM.
+ * -# If both fail, create your own keys store from scratch.
+ */
+void CCryptoTokenHai::OpenStoreL()
+	{
+	TFileName fullPath;
+	MakePrivateFilenameL(iFs, KKeyStoreFilename, fullPath);
+
+	EnsurePathL(iFs, fullPath);
+	TRAPD(result, OpenStoreInFileL(fullPath));
+
+	if (result == KErrInUse  ) 
+		{		
+		// Cannot access the file now. Abort rather than wiping the keystore.
+		User::Leave(result); 
+		}
+	
+	if (result != KErrNone )
+		{		
+		/*
+		 * Not yet opened a valid store, either no file to be found, or 
+		 * no valid store in it. Copy the original one stored in the 
+		 * ROM.
+		 */
+		TRAPD(result2, CopyStoreFromROML(fullPath, result));
+				
+		if (KErrNone != result2)
+			{
+			/*
+			 * We tried to copy the keystore from ROM. For some reason this
+			 * failed and we still cannot open the file. Create a new one from
+			 * scratch.
+			 */ 
+			CreateStoreInFileL(fullPath);
+			}
+		}
+
+	}
+
+/**
+ * Copies the key store file from ROM to RAM.
+ */
+void CCryptoTokenHai::CopyStoreFromROML(const TDesC& fullPath, TInt result)
+    {
+    if (result != KErrNotFound)
+        {
+        // Wipe the keystore if we can't open it (it's corrupt anyway)
+        User::LeaveIfError(iFs.Delete(fullPath));
+        }
+
+    TFileName romPath;
+    MakePrivateROMFilenameL(iFs, KKeyStoreFilename, romPath);
+
+    // Copy data from rom and open it   
+    CopyL(iFs, romPath, fullPath);
+    OpenStoreInFileL(fullPath);
+    }
+
+/**
+ * Opens a store from the given file.
+ */
+void CCryptoTokenHai::OpenStoreInFileL(const TDesC& aFile)
+	{
+	RFile file;
+	User::LeaveIfError(file.Open(iFs, aFile, EFileRead | EFileWrite | EFileShareAny));
+	CleanupClosePushL(file);
+    delete iFileStore;
+    iFileStore = NULL;
+
+	iFileStore = CPermanentFileStore::FromL(file);
+    // iFileStore takes ownership of file now
+	CleanupStack::Pop(&file);
+	
+    // Get the salt, root and manager TStreamIds
+    iRootStreamId = iFileStore->Root();
+    if (iRootStreamId == KNullStreamId)
+        {
+        User::Leave(KErrCorrupt);
+        }
+    RStoreReadStream rootStream;
+    rootStream.OpenLC(*iFileStore, iRootStreamId);
+    ReadKeysFromStoreL();
+    CleanupStack::PopAndDestroy(&rootStream);
+    }
+
+/**
+ * Creates a keys store in RAM from scratch.
+ * 
+ * @note This function should never get called as hwkeys.dat should be 
+ * always present in ROM. If this function somehow gets called, it 
+ * will create a hwkeys.dat file from scratch. However, this file would 
+ * not contain any keys and tests would not pass.
+ */
+void CCryptoTokenHai::CreateStoreInFileL(const TDesC& aFile)
+	{
+	TInt r = iFs.MkDirAll(aFile);
+	if ( (r!=KErrNone) && (r!=KErrAlreadyExists) )
+		User::Leave(r);
+
+    delete iFileStore;
+    iFileStore = NULL;
+	iFileStore = CPermanentFileStore::ReplaceL(iFs, aFile, EFileRead | EFileWrite | EFileShareExclusive);
+	iFileStore->SetTypeL(KPermanentFileStoreLayoutUid);
+
+	TCleanupItem cleanupStore(RevertStore, iFileStore);
+	CleanupStack::PushL(cleanupStore);
+	
+	// Create root stream - just contains id of info stream
+	RStoreWriteStream rootStream;
+	iRootStreamId = rootStream.CreateLC(*iFileStore);
+	iFileStore->SetRootL(iRootStreamId);
+	WriteKeysToStoreL(rootStream);
+	iFileStore->CommitL();
+	CleanupStack::PopAndDestroy(&rootStream);
+	CleanupStack::Pop(); // cleanupStore
+	}
+
+/**
+ * Copies the keys stored in the instance to inputted write stream.
+ * 
+ * This invokes the CKeyDetails::ExternalizeL() function.
+ */
+void CCryptoTokenHai::WriteKeysToStoreL(RStoreWriteStream& aRootStream)
+	{
+	TInt keyCount = iKeys.Count();
+	aRootStream.WriteInt32L(keyCount);
+
+	for (TInt index = 0; index < keyCount; index++)
+		{
+		aRootStream << *iKeys[index];
+		}
+	aRootStream.CommitL();
+	}
+
+/**
+ * Copies the keys present in the read store to instance of class.
+ * 
+ * This eventually invokes the CKeyDetails::InternalizeL() function.
+ */
+void CCryptoTokenHai::ReadKeysFromStoreL()
+	{
+	RStoreReadStream rootStream;
+	
+	rootStream.OpenLC(*iFileStore, iRootStreamId);
+	TInt keyCount = rootStream.ReadInt32L();
+
+	for (TInt index = 0; index < keyCount; index++)
+		{
+		CKeyDetails* keyDetails = CKeyDetails::NewL(rootStream);
+		iKeys.Append(keyDetails);
+		}
+	CleanupStack::PopAndDestroy(&rootStream);
+	}
+
+/**
+ * This is a cleanup item that reverts the store.
+ */
+void CCryptoTokenHai::RevertStore(TAny* aStore)
+	{
+	CPermanentFileStore* store = reinterpret_cast<CPermanentFileStore*>(aStore);
+	TRAP_IGNORE(store->RevertL());
+	}
+
+/**
+ * Compacts the store.
+ */
+void CCryptoTokenHai::CompactStore()
+    {
+    ASSERT(iFileStore);
+    TRAP_IGNORE(iFileStore->ReclaimL(); iFileStore->CompactL());
+    }
+
+/**
+ * Populates the list of keys based on the input filter.
+ * 
+ * @param aFilter Set of conditions to be used to decide which keys 
+ * should be listed
+ * @param aKeys Output param. Contains the array of keys which fulfil 
+ * criteria mentioned in filter. Caller should take responsibility of 
+ * this array.
+ * 
+ * @leave Any of the system wide error codes.
+ * 
+ * @note Though Crypto Token HAI internally operates in CKeyDetails, 
+ * this function returns CCTKeyInfo array.
+ */
+EXPORT_C void CCryptoTokenHai::ListL(const TCTKeyAttributeFilter&  aFilter , 
+                RPointerArray<CCTKeyInfo>& aKeys) const
+    {
+    TInt count = iKeys.Count();
+    for(TInt index = 0 ;index < count; ++ index)
+    	{
+    	const CKeyDetails* keyDetails = iKeys[index];
+    	
+    	if(KeyMatchesFilterL(*keyDetails,aFilter))
+    		{
+			MCTAuthenticationObject* authObject = NULL;
+			HBufC8* attribute = keyDetails->PKCS8AttributeSet().AllocLC();
+			HBufC* label = keyDetails->Label().AllocLC();
+			
+			CCTKeyInfo* keyInfo = CCTKeyInfo::NewL(
+					keyDetails->ID(),keyDetails->Usage(),keyDetails->Size(),
+					authObject,label,iToken,keyDetails->Handle(),keyDetails->UsePolicy(),
+					keyDetails->ManagementPolicy(),keyDetails->Algorithm(),keyDetails->AccessType(),
+					keyDetails->Native(),keyDetails->StartDate(),keyDetails->EndDate(),attribute);
+			
+			CleanupStack::Pop(2, attribute); // label
+			CleanupReleasePushL(*keyInfo);
+						
+			User::LeaveIfError(aKeys.Append(keyInfo));
+			CleanupStack::Pop(keyInfo); 
+			
+			}
+    	}
+    	
+    }
+
+/**
+ * Takes in a filter and key details and decides if key fulfils the 
+ * filter criteria.
+ * 
+ * @param aInfo The Key Details
+ * @param aFilter Filter specifying the conditions to be satisfied for 
+ * listing the keys.
+ * 
+ * @retval ETrue if key satisfies the conditions specified in filter
+ * @retval EFalse otherwise.
+ * 
+ * @leave KErrArgument If there is an issue in policy filter.
+ */
+TBool CCryptoTokenHai::KeyMatchesFilterL(const CKeyDetails& aInfo,
+										   const TCTKeyAttributeFilter& aFilter) const
+	{
+		
+	if (aFilter.iKeyId.Length() && aFilter.iKeyId != aInfo.ID())
+		{
+		return EFalse;
+		}
+
+	if (aFilter.iUsage != EPKCS15UsageAll)
+		{
+		if ((aInfo.Usage() & aFilter.iUsage) == 0)
+			return EFalse;
+		}
+
+	if (aFilter.iKeyAlgorithm != CCTKeyInfo::EInvalidAlgorithm && 
+		aFilter.iKeyAlgorithm != aInfo.Algorithm())
+		{
+		return EFalse;
+		}
+	
+	switch (aFilter.iPolicyFilter)
+		{
+		case TCTKeyAttributeFilter::EAllKeys:
+			// All keys pass
+			break;
+			   
+		case TCTKeyAttributeFilter::EUsableKeys:
+			if (!aInfo.UsePolicy().CheckPolicy(RThread()))
+				{
+				return EFalse;
+				}
+			break;
+			
+		case TCTKeyAttributeFilter::EManageableKeys:
+			if (!aInfo.ManagementPolicy().CheckPolicy(RThread()))
+				{
+				return EFalse;
+				}
+			break;
+
+		case TCTKeyAttributeFilter::EUsableOrManageableKeys:
+			if (!aInfo.UsePolicy().CheckPolicy(RThread()) &&
+				!aInfo.ManagementPolicy().CheckPolicy(RThread()))
+				{
+				return EFalse;
+				}
+			break;
+						
+		default:
+			User::Leave(KErrArgument);
+		}
+
+	return ETrue;
+	}
+
+
+