diff -r da2ae96f639b -r cd501b96611d cryptoplugins/cryptospiplugins/source/softwarecrypto/symmetriccipherimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cryptoplugins/cryptospiplugins/source/softwarecrypto/symmetriccipherimpl.cpp Fri Nov 06 13:21:00 2009 +0200 @@ -0,0 +1,650 @@ +/* +* Copyright (c) 2007-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: +* +*/ + + +#include "symmetriccipherimpl.h" + +#include +#include +#include +#include "keys.h" +#include +#include +#include "pluginconfig.h" +#include +#include "common/inlines.h" + +using namespace SoftwareCrypto; + +// +// Implementation of Symmetric Cipher class +// +CSymmetricCipherImpl::CSymmetricCipherImpl() + { + } + +void CSymmetricCipherImpl::ConstructL(const CKey& aKey) + { + DoSetKeyL(aKey); + } + +void CSymmetricCipherImpl::SecureDelete(HBufC8*& aBuffer) + { + if (aBuffer) + { + aBuffer->Des().FillZ(); + } + delete aBuffer; + aBuffer = 0; + } + +CSymmetricCipherImpl::~CSymmetricCipherImpl() + { + SecureDelete(iKey); + } + +void CSymmetricCipherImpl::Close() + { + delete this; + } + +TAny* CSymmetricCipherImpl::GetExtension(TUid /*aExtensionId*/) + { + return 0; + } + +void CSymmetricCipherImpl::GetCharacteristicsL(const TAny*& aPluginCharacteristics) + { + TInt numCiphers = sizeof(KSymmetricCipherCharacteristics)/sizeof(TSymmetricCipherCharacteristics*); + TInt32 implUid = ImplementationUid().iUid; + for (TInt i = 0; i < numCiphers; ++i) + { + if (KSymmetricCipherCharacteristics[i]->cmn.iImplementationUID == implUid) + { + aPluginCharacteristics = KSymmetricCipherCharacteristics[i]; + break; + } + } + } + +TInt CSymmetricCipherImpl::GetKeyStrength() const + { + return BytesToBits(iKey->Length()); + } + +HBufC8* CSymmetricCipherImpl::ExtractKeyDataLC(const CKey& aKey) const + { + const TDesC8& keyContent = aKey.GetTDesC8L(KSymmetricKeyParameterUid); + return keyContent.AllocLC(); + } + +TInt CSymmetricCipherImpl::KeySize() const + { + // return key size in BITS + return BytesToBits(iKeyBytes); + } + +void CSymmetricCipherImpl::DoSetKeyL(const CKey& aKey) + { + HBufC8* key = ExtractKeyDataLC(aKey); + TInt keyLength(key->Length()); + + TCrypto::IsSymmetricWeakEnoughL(BytesToBits(keyLength)); + if (! IsValidKeyLength(keyLength)) + { + CleanupStack::PopAndDestroy(key); + User::Leave(KErrNotSupported); + } + + SecureDelete(iKey); + CleanupStack::Pop(key); + iKey = key; + iKeyBytes = keyLength; + } + +// +// Implementation of Symmetric Stream Cipher +// +CSymmetricStreamCipherImpl::CSymmetricStreamCipherImpl() + { + } + +CSymmetricStreamCipherImpl::~CSymmetricStreamCipherImpl() + { + } + +void CSymmetricStreamCipherImpl::SetKeyL(const CKey& aKey) + { + DoSetKeyL(aKey); + TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength()); + Reset(); + } + +void CSymmetricStreamCipherImpl::ConstructL(const CKey& aKey) + { + CSymmetricCipherImpl::ConstructL(aKey); + } + +TInt CSymmetricStreamCipherImpl::BlockSize() const + { + // return block size in BITS + return BYTE_BITS; + } + +void CSymmetricStreamCipherImpl::SetCryptoModeL(TUid /*aCryptoMode*/) + { + // Call the reset method. + Reset(); + } + +TInt CSymmetricStreamCipherImpl::MaxOutputLength(TInt aInputLength) const + { + return aInputLength; + } + +TInt CSymmetricStreamCipherImpl::MaxFinalOutputLength(TInt aInputLength) const + { + return aInputLength; + } + +void CSymmetricStreamCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput) + { + TInt outputIndex = aOutput.Size(); + + // aOutput may already have outputIndex bytes of data in it + // check there will still be enough space to process the result + __ASSERT_DEBUG(aOutput.MaxLength() - outputIndex >= MaxOutputLength(aInput.Length()), User::Panic(KCryptoPanic, ECryptoPanicOutputDescriptorOverflow)); + + aOutput.Append(aInput); + + TPtr8 transformBuf((TUint8*)(aOutput.Ptr()) + outputIndex, aInput.Size(), + aInput.Size()); + DoProcess(transformBuf); + } + +void CSymmetricStreamCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput) + { + ProcessL(aInput, aOutput); + } + +// +// Implementation of Symmetric Block Cipher +// +CSymmetricBlockCipherImpl::CSymmetricBlockCipherImpl( + TUint8 aBlockBytes, + TUid aCryptoMode, + TUid aOperationMode, + TUid aPaddingMode) : + iBlockBytes(aBlockBytes), + iCryptoMode(aCryptoMode), + iOperationMode(aOperationMode), + iPaddingMode(aPaddingMode), + iBufferedPlaintextPtr(0,0,0), + iCtrUnusedKeystreamPtr(0,0,0) + { + } + +CSymmetricBlockCipherImpl::~CSymmetricBlockCipherImpl() + { + delete iPadding; + delete [] iRegister; + delete [] iCurrentCipherText; + delete iBufferedPlaintext; + delete iCtrUnusedKeystream; + iIv.Close(); + iInputStore.Close(); + iPaddingBlock.Close(); + } + + +void CSymmetricBlockCipherImpl::ConstructL(const CKey& aKey) + { + CSymmetricCipherImpl::ConstructL(aKey); + DoSetOperationModeL(iOperationMode); + DoSetCryptoModeL(iCryptoMode); + DoSetPaddingModeL(iPaddingMode); + + iInputStore.ReAllocL(iBlockBytes); + iPaddingBlock.ReAllocL(iBlockBytes); + + iRegister = new(ELeave) TUint32[iBlockBytes/4]; + iRegisterPtr = reinterpret_cast(iRegister); + + iCurrentCipherText = new(ELeave) TUint32[iBlockBytes/4]; + iCurrentCipherTextPtr = reinterpret_cast(iCurrentCipherText); + + iBufferedPlaintext = HBufC8::NewL(iBlockBytes); + iBufferedPlaintextPtr.Set(iBufferedPlaintext->Des()); + + iCtrUnusedKeystream = HBufC8::NewL(iBlockBytes); + iCtrUnusedKeystreamPtr.Set(iCtrUnusedKeystream->Des()); + } + +void CSymmetricBlockCipherImpl::Reset() + { + iInputStore.Zero(); + iPaddingBlock.Zero(); + iCtrUnusedKeystreamPtr.Zero(); + + if (iOperationMode.iUid == KOperationModeCBC) + { + // only copy the IV if it is already set + if (iIv.MaxLength() > 0) + { + Mem::Copy(iRegisterPtr, &iIv[0], iBlockBytes); + } + } + } + +void CSymmetricBlockCipherImpl::SetKeyL(const CKey& aKey) + { + DoSetKeyL(aKey); + TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength()); + SetKeySchedule(); + Reset(); + } + +void CSymmetricBlockCipherImpl::SetOperationModeL(TUid aOperationMode) + { + DoSetOperationModeL(aOperationMode); + Reset(); + } + +void CSymmetricBlockCipherImpl::SetCryptoModeL(TUid aCryptoMode) + { + DoSetCryptoModeL(aCryptoMode); + SetKeySchedule(); + Reset(); + } + +void CSymmetricBlockCipherImpl::SetPaddingModeL(TUid aPaddingMode) + { + DoSetPaddingModeL(aPaddingMode); + Reset(); + } + +void CSymmetricBlockCipherImpl::SetIvL(const TDesC8& aIv) + { + if ((iOperationMode.iUid != KOperationModeCBC) && (iOperationMode.iUid != KOperationModeCTR)) + { + User::Leave(KErrNotSupported); + } + DoSetIvL(aIv); + Reset(); + } + +void CSymmetricBlockCipherImpl::DoSetOperationModeL(TUid aOperationMode) + { + switch (aOperationMode.iUid) + { + case KOperationModeNone: + case KOperationModeECB: + case KOperationModeCBC: + break; + case KOperationModeCTR: + SetCryptoModeL(KCryptoModeEncryptUid); + break; + default: + User::Leave(KErrNotSupported); + } + iOperationMode = aOperationMode; + } + +void CSymmetricBlockCipherImpl::DoSetCryptoModeL(TUid aCryptoMode) + { + switch (aCryptoMode.iUid) + { + case KCryptoModeEncrypt: + break; + case KCryptoModeDecrypt: + if (iOperationMode.iUid == KOperationModeCTR) + { + return; + } + break; + default: + User::Leave(KErrNotSupported); + } + iCryptoMode = aCryptoMode; + } + +void CSymmetricBlockCipherImpl::DoSetPaddingModeL(TUid aPaddingMode) + { + CPadding* padding(0); + switch (aPaddingMode.iUid) + { + case KPaddingModeNone: + padding = CPaddingNone::NewL(iBlockBytes); + break; + case KPaddingModeSSLv3: + padding = CPaddingSSLv3::NewL(iBlockBytes); + break; + case KPaddingModePKCS7: + padding = CPaddingPKCS7::NewL(iBlockBytes); + break; + default: + User::Leave(KErrNotSupported); + } + delete iPadding; + iPadding = padding; + iPaddingMode = aPaddingMode; + } + +void CSymmetricBlockCipherImpl::DoSetIvL(const TDesC8& aIv) + { + iIv.ReAllocL(iBlockBytes); + iIv.SetLength(iBlockBytes); + + iIv.Zero(); + if (aIv.Length() != iBlockBytes) + { + User::Leave(KErrArgument); + } + iIv = aIv; + Mem::Copy(iRegisterPtr, &iIv[0], iBlockBytes); //for CTR mode + + } + +TInt CSymmetricBlockCipherImpl::BlockSize() const + { + // return block size in BITS + if (iOperationMode.iUid == KOperationModeCTR) + { + return 8; + } + else + { + return BytesToBits(iBlockBytes); + } + } + +TInt CSymmetricBlockCipherImpl::MaxOutputLength(TInt aInputLength) const + { + if (iOperationMode.iUid == KOperationModeCTR) + { + return aInputLength; + } + else + { + // The maximum output length required for Process is equal to the + // size of the number of whole input blocks available. + // + // The block bytes is a power of two so we can use this to avoid + // doing a real mod operation + TUint inputStoreLength(iInputStore.Length()); + TInt rem = (aInputLength + inputStoreLength) & (iBlockBytes - 1); + return (aInputLength + inputStoreLength - rem); + } + } + +TInt CSymmetricBlockCipherImpl::MaxFinalOutputLength(TInt aInputLength) const + { + if (iOperationMode.iUid == KOperationModeCTR) + { + return aInputLength; + } + else if (iCryptoMode.iUid == KCryptoModeEncrypt) + { + return iPadding->MaxPaddedLength(iInputStore.Length() + aInputLength); + } + else + { + return iPadding->MaxUnPaddedLength(aInputLength + iInputStore.Size()); + } + } + +void CSymmetricBlockCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput) + { + // if we're running in CBC or CTR mode then we must have an IV set before we can + // do any processing ie call SetIvL() before this method + if ((iOperationMode.iUid == KOperationModeCBC) || (iOperationMode.iUid == KOperationModeCTR)) + { + if (iIv.MaxLength() == 0) + { + User::Leave(KErrNotSupported); + } + } + + TInt inputLength(aInput.Length()); + TInt inputStoreLength(iInputStore.Length()); + + if (MaxOutputLength(inputLength) > aOutput.MaxLength()) + { + User::Leave(KErrOverflow); + } + + if (iOperationMode.iUid == KOperationModeCTR) + { + ProcessCtrL(aInput, aOutput); + } + else + { + TUint8 blockSizeLog = CryptoLog2(iBlockBytes); + TInt wholeBlocks = (inputLength + inputStoreLength) >> blockSizeLog; + TInt wholeBlocksSize = wholeBlocks << blockSizeLog; + + if (wholeBlocks) + { + TInt outputLength(aOutput.Length()); + + if (inputStoreLength > 0) + { + aOutput.Append(iInputStore); + iInputStore.Zero(); + } + aOutput.Append(aInput.Left(wholeBlocksSize - inputStoreLength)); + Transform(const_cast(aOutput.Ptr()) + outputLength, wholeBlocks); + } + + TInt remainingBytes = inputLength + inputStoreLength - wholeBlocksSize; + if (remainingBytes > 0) + { + iInputStore.Append(aInput.Right(remainingBytes)); + } + } + } + +void CSymmetricBlockCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput) + { + if (iOperationMode.iUid == KOperationModeCTR) + { + ProcessL(aInput, aOutput); + } + else + { + // if we're running in CBC mode then we must have an IV set before we can + // do any processing ie call SetIvL() before this method + if (iOperationMode.iUid == KOperationModeCBC) + { + if (iIv.MaxLength() == 0) + { + User::Leave(KErrNotSupported); + } + } + + if (iCryptoMode.iUid == KCryptoModeEncrypt) + { + return DoProcessFinalEncryptL(aInput, aOutput); + } + else + { + return DoProcessFinalDecryptL(aInput, aOutput); + } + } + } + +void CSymmetricBlockCipherImpl::DoProcessFinalEncryptL(const TDesC8& aInput, TDes8& aOutput) + { + if (MaxFinalOutputLength(aInput.Length()) > aOutput.MaxLength() - aOutput.Length()) + { + User::Leave(KErrOverflow); + } + + // process everything up to the last (possibly empty block) + TInt outputStartIndex = aOutput.Length(); + ProcessL(aInput, aOutput); + + // pad the plaintext + iPadding->PadL(iInputStore, iPaddingBlock); + + // if padding required + if (iPaddingBlock.Length() > 0) + { + iInputStore.Zero(); + + // make sure the output is a multiple of the block size + User::LeaveIfError(((aOutput.Length() - outputStartIndex + iPaddingBlock.Length()) % iBlockBytes) == 0 ? KErrNone : KErrInvalidPadding); + + outputStartIndex = aOutput.Length(); + aOutput.Append(iPaddingBlock); + iPaddingBlock.Zero(); + TransformEncrypt(const_cast(aOutput.Ptr()) + outputStartIndex, 1); + } + } + +void CSymmetricBlockCipherImpl::DoProcessFinalDecryptL(const TDesC8& aInput, TDes8& aOutput) + { + if (MaxFinalOutputLength(aInput.Length()) > aOutput.MaxLength() - aOutput.Length()) + { + User::Leave(KErrOverflow); + } + + // Input length (including inputstore) must be a multiple of the + // block size in length + if ((aInput.Length() + iInputStore.Length()) & (iBlockBytes - 1)) + { + User::Leave(KErrArgument); + } + + if(aInput.Length() > iBlockBytes) + { + HBufC8* processBuf = HBufC8::NewLC(MaxFinalOutputLength(aInput.Length())); + TPtr8 processPtr = processBuf->Des(); + + ProcessL(aInput, processPtr); + + ASSERT(iInputStore.Length()==0); // all the blocks should have been decrypted + + // Unpad processPtr into aOutput + iPadding->UnPadL(processPtr, aOutput); + + CleanupStack::PopAndDestroy(processBuf); + } + else + { + // now contains the final ciphertext block + iInputStore.Append(aInput); + + // Decrypt the last _padding_ blocksize into a new buffer + TransformDecrypt(const_cast(iInputStore.Ptr()), 1); + + // Unpad the last block and append to output + iPadding->UnPadL(iInputStore, aOutput); + } + + iPaddingBlock.Zero(); + iInputStore.Zero(); + } + + +/** +CTR mode behaves like a stream cipher, accepting input of any arbitrary length. This results +in a significant body of code that behaves fundamentally differently to the ECB and CBC modes. +ProcessCtrL() is called by ProcessL() when operating in CTR mode, wrapping up all this +functionality into a separate method for clarity. + +Encrypting zero-filled bytes will return the keystream since the output of Transformation is simply +the input XORed with the keystream. + +See: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +*/ +void CSymmetricBlockCipherImpl::ProcessCtrL(const TDesC8& aInput, TDes8& aOutput) + { + TInt inputLength(aInput.Length()); + + TInt outputLength(aOutput.Length()); + TInt amountToXor = Min(iCtrUnusedKeystreamPtr.Length(), inputLength); + + // Try applying previously unused key stream bytes. + if (amountToXor > 0) + { + aOutput.Append(aInput.Left(amountToXor)); + for (TInt i = 0; i < amountToXor; ++i) + { + aOutput[outputLength + i] ^= iCtrUnusedKeystreamPtr[i]; + } + iCtrUnusedKeystreamPtr = iCtrUnusedKeystreamPtr.RightTPtr((iCtrUnusedKeystreamPtr.Length() - amountToXor)); + } + + TInt amountToEncode = inputLength - amountToXor; + + if ((iCtrUnusedKeystreamPtr.Length() == 0) && (amountToEncode > 0)) + { + // For each whole block's worth of input, transform it. + TInt wholeBlocks = (amountToEncode) / iBlockBytes; + TInt wholeBlocksSize = wholeBlocks * iBlockBytes; + outputLength = aOutput.Length(); + + if (wholeBlocks) + { + aOutput.Append(aInput.Mid(amountToXor, wholeBlocksSize)); + Transform(const_cast(aOutput.Ptr()) + outputLength, wholeBlocks); + } + + // CTR mode can handle arbitrary sized inputs. Here any remaining input data of less than the block size + // is padded with zeros and then transformed. On return this padded section of the block will contain the next + // sequence of keystream, which is copied to iCtrUnusedKeystream for use next time ProcessCtrL() is called. + TInt remainingBytes = amountToEncode - wholeBlocksSize; + iCtrUnusedKeystreamPtr = iCtrUnusedKeystream->Des(); + iCtrUnusedKeystreamPtr.SetMax(); + iCtrUnusedKeystreamPtr.FillZ(); + iCtrUnusedKeystreamPtr.Copy(aInput.Right(remainingBytes)); + iCtrUnusedKeystreamPtr.SetLength(iBlockBytes); + + Transform(const_cast(iCtrUnusedKeystreamPtr.Ptr()), 1); + + aOutput.Append(iCtrUnusedKeystreamPtr.Left(remainingBytes)); + + iCtrUnusedKeystreamPtr = iCtrUnusedKeystreamPtr.RightTPtr((iCtrUnusedKeystreamPtr.Length() - remainingBytes)); + } + } + + + +// Methods implemented in subclass. No coverage here. +#ifdef _BullseyeCoverage +#pragma suppress_warnings on +#pragma BullseyeCoverage off +#pragma suppress_warnings off +#endif +void CSymmetricStreamCipherImpl::SetOperationModeL(TUid /*aOperationMode*/) + { + // Override in subclass + User::Leave(KErrNotSupported); + } + +void CSymmetricStreamCipherImpl::SetPaddingModeL(TUid /*aPaddingMode*/) + { + // Override in subclass + User::Leave(KErrNotSupported); + } + +void CSymmetricStreamCipherImpl::SetIvL(const TDesC8& /*aIv*/) + { + // Override in subclass + User::Leave(KErrNotSupported); + }