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: