/*
* Copyright (c) 2002-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 <e32std.h>
#include <random.h>

#include "pbencryptor.h"
#include "pbesymmetricfactory.h"

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
/** The maximum block size supported (in bytes) */
const TUint KMaxBlockSizeSupported = 32;
#endif

EXPORT_C CPBEncryptorSet* CPBEncryptorSet::NewL(
	const TPBECipher aCipher, const TDesC8& aKey) 
	{
	CPBEncryptorSet* self = NewLC(aCipher, aKey);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CPBEncryptorSet* CPBEncryptorSet::NewLC(
	const TPBECipher aCipher, const TDesC8& aKey)
	{
	CPBEncryptorSet* self = new(ELeave) CPBEncryptorSet;
	CleanupStack::PushL(self);

	self->ConstructL(aCipher, aKey);
	
	return self;
	}

void CPBEncryptorSet::ConstructL(const TPBECipher aCipher,const TDesC8& aKey)
	{
	TInt blocksize = PBE::GetBlockBytes(aCipher);
	iIV = HBufC8::NewMaxL(blocksize); 
	TPtr8 iv = iIV->Des();
	TRandom::RandomL(iv);

	iCipher = PBE::MakeEncryptorL(aCipher, aKey, iv);
	}

void CPBEncryptorSet::Process(const TDesC8& aInput, TDes8& aOutput)
	{
	if(!iIVSent)
		{
		aOutput.Append(*iIV);
		iIVSent = ETrue;
		}
	iCipher->Process(aInput, aOutput);
	}

void CPBEncryptorSet::ProcessFinalL(const TDesC8& aInput, 
	TDes8& aOutput)
	{
	if(!iIVSent)
		{
		aOutput.Append(*iIV);
		iIVSent = ETrue;
		}
	
	iCipher->ProcessFinalL(aInput, aOutput);
	}


CPBEncryptorSet::CPBEncryptorSet() : iIVSent(EFalse)
	{
	}

CPBEncryptorSet::~CPBEncryptorSet()
	{
	delete iIV;
	delete iCipher;	//	Don't delete iCBCDecryptor, this belongs to iCipher
	}

TInt CPBEncryptorSet::MaxOutputLength(TUint aMaxInputLength) const
    {
	if(iIVSent)
		{
		return (iCipher->MaxOutputLength(aMaxInputLength));
		}
	else 
		{//	If we've not sent the iv yet then its the length 
		//	from the cipher plus a blocksize for the iv
		return (iCipher->MaxOutputLength(aMaxInputLength + iCipher->BlockSize()));
		}
    }

TInt CPBEncryptorSet::MaxFinalOutputLength(TUint aMaxInputLength) const
    {
	if(iIVSent)
		{
		return (iCipher->MaxFinalOutputLength(aMaxInputLength));
		}
	else 
		{//	If we've not sent the iv yet then its the length 
		//	from the cipher plus a blocksize for the iv
		return (iCipher->MaxFinalOutputLength(aMaxInputLength + iCipher->BlockSize()));
		}
    }

EXPORT_C CPBDecryptorSet* CPBDecryptorSet::NewL(const TPBECipher aCipher, 
	const TDesC8& aKey)
	{
	CPBDecryptorSet* self = NewLC(aCipher, aKey);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CPBDecryptorSet* CPBDecryptorSet::NewLC(const TPBECipher aCipher,
	const TDesC8& aKey)
	{
	CPBDecryptorSet* self = new(ELeave) CPBDecryptorSet;
	CleanupStack::PushL(self);
	TBuf8<KMaxBlockSizeSupported> fakeIV;
	fakeIV.SetLength(PBE::GetBlockBytes(aCipher));
	self->ConstructL(aCipher, aKey, fakeIV);
	return self;
	}

void CPBDecryptorSet::ConstructL(const TPBECipher aCipher, 
	const TDesC8& aKey, const TDesC8& aIV)
	{
	iCipher = PBE::MakeDecryptorL(aCipher, aKey, aIV);
	iIVBuf = HBufC8::NewL(PBE::GetBlockBytes(aCipher));
	}

void CPBDecryptorSet::Process(const TDesC8& aInput, TDes8& aOutput)
	{
	TPtrC8 input = ProcessIV(aInput);
	iCipher->Process(input, aOutput);
	}

void CPBDecryptorSet::ProcessFinalL(const TDesC8& aInput, TDes8& aOutput)
	{
	TPtrC8 input = ProcessIV(aInput);
	iCipher->ProcessFinalL(input, aOutput);
	}

TPtrC8 CPBDecryptorSet::ProcessIV(const TDesC8& aInput)
	{
	if(!iIVSent)
		{
		TPtr8 iIVBufPtr(iIVBuf->Des());
		if(aInput.Length() + iIVBufPtr.Length() < iCipher->BlockSize())
			{
			iIVBufPtr.Append(aInput);
			return TPtrC8();
			}
		else 
			{
			TInt rem = iCipher->BlockSize() - iIVBufPtr.Length();
			iIVBufPtr.Append(aInput.Mid(0, rem));
			CBufferedDecryptor* blockDecryptor = static_cast<CBufferedDecryptor*>(iCipher);
			static_cast<CModeCBCDecryptor*>(blockDecryptor->BlockTransformer())->SetIV(iIVBufPtr);
			iIVSent = ETrue;
			return aInput.Mid(rem);
			}
		}
	else 
		{
		return aInput;
		}
	}

CPBDecryptorSet::CPBDecryptorSet() : iIVSent(EFalse)
	{
	}

CPBDecryptorSet::~CPBDecryptorSet()
	{
	delete iCipher;
	//iCipher owns iCBCDecryptor we _don't_ need to delete it
	delete iIVBuf;
	}

TInt CPBDecryptorSet::MaxOutputLength(TUint aMaxInputLength) const
    {
	if(iIVSent)
		{
		return (iCipher->MaxOutputLength(aMaxInputLength));
		}
	else	//	if we haven't sent the iv yet, then the length the max length of the
		{	//	cipher minus a BlockSize where the IV was sitting (as the iv would be part
			//	of the length they've sent us)
		return (iCipher->MaxOutputLength(aMaxInputLength - iCipher->BlockSize()));
		}
    }

TInt CPBDecryptorSet::MaxFinalOutputLength(TUint aMaxInputLength) const
    {
	if(iIVSent)
		{
		return (iCipher->MaxFinalOutputLength(aMaxInputLength));
		}
	else	//	if we haven't sent the iv yet, then the length the max length of the
		{	//	cipher minus a BlockSize where the IV was sitting (as the iv would be part
			//	of the length they've sent us)
		return (iCipher->MaxFinalOutputLength(aMaxInputLength - iCipher->BlockSize()));
		}
    }
