sl@0: /* sl@0: * Copyright (c) 2007-2010 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: #include "symmetriccipherimpl.h" sl@0: sl@0: #include <e32def.h> sl@0: #include <cryptostrength.h> sl@0: #include <cryptospi/cryptospidef.h> sl@0: #include <cryptospi/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 "../../../source/common/inlines.h" sl@0: sl@0: using namespace SoftwareCrypto; sl@0: sl@0: // sl@0: // Implementation of Symmetric Cipher class sl@0: // sl@0: CSymmetricCipherImpl::CSymmetricCipherImpl() sl@0: { sl@0: } sl@0: sl@0: void CSymmetricCipherImpl::ConstructL(const CKey& aKey) sl@0: { sl@0: DoSetKeyL(aKey); sl@0: } sl@0: sl@0: void CSymmetricCipherImpl::SecureDelete(HBufC8*& aBuffer) sl@0: { sl@0: if (aBuffer) sl@0: { sl@0: aBuffer->Des().FillZ(); sl@0: } sl@0: delete aBuffer; sl@0: aBuffer = 0; sl@0: } sl@0: sl@0: CSymmetricCipherImpl::~CSymmetricCipherImpl() sl@0: { sl@0: SecureDelete(iKey); sl@0: } sl@0: sl@0: void CSymmetricCipherImpl::Close() sl@0: { sl@0: delete this; sl@0: } sl@0: sl@0: TAny* CSymmetricCipherImpl::GetExtension(TUid /*aExtensionId*/) sl@0: { sl@0: return 0; sl@0: } sl@0: sl@0: void CSymmetricCipherImpl::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: TInt CSymmetricCipherImpl::GetKeyStrength() const sl@0: { sl@0: return BytesToBits(iKey->Length()); sl@0: } sl@0: sl@0: HBufC8* CSymmetricCipherImpl::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 CSymmetricCipherImpl::KeySize() const sl@0: { sl@0: // return key size in BITS sl@0: return BytesToBits(iKeyBytes); sl@0: } sl@0: sl@0: void CSymmetricCipherImpl::DoSetKeyL(const CKey& aKey) sl@0: { sl@0: HBufC8* key = ExtractKeyDataLC(aKey); sl@0: TInt keyLength(key->Length()); sl@0: sl@0: TCrypto::IsSymmetricWeakEnoughL(BytesToBits(keyLength)); sl@0: if (! IsValidKeyLength(keyLength)) sl@0: { sl@0: CleanupStack::PopAndDestroy(key); sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: SecureDelete(iKey); sl@0: CleanupStack::Pop(key); sl@0: iKey = key; sl@0: iKeyBytes = keyLength; sl@0: } sl@0: sl@0: // sl@0: // Implementation of Symmetric Stream Cipher sl@0: // sl@0: CSymmetricStreamCipherImpl::CSymmetricStreamCipherImpl() sl@0: { sl@0: } sl@0: sl@0: CSymmetricStreamCipherImpl::~CSymmetricStreamCipherImpl() sl@0: { sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::SetKeyL(const CKey& aKey) sl@0: { sl@0: DoSetKeyL(aKey); sl@0: TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength()); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::ConstructL(const CKey& aKey) sl@0: { sl@0: CSymmetricCipherImpl::ConstructL(aKey); sl@0: } sl@0: sl@0: TInt CSymmetricStreamCipherImpl::BlockSize() const sl@0: { sl@0: // return block size in BITS sl@0: return 8; sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::SetOperationModeL(TUid /*aOperationMode*/) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::SetCryptoModeL(TUid /*aCryptoMode*/) sl@0: { sl@0: // Call the reset method. sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::SetPaddingModeL(TUid /*aPaddingMode*/) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::SetIvL(const TDesC8& /*aIv*/) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: TInt CSymmetricStreamCipherImpl::MaxOutputLength(TInt aInputLength) const sl@0: { sl@0: return aInputLength; sl@0: } sl@0: sl@0: TInt CSymmetricStreamCipherImpl::MaxFinalOutputLength(TInt aInputLength) const sl@0: { sl@0: return aInputLength; sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput) sl@0: { sl@0: TInt outputIndex = aOutput.Size(); sl@0: sl@0: // aOutput may already have outputIndex bytes of data in it sl@0: // check there will still be enough space to process the result sl@0: __ASSERT_DEBUG(aOutput.MaxLength() - outputIndex >= MaxOutputLength(aInput.Length()), User::Panic(KCryptoPanic, ECryptoPanicOutputDescriptorOverflow)); sl@0: sl@0: aOutput.Append(aInput); sl@0: sl@0: TPtr8 transformBuf((TUint8*)(aOutput.Ptr()) + outputIndex, aInput.Size(), sl@0: aInput.Size()); sl@0: DoProcess(transformBuf); sl@0: } sl@0: sl@0: void CSymmetricStreamCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput) sl@0: { sl@0: ProcessL(aInput, aOutput); sl@0: } sl@0: sl@0: // sl@0: // Implementation of Symmetric Block Cipher sl@0: // sl@0: CSymmetricBlockCipherImpl::CSymmetricBlockCipherImpl( sl@0: 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: CSymmetricBlockCipherImpl::~CSymmetricBlockCipherImpl() sl@0: { sl@0: delete iPadding; sl@0: delete iCbcRegister; sl@0: delete iCurrentCipherText; sl@0: iIv.Close(); sl@0: iInputStore.Close(); sl@0: iPaddingBlock.Close(); sl@0: } sl@0: sl@0: sl@0: void CSymmetricBlockCipherImpl::ConstructL(const CKey& aKey) sl@0: { sl@0: CSymmetricCipherImpl::ConstructL(aKey); sl@0: DoSetOperationModeL(iOperationMode); sl@0: DoSetCryptoModeL(iCryptoMode); sl@0: DoSetPaddingModeL(iPaddingMode); sl@0: sl@0: iInputStore.ReAllocL(iBlockBytes); sl@0: iPaddingBlock.ReAllocL(iBlockBytes); sl@0: sl@0: iCbcRegister = new(ELeave) TUint32[iBlockBytes/4]; sl@0: iCbcRegisterPtr = reinterpret_cast<TUint8*>(iCbcRegister); sl@0: sl@0: iCurrentCipherText = new(ELeave) TUint32[iBlockBytes/4]; sl@0: iCurrentCipherTextPtr = reinterpret_cast<TUint8*>(iCurrentCipherText); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::Reset() sl@0: { sl@0: iInputStore.Zero(); sl@0: iPaddingBlock.Zero(); sl@0: sl@0: if (iOperationMode.iUid == KOperationModeCBC) sl@0: { sl@0: // only copy the IV if it is already set sl@0: if (iIv.MaxLength() > 0) sl@0: { sl@0: Mem::Copy(iCbcRegisterPtr, &iIv[0], iBlockBytes); sl@0: } sl@0: } sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::SetKeyL(const CKey& aKey) sl@0: { sl@0: DoSetKeyL(aKey); sl@0: TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength()); sl@0: SetKeySchedule(); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::SetOperationModeL(TUid aOperationMode) sl@0: { sl@0: DoSetOperationModeL(aOperationMode); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::SetCryptoModeL(TUid aCryptoMode) sl@0: { sl@0: DoSetCryptoModeL(aCryptoMode); sl@0: SetKeySchedule(); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::SetPaddingModeL(TUid aPaddingMode) sl@0: { sl@0: DoSetPaddingModeL(aPaddingMode); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::SetIvL(const TDesC8& aIv) sl@0: { sl@0: if (iOperationMode.iUid != KOperationModeCBC) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: DoSetIvL(aIv); sl@0: Reset(); sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::DoSetOperationModeL(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: } sl@0: sl@0: void CSymmetricBlockCipherImpl::DoSetCryptoModeL(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: } sl@0: sl@0: void CSymmetricBlockCipherImpl::DoSetPaddingModeL(TUid aPaddingMode) 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: void CSymmetricBlockCipherImpl::DoSetIvL(const TDesC8& aIv) sl@0: { sl@0: iIv.ReAllocL(iBlockBytes); sl@0: iIv.SetLength(iBlockBytes); sl@0: sl@0: iIv.Zero(); sl@0: if (aIv.Length() != iBlockBytes) sl@0: { sl@0: User::Leave(KErrArgument); sl@0: } sl@0: iIv = aIv; sl@0: } sl@0: sl@0: TInt CSymmetricBlockCipherImpl::BlockSize() const sl@0: { sl@0: // return block size in BITS sl@0: return BytesToBits(iBlockBytes); sl@0: } sl@0: sl@0: TInt CSymmetricBlockCipherImpl::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 inputStoreLength(iInputStore.Length()); sl@0: TInt rem = (aInputLength + inputStoreLength) & (iBlockBytes - 1); sl@0: return (aInputLength + inputStoreLength - rem); sl@0: } sl@0: sl@0: TInt CSymmetricBlockCipherImpl::MaxFinalOutputLength(TInt aInputLength) const sl@0: { sl@0: if (iCryptoMode.iUid == KCryptoModeEncrypt) sl@0: { sl@0: return iPadding->MaxPaddedLength(iInputStore.Length() + aInputLength); sl@0: } sl@0: else sl@0: { sl@0: return iPadding->MaxUnPaddedLength(aInputLength + iInputStore.Size()); sl@0: } sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput) sl@0: { sl@0: // if we're running in CBC mode then we must have an IV set before we can sl@0: // do any processing ie call SetIvL() before this method sl@0: if (iOperationMode.iUid == KOperationModeCBC) sl@0: { sl@0: if (iIv.MaxLength() == 0) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: } sl@0: sl@0: TInt inputLength(aInput.Length()); sl@0: TInt inputStoreLength(iInputStore.Length()); sl@0: sl@0: if (MaxOutputLength(inputLength) > aOutput.MaxLength()) sl@0: { sl@0: User::Leave(KErrOverflow); sl@0: } sl@0: sl@0: TUint8 blockSizeLog = CryptoLog2(iBlockBytes); sl@0: TInt wholeBlocks = (inputLength + inputStoreLength) >> blockSizeLog; sl@0: TInt wholeBlocksSize = wholeBlocks << blockSizeLog; sl@0: sl@0: if (wholeBlocks) sl@0: { sl@0: TInt outputLength(aOutput.Length()); sl@0: sl@0: if (inputStoreLength > 0) sl@0: { sl@0: aOutput.Append(iInputStore); sl@0: iInputStore.Zero(); sl@0: } sl@0: aOutput.Append(aInput.Left(wholeBlocksSize - inputStoreLength)); sl@0: Transform(const_cast<TUint8*>(aOutput.Ptr()) + outputLength, wholeBlocks); sl@0: } sl@0: sl@0: TInt remainingBytes = inputLength + inputStoreLength - wholeBlocksSize; sl@0: if (remainingBytes > 0) sl@0: { sl@0: iInputStore.Append(aInput.Right(remainingBytes)); sl@0: } sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput) sl@0: { sl@0: // if we're running in CBC mode then we must have an IV set before we can sl@0: // do any processing ie call SetIvL() before this method sl@0: if (iOperationMode.iUid == KOperationModeCBC) sl@0: { sl@0: if (iIv.MaxLength() == 0) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: } sl@0: sl@0: if (iCryptoMode.iUid == KCryptoModeEncrypt) sl@0: { sl@0: return DoProcessFinalEncryptL(aInput, aOutput); sl@0: } sl@0: else sl@0: { sl@0: return DoProcessFinalDecryptL(aInput, aOutput); sl@0: } sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::DoProcessFinalEncryptL(const TDesC8& aInput, TDes8& aOutput) 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: // process everything up to the last (possibly empty block) sl@0: TInt outputStartIndex = aOutput.Length(); sl@0: ProcessL(aInput, aOutput); sl@0: sl@0: // pad the plaintext sl@0: iPadding->PadL(iInputStore, iPaddingBlock); sl@0: sl@0: // if padding required sl@0: if (iPaddingBlock.Length() > 0) sl@0: { sl@0: iInputStore.Zero(); sl@0: sl@0: // make sure the output is a multiple of the block size sl@0: User::LeaveIfError(((aOutput.Length() - outputStartIndex + iPaddingBlock.Length()) % iBlockBytes) == 0 ? KErrNone : KErrInvalidPadding); sl@0: sl@0: outputStartIndex = aOutput.Length(); sl@0: aOutput.Append(iPaddingBlock); sl@0: iPaddingBlock.Zero(); sl@0: TransformEncrypt(const_cast<TUint8*>(aOutput.Ptr()) + outputStartIndex, 1); sl@0: } sl@0: } sl@0: sl@0: void CSymmetricBlockCipherImpl::DoProcessFinalDecryptL(const TDesC8& aInput, TDes8& aOutput) 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: // Input length (including inputstore) must be a multiple of the sl@0: // block size in length sl@0: if ((aInput.Length() + iInputStore.Length()) & (iBlockBytes - 1)) sl@0: { sl@0: User::Leave(KErrArgument); sl@0: } sl@0: sl@0: TInt bytesProcessed(0); sl@0: if(aInput.Length() > iBlockBytes) sl@0: { sl@0: // the last block lies entirely within aInput so decrypt everything up sl@0: // to this point. sl@0: bytesProcessed = aInput.Length() - iBlockBytes; sl@0: ProcessL(aInput.Left(bytesProcessed), aOutput); sl@0: ASSERT(iInputStore.Length()==0); // all the blocks should have been decrypted sl@0: } sl@0: else sl@0: { sl@0: // if the input is less than one block in length then this + input sl@0: // store should combine to give exactly one block of data sl@0: ASSERT((iInputStore.Length() + aInput.Length()) == iBlockBytes); sl@0: } sl@0: sl@0: // now contains the final ciphertext block sl@0: iInputStore.Append(aInput.Right(aInput.Length() - bytesProcessed)); sl@0: sl@0: // Decrypt the last _padding_ blocksize into a new buffer sl@0: TransformDecrypt(const_cast<TUint8*>(iInputStore.Ptr()), 1); sl@0: sl@0: // Unpad the last block and append to output sl@0: iPadding->UnPadL(iInputStore, aOutput); sl@0: sl@0: iPaddingBlock.Zero(); sl@0: iInputStore.Zero(); sl@0: } sl@0: sl@0: