--- /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 <e32def.h>
+#include <cryptostrength.h>
+#include <cryptospi/cryptospidef.h>
+#include "keys.h"
+#include <cryptopanic.h>
+#include <cryptospi/plugincharacteristics.h>
+#include "pluginconfig.h"
+#include <securityerr.h>
+#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<TUint8*>(iRegister);
+
+ iCurrentCipherText = new(ELeave) TUint32[iBlockBytes/4];
+ iCurrentCipherTextPtr = reinterpret_cast<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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);
+ }