cryptoservices/filebasedcertificateandkeystores/test/tcryptotokenhai/tcryptotokenhai.cpp
/*
* 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:
// As this key store implementation is a hardware simulation,
// the support for managing through software interface has been diabled.
return EFalse;
case TCTKeyAttributeFilter::EUsableOrManageableKeys:
if (!aInfo.UsePolicy().CheckPolicy(RThread()) &&
!aInfo.ManagementPolicy().CheckPolicy(RThread()))
{
return EFalse;
}
break;
default:
User::Leave(KErrArgument);
}
return ETrue;
}