sl@0: /*
sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: * All rights reserved.
sl@0: * This component and the accompanying materials are made available
sl@0: * under the terms of the License "Eclipse Public License v1.0"
sl@0: * which accompanies this distribution, and is available
sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: *
sl@0: * Initial Contributors:
sl@0: * Nokia Corporation - initial contribution.
sl@0: *
sl@0: * Contributors:
sl@0: *
sl@0: * Description: 
sl@0: *
sl@0: */
sl@0: 
sl@0: 
sl@0: /**
sl@0:  @file
sl@0:  @internalComponent
sl@0:  @released
sl@0: */
sl@0: #include "h4cipherimpl.h"
sl@0: 
sl@0: #include <e32def.h>
sl@0: #include <cryptostrength.h>
sl@0: #include <cryptospi/cryptospidef.h>
sl@0: #include "keys.h"
sl@0: #include <cryptospi/plugincharacteristics.h>
sl@0: #include "pluginconfig.h"
sl@0: #include <cryptopanic.h>
sl@0: #include <securityerr.h>
sl@0: #include "keyhandle.h"
sl@0: 
sl@0: using namespace HwCrypto;
sl@0: 
sl@0: #define BytesToBits(byteCount) (byteCount*8)
sl@0: 
sl@0: //
sl@0: // Implementation of Symmetric Cipher class
sl@0: //
sl@0: CH4CipherImpl::CH4CipherImpl(TUint8 aBlockBytes,
sl@0: 							 TUid aCryptoMode,
sl@0: 							 TUid aOperationMode,
sl@0: 							 TUid aPaddingMode) 
sl@0: 	: iBlockBytes(aBlockBytes),
sl@0: 	  iCryptoMode(aCryptoMode),
sl@0: 	  iOperationMode(aOperationMode),
sl@0: 	  iPaddingMode(aPaddingMode)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::ConstructL(const CKey& aKey) 
sl@0: 	{
sl@0: 	SetKeyL(aKey);		
sl@0: 	SetOperationModeL(iOperationMode);
sl@0: 	SetCryptoModeL(iCryptoMode);	
sl@0: 	SetPaddingModeL(iPaddingMode);
sl@0: 	
sl@0: 	iPartialBlock.ReAllocL(iBlockBytes);
sl@0: 	iPaddingBlock.ReAllocL(iBlockBytes);
sl@0: 	
sl@0: 	iIv.ReAllocL(iBlockBytes);
sl@0: 	//	iIv.SetLength(iBlockBytes);
sl@0: 	//	TRandom::RandomL(iIv);
sl@0: 	}
sl@0: 	
sl@0: CExtendedCharacteristics* CH4CipherImpl::CreateExtendedCharacteristicsL()
sl@0: 	{
sl@0: 	// All Symbian software plug-ins have unlimited concurrency, cannot be reserved
sl@0: 	// for exclusive use and are not CERTIFIED to be standards compliant.		
sl@0: 
sl@0: 	//	return CExtendedCharacteristics::NewL(KMaxTInt, iStandardsConformance, EFalse);	
sl@0: 	return 0;	
sl@0: 	}
sl@0: 
sl@0: CH4CipherImpl::~CH4CipherImpl()
sl@0: 	{			
sl@0: 	delete iPadding;
sl@0: 	iIv.Close();
sl@0: 	iPartialBlock.Close();
sl@0: 	iPaddingBlock.Close();	
sl@0: 
sl@0: 	delete iKey;	
sl@0: 	iStandardsConformance.Close();	
sl@0: 	}
sl@0: 		
sl@0: void CH4CipherImpl::Close()
sl@0: 	{
sl@0: 	delete this;
sl@0: 	}
sl@0: 	
sl@0: TAny* CH4CipherImpl::GetExtension(TUid /*aExtensionId*/) 
sl@0: 	{
sl@0: 	return 0;
sl@0: 	}
sl@0: 	
sl@0: void CH4CipherImpl::GetCharacteristicsL(const TAny*& aPluginCharacteristics)
sl@0: 	{
sl@0: 	TInt numCiphers = sizeof(KSymmetricCipherCharacteristics)/sizeof(TSymmetricCipherCharacteristics*);
sl@0: 	TInt32 implUid = ImplementationUid().iUid;
sl@0: 	for (TInt i = 0; i < numCiphers; ++i)
sl@0: 		{
sl@0: 		if (KSymmetricCipherCharacteristics[i]->cmn.iImplementationUID == implUid)
sl@0: 			{
sl@0: 			aPluginCharacteristics = KSymmetricCipherCharacteristics[i];
sl@0: 			break;
sl@0: 			}
sl@0: 		}	
sl@0: 	}
sl@0: 
sl@0: 
sl@0: void CH4CipherImpl::Reset()
sl@0: 	{
sl@0: 	iPartialBlock.Zero();
sl@0: 	iPaddingBlock.Zero();
sl@0: 	
sl@0: 	// Reconfigure h/w based on iCryptoMode/iOperationMode/iKey/iIv
sl@0: 	TRAP_IGNORE(DoSetupL());
sl@0: 	iNeedToSetupHw = EFalse;
sl@0: 	}	
sl@0: 
sl@0: 
sl@0: TInt CH4CipherImpl::GetKeyStrength() const
sl@0: 	{
sl@0: 	return BytesToBits(iKey->Length());
sl@0: 	}
sl@0: 	
sl@0: HBufC8* CH4CipherImpl::ExtractKeyDataLC(const CKey& aKey) const
sl@0: 	{
sl@0: 	const TDesC8& keyContent = aKey.GetTDesC8L(KSymmetricKeyParameterUid);
sl@0: 	return keyContent.AllocLC();
sl@0: 	}
sl@0: 
sl@0: TInt CH4CipherImpl::KeySize() const
sl@0: 	{
sl@0: 	// return key size in BITS
sl@0: 	return BytesToBits(iKeyBytes);
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::SetKeyL(const CKey& aKey)
sl@0: 	{
sl@0: 	HBufC8* key = ExtractKeyDataLC(aKey);
sl@0: 	TInt keyLength(key->Length());
sl@0: 	
sl@0: 	const TKeyProperty &keyProps = aKey.KeyProperty();
sl@0: 
sl@0: 	if(keyProps.iKeyType != KSymmetricKeyUid)
sl@0: 		{
sl@0: 		User::Leave(KErrArgument);
sl@0: 		}
sl@0: 
sl@0: 	if(keyProps.iKeyAttribute == KNonEmbeddedKeyUid)
sl@0: 		{
sl@0: 		// Not an embedded key, so key is the actual key data and we
sl@0: 		// can therefore check its strength...
sl@0: 		TCrypto::IsSymmetricWeakEnoughL(BytesToBits(keyLength));
sl@0: 		if (! IsValidKeyLength(keyLength))
sl@0: 			{
sl@0: 			User::Leave(KErrNotSupported);
sl@0: 			}
sl@0: 		}
sl@0: 		
sl@0: 	delete iKey;	
sl@0: 	CleanupStack::Pop(key);
sl@0: 	iKey = key;
sl@0: 	iKeyBytes = keyLength;
sl@0: 
sl@0: 	// H/W needs reconfiguring
sl@0: 	iNeedToSetupHw = ETrue;
sl@0: 	}	
sl@0: 
sl@0: TInt CH4CipherImpl::BlockSize() const
sl@0: 	{
sl@0: 	// return block size in BITS
sl@0: 	return BytesToBits(iBlockBytes);
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::SetCryptoModeL(TUid aCryptoMode)
sl@0: 	{
sl@0: 	switch (aCryptoMode.iUid)
sl@0: 		{
sl@0: 		case KCryptoModeEncrypt:
sl@0: 		case KCryptoModeDecrypt:
sl@0: 			break;
sl@0: 		default:
sl@0: 			User::Leave(KErrNotSupported);
sl@0: 		}
sl@0: 	iCryptoMode = aCryptoMode;		
sl@0: 	// H/W needs reconfiguring
sl@0: 	iNeedToSetupHw = ETrue;
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::SetOperationModeL(TUid aOperationMode)
sl@0: 	{
sl@0: 	switch (aOperationMode.iUid)
sl@0: 		{
sl@0: 		case KOperationModeNone:
sl@0: 		case KOperationModeECB:
sl@0: 		case KOperationModeCBC:
sl@0: 			break;
sl@0: 		default:
sl@0: 			User::Leave(KErrNotSupported);
sl@0: 		}
sl@0: 	iOperationMode = aOperationMode;		
sl@0: 	// H/W needs reconfiguring
sl@0: 	iNeedToSetupHw = ETrue;
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::SetPaddingModeL(TUid aPaddingMode)
sl@0: 	{
sl@0: 	if(!iPadding || (aPaddingMode != iPaddingMode))
sl@0: 		{
sl@0: 		CPadding* padding(0);
sl@0: 		switch (aPaddingMode.iUid)
sl@0: 			{
sl@0: 			case KPaddingModeNone:
sl@0: 				padding = CPaddingNone::NewL(iBlockBytes);
sl@0: 				break;
sl@0: 			case KPaddingModeSSLv3:
sl@0: 			padding = CPaddingSSLv3::NewL(iBlockBytes);
sl@0: 			break;
sl@0: 			case KPaddingModePKCS7:
sl@0: 				padding = CPaddingPKCS7::NewL(iBlockBytes);
sl@0: 				break;
sl@0: 			default:
sl@0: 				User::Leave(KErrNotSupported);
sl@0: 			}
sl@0: 		delete iPadding;
sl@0: 		iPadding = padding;
sl@0: 		iPaddingMode = aPaddingMode;
sl@0: 		}
sl@0: 	
sl@0: 	// H/W needs reconfiguring
sl@0: 	iNeedToSetupHw = ETrue;
sl@0: 	}
sl@0: 	
sl@0: void CH4CipherImpl::SetIvL(const TDesC8& aIv)
sl@0: 	{
sl@0: 	if (iOperationMode.iUid != KOperationModeCBC)
sl@0: 		{
sl@0: 		User::Leave(KErrNotSupported);
sl@0: 		}
sl@0: 
sl@0: 	if (aIv.Length() != iBlockBytes) 
sl@0: 		{
sl@0: 		User::Leave(KErrArgument);
sl@0: 		}
sl@0: 	iIv = aIv;	
sl@0: 
sl@0: 	// H/W needs reconfiguring
sl@0: 	iNeedToSetupHw = ETrue;
sl@0: 	}
sl@0: 
sl@0: 
sl@0: 
sl@0: 	
sl@0: 	
sl@0: 
sl@0: 
sl@0: 
sl@0: 
sl@0: TInt CH4CipherImpl::MaxOutputLength(TInt aInputLength) const
sl@0: 	{	
sl@0: 	// The maximum output length required for Process is equal to the
sl@0: 	// size of the number of whole input blocks available.
sl@0: 	//
sl@0: 	// The block bytes is a power of two so we can use this to avoid
sl@0: 	// doing a real mod operation
sl@0: 	TUint partialBlockLength(iPartialBlock.Length());
sl@0: 	return (partialBlockLength + aInputLength) & ~TUint32(iBlockBytes - 1);
sl@0: 	}	
sl@0: 
sl@0: TInt CH4CipherImpl::MaxFinalOutputLength(TInt aInputLength) const
sl@0: 	{
sl@0: 	if (iCryptoMode.iUid == KCryptoModeEncrypt)
sl@0: 		{
sl@0: 		return iPadding->MaxPaddedLength(iPartialBlock.Length() + aInputLength);
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		return iPadding->MaxUnPaddedLength(aInputLength + iPartialBlock.Length());
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: 
sl@0: 
sl@0: void CH4CipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput)
sl@0: 	{
sl@0: 	if(iNeedToSetupHw)
sl@0: 		{
sl@0: 		// Reconfigure h/w based on iCryptoMode/iOperationMode/iKey/iIv
sl@0: 		DoSetupL();
sl@0: 		iNeedToSetupHw = EFalse;
sl@0: 		}
sl@0: 
sl@0: 	TInt inputLength(aInput.Length());	
sl@0: 	if (MaxOutputLength(inputLength) > aOutput.MaxLength())
sl@0: 		{
sl@0: 		User::Leave(KErrOverflow);
sl@0: 		}	
sl@0: 
sl@0: 	TInt partialBlockLength(iPartialBlock.Length()); // partial data written to h/w last time
sl@0: 	TUint32 totalInput = partialBlockLength + inputLength;
sl@0: 	
sl@0: 	// Pass new data to h/w driver
sl@0: 	DoWriteL(aInput.Ptr(), inputLength);
sl@0: 
sl@0: 	if(totalInput < iBlockBytes)
sl@0: 		{
sl@0: 		// Keep a copy of the partial block which is inside the h/w
sl@0: 		// driver in case we need to calculate pad for it later...
sl@0: 		if(inputLength) iPartialBlock.Append(aInput);
sl@0: 		// Not enough written yet for more data to be available yet.
sl@0: 		return;
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		// We have completed the previous partial block so we no
sl@0: 		// longer need to keep a copy of it
sl@0: 		iPartialBlock.Zero();
sl@0: 		// Work out length of partial block at end of current data
sl@0: 		TUint32 trailing = TUint32(partialBlockLength + inputLength) & TUint32(iBlockBytes - 1);
sl@0: 		// Keep a copy of the partial block data
sl@0: 		if(trailing) iPartialBlock.Append(aInput.Right(trailing));
sl@0: 		}
sl@0: 
sl@0: 	//
sl@0: 	// Work out how much data is available for reading.
sl@0: 	//
sl@0: 	// The h/w processes data a block at a time, and we only ever read
sl@0: 	// a multiple of the block size so the available data will always
sl@0: 	// be a multiple of the blocksize.
sl@0: 
sl@0: 	TUint32 availableData = totalInput & ~TUint32(iBlockBytes - 1);
sl@0: 	if (availableData)
sl@0: 		{
sl@0: 		// Read available data
sl@0: 		DoReadL(aOutput, availableData);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CH4CipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput)
sl@0: 	{
sl@0: 	if(iNeedToSetupHw)
sl@0: 		{
sl@0: 		// Reconfigure h/w based on iCryptoMode/iOperationMode/iKey/iIv
sl@0: 		DoSetupL();
sl@0: 		iNeedToSetupHw = EFalse;
sl@0: 		}
sl@0: 
sl@0: 	if(MaxFinalOutputLength(aInput.Length()) > aOutput.MaxLength() - aOutput.Length())
sl@0: 		{
sl@0: 		User::Leave(KErrOverflow);
sl@0: 		}
sl@0: 
sl@0: 
sl@0: 	if(iCryptoMode.iUid == KCryptoModeEncrypt)
sl@0: 		{
sl@0: 		// process everything up to the last (possibly empty block)
sl@0: 		ProcessL(aInput, aOutput);
sl@0: 
sl@0: 		// pad the plaintext
sl@0: 		iPaddingBlock.Zero();
sl@0: 		iPadding->PadL(iPartialBlock, iPaddingBlock);
sl@0: 
sl@0: 		TUint32 padLength = iPaddingBlock.Length();
sl@0: 		// Make sure pad worked
sl@0: 		if(padLength & TUint32(iBlockBytes-1))
sl@0: 			{
sl@0: 			User::Leave(KErrInvalidPadding);
sl@0: 			}
sl@0: 	
sl@0: 		if(padLength > 0)
sl@0: 			{
sl@0: 			// Padding created
sl@0: 
sl@0: 			// We have already written iPartialBlock data to the h/w
sl@0: 			// so skip those bytes which are in the pad.
sl@0: 			TUint32 partialLength = iPartialBlock.Length();
sl@0: 			if(partialLength)
sl@0: 				{
sl@0: 				// Make sure the pad algorithm did not change the bytes at
sl@0: 				// the start of the block....
sl@0: 				if(iPaddingBlock.Left(partialLength) != iPartialBlock)
sl@0: 					{
sl@0: 					User::Leave(KErrInvalidPadding);
sl@0: 					}
sl@0: 				}
sl@0: 			// Pass new data to h/w driver
sl@0: 			DoWriteL(iPaddingBlock.Ptr() + partialLength, padLength - partialLength);
sl@0: 
sl@0: 			// Have now written an exact multiple of blocks to h/w so clear partial
sl@0: 			iPartialBlock.Zero();
sl@0: 
sl@0: 			// Read data
sl@0: 			DoReadL(aOutput, padLength);
sl@0: 			}
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		// Decrypt
sl@0: 
sl@0: 		// Input length (including inputstore) must be a multiple of the 
sl@0: 		// block size in length
sl@0: 		if ((aInput.Length() + iPartialBlock.Length()) & (iBlockBytes - 1)) 
sl@0: 			{
sl@0: 			User::Leave(KErrArgument);
sl@0: 			}
sl@0: 		
sl@0: 		// process everything up to the last (possibly empty block)
sl@0: 		ProcessL(aInput, aOutput);
sl@0: 		ASSERT(iPartialBlock.Length()==0); // all the blocks should have been decrypted
sl@0: 
sl@0: 		// Retrieve last decrypted block.
sl@0: 		iPaddingBlock = aOutput.Right(iBlockBytes);
sl@0: 		aOutput.SetLength(aOutput.Length() - iBlockBytes);
sl@0: 
sl@0: 		// Unpad the last block and (re)append to output
sl@0: 		iPadding->UnPadL(iPaddingBlock, aOutput);
sl@0: 	
sl@0: 		iPaddingBlock.Zero();
sl@0: 		iPartialBlock.Zero();
sl@0: 		}
sl@0: 	
sl@0: 	}
sl@0: 
sl@0: 
sl@0: 
sl@0: // End of file
sl@0: 
sl@0: