/*
* Copyright (c) 2006-2010 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 "rijndaelimpl.h"
#include <cryptospi/keys.h>

#include "rijndaeltables.h"
#include "../../../source/common/inlines.h"
#include "pluginconfig.h"
#include "symmetriccipherimpl.h"
#include <cryptostrength.h>

using namespace SoftwareCrypto;

const TUint KAESKeyBytes128 = 16;
const TUint KAESKeyBytes192 = 24;
const TUint KAESKeyBytes256 = 32;
const TUint KAESBlockBytes = 16;

/* CRijndaelmpl*/
CRijndaelImpl::CRijndaelImpl(
	TUid aCryptoMode,
	TUid aOperationMode,
	TUid aPadding,
	TUid aImplementationUid) :
	CSymmetricBlockCipherImpl(KAESBlockBytes, aCryptoMode, aOperationMode, aPadding),
	iImplementationUid(aImplementationUid)
	{
	}

CRijndaelImpl* CRijndaelImpl::NewL(const CKey& aKey, TUid aCryptoMode,	TUid aOperationMode,
									TUid aPadding, TUid aImplementationUid)
	{
	CRijndaelImpl* self = CRijndaelImpl::NewLC(aKey, aCryptoMode, aOperationMode, 
												aPadding, aImplementationUid);
	CleanupStack::Pop(self);
	return self;
	}
	
CRijndaelImpl* CRijndaelImpl::NewLC(const CKey& aKey, TUid aCryptoMode, TUid aOperationMode,
									TUid aPadding, TUid aImplementationUid)
	{
	CRijndaelImpl* self = new(ELeave) CRijndaelImpl(aCryptoMode, aOperationMode, aPadding,
												aImplementationUid);
	CleanupStack::PushL(self);
	self->ConstructL(aKey);
	
	const TDesC8& keyContent = aKey.GetTDesC8L(KSymmetricKeyParameterUid);
	TCrypto::IsSymmetricWeakEnoughL(BytesToBits(keyContent.Size()) - keyContent.Size());
	return self;
	}
		
CRijndaelImpl::~CRijndaelImpl()
	{
	// make sure key information isn't visible to other processes if the
	// page is reused.
	Mem::FillZ(&iK, sizeof(iK));
	}
	
void CRijndaelImpl::ConstructL(const CKey& aKey)
	{
	CSymmetricBlockCipherImpl::ConstructL(aKey);			
	SetKeySchedule();
	}

CExtendedCharacteristics* CRijndaelImpl::CreateExtendedCharacteristicsL()
	{
	// All Symbian software plug-ins have unlimited concurrency, cannot be reserved
	// for exclusive use and are not CERTIFIED to be standards compliant.
	return CExtendedCharacteristics::NewL(KMaxTInt, EFalse);
	}

const CExtendedCharacteristics* CRijndaelImpl::GetExtendedCharacteristicsL()
	{
	return CRijndaelImpl::CreateExtendedCharacteristicsL();
	}

TUid CRijndaelImpl::ImplementationUid() const
	{
	return iImplementationUid;
	}
	
TBool CRijndaelImpl::IsValidKeyLength(TInt aKeyBytes) const
	{
	switch(aKeyBytes)
		{
		case KAESKeyBytes128:
		case KAESKeyBytes192:
		case KAESKeyBytes256:
			return ETrue;
		default:
			return EFalse;
		}			
	}
	
void CRijndaelImpl::SetKeySchedule()
	{
	iRounds = iKeyBytes/4 + 6;
	if (iCryptoMode.iUid == KCryptoModeEncrypt)
		{
		SetEncryptKeySchedule(*iKey, &iK[0]);
		}
	else 
		{
		ASSERT(iCryptoMode.iUid == KCryptoModeDecrypt);
		SetDecryptKeySchedule(*iKey, &iK[0]);
		}	
	}	

void CRijndaelImpl::TransformEncrypt(
	TUint8* aBuffer, 
	TUint aNumBlocks)
	{
	for (TInt i = 0; i < aNumBlocks; ++i)
		{		
		ModeEncryptStart(aBuffer);
			
		TUint32 s0, s1, s2, s3, t0, t1, t2, t3;
		const TUint32* rk = &iK[0];

	/*
	 *	map byte array block to cipher state
	 *	and add initial round key:
	*/
		GetBlockBigEndian(aBuffer, s0, s1, s2, s3);
		s0 ^= rk[0];
		s1 ^= rk[1];
		s2 ^= rk[2];
		s3 ^= rk[3];
	/*
	 *	Nr - 1 full rounds:
	*/
	    TUint r = iRounds >> 1;
		FOREVER
			{
	        t0 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(s0, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(s1, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(s2, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(s3, 0)] ^
	            rk[4];
	        t1 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(s1, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(s2, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(s3, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(s0, 0)] ^
	            rk[5];
	        t2 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(s2, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(s3, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(s0, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(s1, 0)] ^
	            rk[6];
	        t3 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(s3, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(s0, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(s1, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(s2, 0)] ^
	            rk[7];

	        rk += 8;
	        if (--r == 0) 
				break;
	        
	        s0 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(t0, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(t1, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(t2, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(t3, 0)] ^
	            rk[0];
	        s1 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(t1, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(t2, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(t3, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(t0, 0)] ^
	            rk[1];
	        s2 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(t2, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(t3, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(t0, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(t1, 0)] ^
	            rk[2];
	        s3 =
	            RIJNDAEL_TABLE::Te0[GETBYTE(t3, 3)] ^
	            RIJNDAEL_TABLE::Te1[GETBYTE(t0, 2)] ^
	            RIJNDAEL_TABLE::Te2[GETBYTE(t1, 1)] ^
	            RIJNDAEL_TABLE::Te3[GETBYTE(t2, 0)] ^
	            rk[3];
			}
	/*
	 *	apply last round and
	 *	map cipher state to byte array block:
	*/

		s0 =
			(RIJNDAEL_TABLE::Te4[GETBYTE(t0, 3)] & 0xff000000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t1, 2)] & 0x00ff0000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t2, 1)] & 0x0000ff00) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t3, 0)] & 0x000000ff) ^
			rk[0];
		s1 =
			(RIJNDAEL_TABLE::Te4[GETBYTE(t1, 3)] & 0xff000000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t2, 2)] & 0x00ff0000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t3, 1)] & 0x0000ff00) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t0, 0)] & 0x000000ff) ^
			rk[1];
		s2 =
			(RIJNDAEL_TABLE::Te4[GETBYTE(t2, 3)] & 0xff000000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t3, 2)] & 0x00ff0000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t0, 1)] & 0x0000ff00) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t1, 0)] & 0x000000ff) ^
			rk[2];
		s3 =
			(RIJNDAEL_TABLE::Te4[GETBYTE(t3, 3)] & 0xff000000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t0, 2)] & 0x00ff0000) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t1, 1)] & 0x0000ff00) ^
			(RIJNDAEL_TABLE::Te4[GETBYTE(t2, 0)] & 0x000000ff) ^
			rk[3];

		PutBlockBigEndian(aBuffer, s0, s1, s2, s3);
		ModeEncryptEnd(aBuffer);
		aBuffer += KAESBlockBytes;
		}
	}

void CRijndaelImpl::TransformDecrypt(
	TUint8* aBuffer,
	TUint aNumBlocks)
	{
	for (TInt i = 0; i < aNumBlocks; ++i)
		{		
		ModeDecryptStart(aBuffer);
		
		TUint32 s0, s1, s2, s3, t0, t1, t2, t3;
	    const TUint32* rk = &iK[0];

	/*
	 *	map byte array block to cipher state
	 *	and add initial round key:
	*/
		GetBlockBigEndian(aBuffer, s0, s1, s2, s3);

		s0 ^= rk[0];
		s1 ^= rk[1];
		s2 ^= rk[2];
		s3 ^= rk[3];
	/*
	 *	Nr - 1 full rounds:
	*/
	    TUint r = iRounds >> 1;
	    FOREVER
			{
	        t0 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(s0, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(s3, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(s2, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(s1, 0)] ^
	            rk[4];
	        t1 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(s1, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(s0, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(s3, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(s2, 0)] ^
	            rk[5];
	        t2 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(s2, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(s1, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(s0, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(s3, 0)] ^
	            rk[6];
	        t3 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(s3, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(s2, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(s1, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(s0, 0)] ^
	            rk[7];

	        rk += 8;
	        if (--r == 0)
	            break;
	        
	        s0 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(t0, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(t3, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(t2, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(t1, 0)] ^
	            rk[0];
	        s1 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(t1, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(t0, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(t3, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(t2, 0)] ^
	            rk[1];
	        s2 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(t2, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(t1, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(t0, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(t3, 0)] ^
	            rk[2];
	        s3 =
	            RIJNDAEL_TABLE::Td0[GETBYTE(t3, 3)] ^
	            RIJNDAEL_TABLE::Td1[GETBYTE(t2, 2)] ^
	            RIJNDAEL_TABLE::Td2[GETBYTE(t1, 1)] ^
	            RIJNDAEL_TABLE::Td3[GETBYTE(t0, 0)] ^
	            rk[3];
			}
	/*
	 *	apply last round and
	 *	map cipher state to byte array block:
	*/
	   	s0 =
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t0, 3)] & 0xff000000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t3, 2)] & 0x00ff0000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t2, 1)] & 0x0000ff00) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t1, 0)] & 0x000000ff) ^
	   		rk[0];
	   	s1 =
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t1, 3)] & 0xff000000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t0, 2)] & 0x00ff0000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t3, 1)] & 0x0000ff00) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t2, 0)] & 0x000000ff) ^
	   		rk[1];
	   	s2 =
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t2, 3)] & 0xff000000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t1, 2)] & 0x00ff0000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t0, 1)] & 0x0000ff00) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t3, 0)] & 0x000000ff) ^
	   		rk[2];
	   	s3 =
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t3, 3)] & 0xff000000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t2, 2)] & 0x00ff0000) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t1, 1)] & 0x0000ff00) ^
	   		(RIJNDAEL_TABLE::Td4[GETBYTE(t0, 0)] & 0x000000ff) ^
	   		rk[3];		
		PutBlockBigEndian(aBuffer, s0, s1, s2, s3);
		ModeDecryptEnd(aBuffer);
		aBuffer += KAESBlockBytes;
		}
	}

void CRijndaelImpl::SetEncryptKeySchedule(const TDesC8& aKey, TUint32* aKeySchedule)
	{		
	TUint keySize = aKey.Length();
	TUint32 temp; 
	TUint32* rk = aKeySchedule;

	TUint i = 0;

	GetUserKeyBigEndian(rk, keySize/4, &aKey[0], keySize);

	switch(keySize)
		{
		case (KAESKeyBytes128):
			{
			FOREVER
				{
				temp  = rk[3];
				rk[4] = rk[0] ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 2)] & 0xff000000) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 3)] & 0x000000ff) ^
					RIJNDAEL_TABLE::rcon[i];
				rk[5] = rk[1] ^ rk[4];
				rk[6] = rk[2] ^ rk[5];
				rk[7] = rk[3] ^ rk[6];
				if (++i == 10)
					break;
				rk += 4;
				}
			}
		break;

		case (KAESKeyBytes192):
			{
			FOREVER
				{
				temp = rk[ 5];
				rk[ 6] = rk[ 0] ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 2)] & 0xff000000) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^
					(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 3)] & 0x000000ff) ^
					RIJNDAEL_TABLE::rcon[i];
				rk[ 7] = rk[ 1] ^ rk[ 6];
				rk[ 8] = rk[ 2] ^ rk[ 7];
				rk[ 9] = rk[ 3] ^ rk[ 8];
				if (++i == 8)
					break;
				rk[10] = rk[ 4] ^ rk[ 9];
				rk[11] = rk[ 5] ^ rk[10];
				rk += 6;
				}
			}
		break;

		case (KAESKeyBytes256):
			{
			FOREVER
				{
        		temp = rk[ 7];
        		rk[ 8] = rk[ 0] ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 2)] & 0xff000000) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 3)] & 0x000000ff) ^
        			RIJNDAEL_TABLE::rcon[i];
        		rk[ 9] = rk[ 1] ^ rk[ 8];
        		rk[10] = rk[ 2] ^ rk[ 9];
        		rk[11] = rk[ 3] ^ rk[10];
				if (++i == 7)
					break;
        		temp = rk[11];
        		rk[12] = rk[ 4] ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 3)] & 0xff000000) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 2)] & 0x00ff0000) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 1)] & 0x0000ff00) ^
        			(RIJNDAEL_TABLE::Te4[GETBYTE(temp, 0)] & 0x000000ff);
        		rk[13] = rk[ 5] ^ rk[12];
        		rk[14] = rk[ 6] ^ rk[13];
        		rk[15] = rk[ 7] ^ rk[14];

				rk += 8;
				}
			}
		break;

		default:
			assert(0);	//	Shouldn't get here, keeps compiler happy
		}
	}

void CRijndaelImpl::SetDecryptKeySchedule(const TDesC8& aKey, TUint32* aKeySchedule)
	{
	SetEncryptKeySchedule(aKey, aKeySchedule);

	TUint i, j;
	TUint32* rk = aKeySchedule;
	TUint32 temp;

	// invert the order of the round keys 
	for (i = 0, j = 4*iRounds; i < j; i += 4, j -= 4)
		{
		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
		temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
		}

	// apply the inverse MixColumn transform to all round keys but the first and the last
	for (i = 1; i < iRounds; i++)
		{
		rk += 4;
		rk[0] =
			RIJNDAEL_TABLE::Td0[RIJNDAEL_TABLE::Te4[GETBYTE(rk[0], 3)] & 0xff] ^
			RIJNDAEL_TABLE::Td1[RIJNDAEL_TABLE::Te4[GETBYTE(rk[0], 2)] & 0xff] ^
			RIJNDAEL_TABLE::Td2[RIJNDAEL_TABLE::Te4[GETBYTE(rk[0], 1)] & 0xff] ^
			RIJNDAEL_TABLE::Td3[RIJNDAEL_TABLE::Te4[GETBYTE(rk[0], 0)] & 0xff];
		rk[1] =
			RIJNDAEL_TABLE::Td0[RIJNDAEL_TABLE::Te4[GETBYTE(rk[1], 3)] & 0xff] ^
			RIJNDAEL_TABLE::Td1[RIJNDAEL_TABLE::Te4[GETBYTE(rk[1], 2)] & 0xff] ^
			RIJNDAEL_TABLE::Td2[RIJNDAEL_TABLE::Te4[GETBYTE(rk[1], 1)] & 0xff] ^
			RIJNDAEL_TABLE::Td3[RIJNDAEL_TABLE::Te4[GETBYTE(rk[1], 0)] & 0xff];
		rk[2] =
			RIJNDAEL_TABLE::Td0[RIJNDAEL_TABLE::Te4[GETBYTE(rk[2], 3)] & 0xff] ^
			RIJNDAEL_TABLE::Td1[RIJNDAEL_TABLE::Te4[GETBYTE(rk[2], 2)] & 0xff] ^
			RIJNDAEL_TABLE::Td2[RIJNDAEL_TABLE::Te4[GETBYTE(rk[2], 1)] & 0xff] ^
			RIJNDAEL_TABLE::Td3[RIJNDAEL_TABLE::Te4[GETBYTE(rk[2], 0)] & 0xff];
		rk[3] =
			RIJNDAEL_TABLE::Td0[RIJNDAEL_TABLE::Te4[GETBYTE(rk[3], 3)] & 0xff] ^
			RIJNDAEL_TABLE::Td1[RIJNDAEL_TABLE::Te4[GETBYTE(rk[3], 2)] & 0xff] ^
			RIJNDAEL_TABLE::Td2[RIJNDAEL_TABLE::Te4[GETBYTE(rk[3], 1)] & 0xff] ^
			RIJNDAEL_TABLE::Td3[RIJNDAEL_TABLE::Te4[GETBYTE(rk[3], 0)] & 0xff];
		}
	}
