os/security/cryptoplugins/cryptospiplugins/source/softwarecrypto/symmetriccipherimpl.cpp
First public contribution.
2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
19 #include "symmetriccipherimpl.h"
22 #include <cryptostrength.h>
23 #include <cryptospi/cryptospidef.h>
25 #include <cryptopanic.h>
26 #include <cryptospi/plugincharacteristics.h>
27 #include "pluginconfig.h"
28 #include <securityerr.h>
29 #include "common/inlines.h"
31 using namespace SoftwareCrypto;
34 // Implementation of Symmetric Cipher class
36 CSymmetricCipherImpl::CSymmetricCipherImpl()
40 void CSymmetricCipherImpl::ConstructL(const CKey& aKey)
45 void CSymmetricCipherImpl::SecureDelete(HBufC8*& aBuffer)
49 aBuffer->Des().FillZ();
55 CSymmetricCipherImpl::~CSymmetricCipherImpl()
60 void CSymmetricCipherImpl::Close()
65 TAny* CSymmetricCipherImpl::GetExtension(TUid /*aExtensionId*/)
70 void CSymmetricCipherImpl::GetCharacteristicsL(const TAny*& aPluginCharacteristics)
72 TInt numCiphers = sizeof(KSymmetricCipherCharacteristics)/sizeof(TSymmetricCipherCharacteristics*);
73 TInt32 implUid = ImplementationUid().iUid;
74 for (TInt i = 0; i < numCiphers; ++i)
76 if (KSymmetricCipherCharacteristics[i]->cmn.iImplementationUID == implUid)
78 aPluginCharacteristics = KSymmetricCipherCharacteristics[i];
84 TInt CSymmetricCipherImpl::GetKeyStrength() const
86 return BytesToBits(iKey->Length());
89 HBufC8* CSymmetricCipherImpl::ExtractKeyDataLC(const CKey& aKey) const
91 const TDesC8& keyContent = aKey.GetTDesC8L(KSymmetricKeyParameterUid);
92 return keyContent.AllocLC();
95 TInt CSymmetricCipherImpl::KeySize() const
97 // return key size in BITS
98 return BytesToBits(iKeyBytes);
101 void CSymmetricCipherImpl::DoSetKeyL(const CKey& aKey)
103 HBufC8* key = ExtractKeyDataLC(aKey);
104 TInt keyLength(key->Length());
106 TCrypto::IsSymmetricWeakEnoughL(BytesToBits(keyLength));
107 if (! IsValidKeyLength(keyLength))
109 CleanupStack::PopAndDestroy(key);
110 User::Leave(KErrNotSupported);
114 CleanupStack::Pop(key);
116 iKeyBytes = keyLength;
120 // Implementation of Symmetric Stream Cipher
122 CSymmetricStreamCipherImpl::CSymmetricStreamCipherImpl()
126 CSymmetricStreamCipherImpl::~CSymmetricStreamCipherImpl()
130 void CSymmetricStreamCipherImpl::SetKeyL(const CKey& aKey)
133 TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength());
137 void CSymmetricStreamCipherImpl::ConstructL(const CKey& aKey)
139 CSymmetricCipherImpl::ConstructL(aKey);
142 TInt CSymmetricStreamCipherImpl::BlockSize() const
144 // return block size in BITS
148 void CSymmetricStreamCipherImpl::SetCryptoModeL(TUid /*aCryptoMode*/)
150 // Call the reset method.
154 TInt CSymmetricStreamCipherImpl::MaxOutputLength(TInt aInputLength) const
159 TInt CSymmetricStreamCipherImpl::MaxFinalOutputLength(TInt aInputLength) const
164 void CSymmetricStreamCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput)
166 TInt outputIndex = aOutput.Size();
168 // aOutput may already have outputIndex bytes of data in it
169 // check there will still be enough space to process the result
170 __ASSERT_DEBUG(aOutput.MaxLength() - outputIndex >= MaxOutputLength(aInput.Length()), User::Panic(KCryptoPanic, ECryptoPanicOutputDescriptorOverflow));
172 aOutput.Append(aInput);
174 TPtr8 transformBuf((TUint8*)(aOutput.Ptr()) + outputIndex, aInput.Size(),
176 DoProcess(transformBuf);
179 void CSymmetricStreamCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput)
181 ProcessL(aInput, aOutput);
185 // Implementation of Symmetric Block Cipher
187 CSymmetricBlockCipherImpl::CSymmetricBlockCipherImpl(
192 iBlockBytes(aBlockBytes),
193 iCryptoMode(aCryptoMode),
194 iOperationMode(aOperationMode),
195 iPaddingMode(aPaddingMode),
196 iBufferedPlaintextPtr(0,0,0),
197 iCtrUnusedKeystreamPtr(0,0,0)
201 CSymmetricBlockCipherImpl::~CSymmetricBlockCipherImpl()
205 delete [] iCurrentCipherText;
206 delete iBufferedPlaintext;
207 delete iCtrUnusedKeystream;
210 iPaddingBlock.Close();
214 void CSymmetricBlockCipherImpl::ConstructL(const CKey& aKey)
216 CSymmetricCipherImpl::ConstructL(aKey);
217 DoSetOperationModeL(iOperationMode);
218 DoSetCryptoModeL(iCryptoMode);
219 DoSetPaddingModeL(iPaddingMode);
221 iInputStore.ReAllocL(iBlockBytes);
222 iPaddingBlock.ReAllocL(iBlockBytes);
224 iRegister = new(ELeave) TUint32[iBlockBytes/4];
225 iRegisterPtr = reinterpret_cast<TUint8*>(iRegister);
227 iCurrentCipherText = new(ELeave) TUint32[iBlockBytes/4];
228 iCurrentCipherTextPtr = reinterpret_cast<TUint8*>(iCurrentCipherText);
230 iBufferedPlaintext = HBufC8::NewL(iBlockBytes);
231 iBufferedPlaintextPtr.Set(iBufferedPlaintext->Des());
233 iCtrUnusedKeystream = HBufC8::NewL(iBlockBytes);
234 iCtrUnusedKeystreamPtr.Set(iCtrUnusedKeystream->Des());
237 void CSymmetricBlockCipherImpl::Reset()
240 iPaddingBlock.Zero();
241 iCtrUnusedKeystreamPtr.Zero();
243 if (iOperationMode.iUid == KOperationModeCBC)
245 // only copy the IV if it is already set
246 if (iIv.MaxLength() > 0)
248 Mem::Copy(iRegisterPtr, &iIv[0], iBlockBytes);
253 void CSymmetricBlockCipherImpl::SetKeyL(const CKey& aKey)
256 TCrypto::IsSymmetricWeakEnoughL(GetKeyStrength());
261 void CSymmetricBlockCipherImpl::SetOperationModeL(TUid aOperationMode)
263 DoSetOperationModeL(aOperationMode);
267 void CSymmetricBlockCipherImpl::SetCryptoModeL(TUid aCryptoMode)
269 DoSetCryptoModeL(aCryptoMode);
274 void CSymmetricBlockCipherImpl::SetPaddingModeL(TUid aPaddingMode)
276 DoSetPaddingModeL(aPaddingMode);
280 void CSymmetricBlockCipherImpl::SetIvL(const TDesC8& aIv)
282 if ((iOperationMode.iUid != KOperationModeCBC) && (iOperationMode.iUid != KOperationModeCTR))
284 User::Leave(KErrNotSupported);
290 void CSymmetricBlockCipherImpl::DoSetOperationModeL(TUid aOperationMode)
292 switch (aOperationMode.iUid)
294 case KOperationModeNone:
295 case KOperationModeECB:
296 case KOperationModeCBC:
298 case KOperationModeCTR:
299 SetCryptoModeL(KCryptoModeEncryptUid);
302 User::Leave(KErrNotSupported);
304 iOperationMode = aOperationMode;
307 void CSymmetricBlockCipherImpl::DoSetCryptoModeL(TUid aCryptoMode)
309 switch (aCryptoMode.iUid)
311 case KCryptoModeEncrypt:
313 case KCryptoModeDecrypt:
314 if (iOperationMode.iUid == KOperationModeCTR)
320 User::Leave(KErrNotSupported);
322 iCryptoMode = aCryptoMode;
325 void CSymmetricBlockCipherImpl::DoSetPaddingModeL(TUid aPaddingMode)
327 CPadding* padding(0);
328 switch (aPaddingMode.iUid)
330 case KPaddingModeNone:
331 padding = CPaddingNone::NewL(iBlockBytes);
333 case KPaddingModeSSLv3:
334 padding = CPaddingSSLv3::NewL(iBlockBytes);
336 case KPaddingModePKCS7:
337 padding = CPaddingPKCS7::NewL(iBlockBytes);
340 User::Leave(KErrNotSupported);
344 iPaddingMode = aPaddingMode;
347 void CSymmetricBlockCipherImpl::DoSetIvL(const TDesC8& aIv)
349 iIv.ReAllocL(iBlockBytes);
350 iIv.SetLength(iBlockBytes);
353 if (aIv.Length() != iBlockBytes)
355 User::Leave(KErrArgument);
358 Mem::Copy(iRegisterPtr, &iIv[0], iBlockBytes); //for CTR mode
362 TInt CSymmetricBlockCipherImpl::BlockSize() const
364 // return block size in BITS
365 if (iOperationMode.iUid == KOperationModeCTR)
371 return BytesToBits(iBlockBytes);
375 TInt CSymmetricBlockCipherImpl::MaxOutputLength(TInt aInputLength) const
377 if (iOperationMode.iUid == KOperationModeCTR)
383 // The maximum output length required for Process is equal to the
384 // size of the number of whole input blocks available.
386 // The block bytes is a power of two so we can use this to avoid
387 // doing a real mod operation
388 TUint inputStoreLength(iInputStore.Length());
389 TInt rem = (aInputLength + inputStoreLength) & (iBlockBytes - 1);
390 return (aInputLength + inputStoreLength - rem);
394 TInt CSymmetricBlockCipherImpl::MaxFinalOutputLength(TInt aInputLength) const
396 if (iOperationMode.iUid == KOperationModeCTR)
400 else if (iCryptoMode.iUid == KCryptoModeEncrypt)
402 return iPadding->MaxPaddedLength(iInputStore.Length() + aInputLength);
406 return iPadding->MaxUnPaddedLength(aInputLength + iInputStore.Size());
410 void CSymmetricBlockCipherImpl::ProcessL(const TDesC8& aInput, TDes8& aOutput)
412 // if we're running in CBC or CTR mode then we must have an IV set before we can
413 // do any processing ie call SetIvL() before this method
414 if ((iOperationMode.iUid == KOperationModeCBC) || (iOperationMode.iUid == KOperationModeCTR))
416 if (iIv.MaxLength() == 0)
418 User::Leave(KErrNotSupported);
422 TInt inputLength(aInput.Length());
423 TInt inputStoreLength(iInputStore.Length());
425 if (MaxOutputLength(inputLength) > aOutput.MaxLength())
427 User::Leave(KErrOverflow);
430 if (iOperationMode.iUid == KOperationModeCTR)
432 ProcessCtrL(aInput, aOutput);
436 TUint8 blockSizeLog = CryptoLog2(iBlockBytes);
437 TInt wholeBlocks = (inputLength + inputStoreLength) >> blockSizeLog;
438 TInt wholeBlocksSize = wholeBlocks << blockSizeLog;
442 TInt outputLength(aOutput.Length());
444 if (inputStoreLength > 0)
446 aOutput.Append(iInputStore);
449 aOutput.Append(aInput.Left(wholeBlocksSize - inputStoreLength));
450 Transform(const_cast<TUint8*>(aOutput.Ptr()) + outputLength, wholeBlocks);
453 TInt remainingBytes = inputLength + inputStoreLength - wholeBlocksSize;
454 if (remainingBytes > 0)
456 iInputStore.Append(aInput.Right(remainingBytes));
461 void CSymmetricBlockCipherImpl::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput)
463 if (iOperationMode.iUid == KOperationModeCTR)
465 ProcessL(aInput, aOutput);
469 // if we're running in CBC mode then we must have an IV set before we can
470 // do any processing ie call SetIvL() before this method
471 if (iOperationMode.iUid == KOperationModeCBC)
473 if (iIv.MaxLength() == 0)
475 User::Leave(KErrNotSupported);
479 if (iCryptoMode.iUid == KCryptoModeEncrypt)
481 return DoProcessFinalEncryptL(aInput, aOutput);
485 return DoProcessFinalDecryptL(aInput, aOutput);
490 void CSymmetricBlockCipherImpl::DoProcessFinalEncryptL(const TDesC8& aInput, TDes8& aOutput)
492 if (MaxFinalOutputLength(aInput.Length()) > aOutput.MaxLength() - aOutput.Length())
494 User::Leave(KErrOverflow);
497 // process everything up to the last (possibly empty block)
498 TInt outputStartIndex = aOutput.Length();
499 ProcessL(aInput, aOutput);
502 iPadding->PadL(iInputStore, iPaddingBlock);
504 // if padding required
505 if (iPaddingBlock.Length() > 0)
509 // make sure the output is a multiple of the block size
510 User::LeaveIfError(((aOutput.Length() - outputStartIndex + iPaddingBlock.Length()) % iBlockBytes) == 0 ? KErrNone : KErrInvalidPadding);
512 outputStartIndex = aOutput.Length();
513 aOutput.Append(iPaddingBlock);
514 iPaddingBlock.Zero();
515 TransformEncrypt(const_cast<TUint8*>(aOutput.Ptr()) + outputStartIndex, 1);
519 void CSymmetricBlockCipherImpl::DoProcessFinalDecryptL(const TDesC8& aInput, TDes8& aOutput)
521 if (MaxFinalOutputLength(aInput.Length()) > aOutput.MaxLength() - aOutput.Length())
523 User::Leave(KErrOverflow);
526 // Input length (including inputstore) must be a multiple of the
527 // block size in length
528 if ((aInput.Length() + iInputStore.Length()) & (iBlockBytes - 1))
530 User::Leave(KErrArgument);
533 if(aInput.Length() > iBlockBytes)
535 HBufC8* processBuf = HBufC8::NewLC(MaxFinalOutputLength(aInput.Length()));
536 TPtr8 processPtr = processBuf->Des();
538 ProcessL(aInput, processPtr);
540 ASSERT(iInputStore.Length()==0); // all the blocks should have been decrypted
542 // Unpad processPtr into aOutput
543 iPadding->UnPadL(processPtr, aOutput);
545 CleanupStack::PopAndDestroy(processBuf);
549 // now contains the final ciphertext block
550 iInputStore.Append(aInput);
552 // Decrypt the last _padding_ blocksize into a new buffer
553 TransformDecrypt(const_cast<TUint8*>(iInputStore.Ptr()), 1);
555 // Unpad the last block and append to output
556 iPadding->UnPadL(iInputStore, aOutput);
559 iPaddingBlock.Zero();
565 CTR mode behaves like a stream cipher, accepting input of any arbitrary length. This results
566 in a significant body of code that behaves fundamentally differently to the ECB and CBC modes.
567 ProcessCtrL() is called by ProcessL() when operating in CTR mode, wrapping up all this
568 functionality into a separate method for clarity.
570 Encrypting zero-filled bytes will return the keystream since the output of Transformation is simply
571 the input XORed with the keystream.
573 See: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
575 void CSymmetricBlockCipherImpl::ProcessCtrL(const TDesC8& aInput, TDes8& aOutput)
577 TInt inputLength(aInput.Length());
579 TInt outputLength(aOutput.Length());
580 TInt amountToXor = Min(iCtrUnusedKeystreamPtr.Length(), inputLength);
582 // Try applying previously unused key stream bytes.
585 aOutput.Append(aInput.Left(amountToXor));
586 for (TInt i = 0; i < amountToXor; ++i)
588 aOutput[outputLength + i] ^= iCtrUnusedKeystreamPtr[i];
590 iCtrUnusedKeystreamPtr = iCtrUnusedKeystreamPtr.RightTPtr((iCtrUnusedKeystreamPtr.Length() - amountToXor));
593 TInt amountToEncode = inputLength - amountToXor;
595 if ((iCtrUnusedKeystreamPtr.Length() == 0) && (amountToEncode > 0))
597 // For each whole block's worth of input, transform it.
598 TInt wholeBlocks = (amountToEncode) / iBlockBytes;
599 TInt wholeBlocksSize = wholeBlocks * iBlockBytes;
600 outputLength = aOutput.Length();
604 aOutput.Append(aInput.Mid(amountToXor, wholeBlocksSize));
605 Transform(const_cast<TUint8*>(aOutput.Ptr()) + outputLength, wholeBlocks);
608 // CTR mode can handle arbitrary sized inputs. Here any remaining input data of less than the block size
609 // is padded with zeros and then transformed. On return this padded section of the block will contain the next
610 // sequence of keystream, which is copied to iCtrUnusedKeystream for use next time ProcessCtrL() is called.
611 TInt remainingBytes = amountToEncode - wholeBlocksSize;
612 iCtrUnusedKeystreamPtr = iCtrUnusedKeystream->Des();
613 iCtrUnusedKeystreamPtr.SetMax();
614 iCtrUnusedKeystreamPtr.FillZ();
615 iCtrUnusedKeystreamPtr.Copy(aInput.Right(remainingBytes));
616 iCtrUnusedKeystreamPtr.SetLength(iBlockBytes);
618 Transform(const_cast<TUint8*>(iCtrUnusedKeystreamPtr.Ptr()), 1);
620 aOutput.Append(iCtrUnusedKeystreamPtr.Left(remainingBytes));
622 iCtrUnusedKeystreamPtr = iCtrUnusedKeystreamPtr.RightTPtr((iCtrUnusedKeystreamPtr.Length() - remainingBytes));
628 // Methods implemented in subclass. No coverage here.
629 #ifdef _BullseyeCoverage
630 #pragma suppress_warnings on
631 #pragma BullseyeCoverage off
632 #pragma suppress_warnings off
634 void CSymmetricStreamCipherImpl::SetOperationModeL(TUid /*aOperationMode*/)
636 // Override in subclass
637 User::Leave(KErrNotSupported);
640 void CSymmetricStreamCipherImpl::SetPaddingModeL(TUid /*aPaddingMode*/)
642 // Override in subclass
643 User::Leave(KErrNotSupported);
646 void CSymmetricStreamCipherImpl::SetIvL(const TDesC8& /*aIv*/)
648 // Override in subclass
649 User::Leave(KErrNotSupported);