/*
* Copyright (c) 2008-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: 
* Software Mac Implementation
* plugin-dll headers
*
*/


/**
 @file
*/
#include "cmacimpl.h"
#include "pluginconfig.h"
#include <cryptospi/cryptomacapi.h>
/**
 * Headers from CryptoSpi framework
 */
#include <cryptospi/cryptospidef.h>

using namespace SoftwareCrypto;
using namespace CryptoSpi;

/**
 * Constants used to generate Key1, Key2 and Key3
 */
const TUint8 K1Constant[] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
const TUint8 K2Constant[] = {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
const TUint8 K3Constant[] = {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};

const TInt KAesXcbcMac96Size = 12;


CCMacImpl* CCMacImpl::NewL(const CKey& aKey, CSymmetricCipher* aSymmetricCipher, TInt32 aAlgorithmUid)
	{
	CCMacImpl* self = CCMacImpl::NewLC(aKey, aSymmetricCipher, aAlgorithmUid);
	CleanupStack::Pop(self);
	return self;
	}
														
CCMacImpl* CCMacImpl::NewLC(const CKey& aKey, CSymmetricCipher* aSymmetricCipher, TInt32 aAlgorithmUid)
	{
	CCMacImpl* self = NULL;
 	TRAPD(err, self = new (ELeave) CCMacImpl(aSymmetricCipher));
  	if(err!=KErrNone)
  		{
  		delete aSymmetricCipher;
  		User::Leave(err);
  		}
	CleanupStack::PushL(self);
	self->ConstructL(aKey, aAlgorithmUid);
	return self;
	}

CKey* CCMacImpl::Create128bitKeyL(const CKey& aKey)
	{
	TBuf8<KMacBlockSize> keybuffer;
	CryptoSpi::CKey* key = NULL;
	
	const TDesC8& keyContent=aKey.GetTDesC8L(CryptoSpi::KSymmetricKeyParameterUid);

	if( (TUint32)keyContent.Size() > KMacBlockSize)
		{
		// Create key
		CryptoSpi::CCryptoParams* keyParams = CryptoSpi::CCryptoParams::NewLC();
		keybuffer.SetLength(KMacBlockSize);
		keybuffer.FillZ();
		// 'keybuffer' is the key with 128 zero bits.
		keyParams->AddL(keybuffer, CryptoSpi::KSymmetricKeyParameterUid);
		key=CryptoSpi::CKey::NewLC(aKey.KeyProperty(),*keyParams);
		// evaluate final key data.
		SetKeyL(*key);
		CleanupStack::PopAndDestroy(2, keyParams);
		keybuffer.Copy(FinalL(keyContent));
		// 'keybuffer' contains the final key data.
		}
	else 
		{
		keybuffer.Copy(keyContent);
		TUint i;
		for (i=keybuffer.Size();i<KMacBlockSize;++i)
			{
			keybuffer.Append(0);
			}
		// 'keybuffer' contains the final key data.
		}
	
	// create a new CKey instance and assign it to iKey using 'keybuffer'.
	CryptoSpi::CCryptoParams* keyParams = CryptoSpi::CCryptoParams::NewLC();
	keyParams->AddL(keybuffer, CryptoSpi::KSymmetricKeyParameterUid);
	key=CryptoSpi::CKey::NewL(aKey.KeyProperty(),*keyParams);
	CleanupStack::PopAndDestroy(keyParams);	

	// 'key' will contain the final CKey instance.
	return key;
	}

void CCMacImpl::SetKeyL(const CKey& aKey)
	{
	const TPtrC8 KeyConstant1(K1Constant, KMacBlockSize);
	const TPtrC8 KeyConstant2(K2Constant, KMacBlockSize);
	const TPtrC8 KeyConstant3(K3Constant, KMacBlockSize);

	// Initialize the cipher class to encrypt Keyconstants to generate additional keys.
	if (iImplementationUid == CryptoSpi::KAlgorithmCipherAesXcbcPrf128)
		{
		// RFC 4434: keys that were not equal in length to 128 bits will no longer be
		// rejected but instead will be made 128 bits for AES-XCBC-PRF-128 Algorithm only.
		CryptoSpi::CKey* key = Create128bitKeyL(aKey);
		CleanupStack::PushL(key);
		iCipherImpl->SetKeyL(*key);
		CleanupStack::PopAndDestroy(key);	
		}
	else
		{
		iCipherImpl->SetKeyL(aKey);
		}
	iCipherImpl->SetCryptoModeL(CryptoSpi::KCryptoModeEncryptUid);
	iCipherImpl->SetOperationModeL(CryptoSpi::KOperationModeNoneUid);

	// cipher class expects the output buffer to be empty.
	iKey1.Zero();
	iKey2.Zero();
	iKey3.Zero();

	// aKey is used to generate Key1, Key2 and Key3.
	// Where Key1 = encrypt KeyConstant1 with aKey
	// Where Key2 = encrypt KeyConstant2 with aKey
	// Where Key3 = encrypt KeyConstant3 with aKey
	
	// Key1 is used to encrypt the data whereas
	// Key2 and Key3 is used to XOR with the last 
	// block.
    iCipherImpl->ProcessFinalL(KeyConstant1, iKey1);
	iCipherImpl->ProcessFinalL(KeyConstant2, iKey2);
	iCipherImpl->ProcessFinalL(KeyConstant3, iKey3);
	
	// Create CKey instance with key1
	CCryptoParams* keyParam =CCryptoParams::NewLC();
 	keyParam->AddL(iKey1, CryptoSpi::KSymmetricKeyParameterUid);

 	delete iKey;
 	iKey = NULL;
 	iKey=CKey::NewL(aKey.KeyProperty(), *keyParam);
 	// Initialize the cipher class for MAC calculation.
	iCipherImpl->SetKeyL(*iKey);
 	iCipherImpl->SetOperationModeL(CryptoSpi::KOperationModeCBCUid);
 	Mem::FillZ(iE, sizeof(iE));
 	iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize));

 	CleanupStack::PopAndDestroy(keyParam);
	}

CCMacImpl::~CCMacImpl()
	{
	delete iKey;
	delete iCipherImpl;
	}

CCMacImpl::CCMacImpl(const CCMacImpl& aCCMacImpl)
	{
	iImplementationUid = aCCMacImpl.iImplementationUid;
	iKey1.Copy(aCCMacImpl.iKey1);
	iKey2.Copy(aCCMacImpl.iKey2);
	iKey3.Copy(aCCMacImpl.iKey3);
	
	(void)Mem::Copy(iE, aCCMacImpl.iE, sizeof(iE));
	(void)Mem::Copy(iData, aCCMacImpl.iData, sizeof(iData));
	
	iCurrentTotalLength = aCCMacImpl.iCurrentTotalLength;
	}

const CExtendedCharacteristics* CCMacImpl::GetExtendedCharacteristicsL()
	{
	return iCipherImpl->GetExtendedCharacteristicsL();
	}

CCMacImpl::CCMacImpl(CryptoSpi::CSymmetricCipher* aSymmetricCipher)
	{
	iCipherImpl = aSymmetricCipher;
	aSymmetricCipher = NULL;
	iMacValue.SetLength(KMacBlockSize);
	}

void CCMacImpl::ConstructL(const CKey& aKey, TInt32 aAlgorithmUid) 
	{
	iImplementationUid = aAlgorithmUid;
	
    switch(aAlgorithmUid)
    	{
    	case CryptoSpi::KAlgorithmCipherAesXcbcMac96:
    	case CryptoSpi::KAlgorithmCipherAesXcbcPrf128:
    		{
    		SetKeyL(aKey);
     		break;
    		}
    	default:
    		{
    		User::Leave(KErrNotSupported);
    		}
    	}
	}

/**
 * Takes the message and XOR it with iData.
 * 
 * @param aKey 128bit key. This key will be XORed with iData.
 * @param aOutput  The result of the XOR operation will be copied to this.
 * 				   Its length should be 128bit (16bytes).
 */

void CCMacImpl::XORKeyWithData(const TDesC8& aKey, TDes8& aOutput)
	{
	for (TInt i = 0; i < KMacBlockSize; ++i)
		{
		aOutput[i] = iData[i] ^ aKey[i];
		}
	}

/**
 * This function is used to pad message M to make the total message
 * length multiple of block size (128bit). The last block M[n] will be 
 * padded with a single "1" bit followed by the number of "0" bits required
 * to increase M[n]'s size to 128 bits (Block Size).
 * 
 * Used in AES-XCBC-MAC-96 and AES-XCBC-PRF-128 Mac algorithms.
 */
void CCMacImpl::PadMessage()
	{
	if(iCurrentTotalLength < KMacBlockSize)
		{
		iData[iCurrentTotalLength] = 0x80;
		Mem::FillZ(iData + iCurrentTotalLength+1, KMacBlockSize - iCurrentTotalLength - 1);
		}
	}

void CCMacImpl::Reset()
	{
	Mem::FillZ(iE,sizeof(iE));
	iCurrentTotalLength =0;
	// record for Reset, for the next time MacL, UpdateL or FinalL is called as we
	// cannot leave in Reset.
	TRAP(iDelayedReset, iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize)));
	}

TPtrC8 CCMacImpl::MacL(const TDesC8& aMessage)
	{
	// Reset the cipher with iE as 128 zero bits as it leaved in previous call to Reset. 
	if (iDelayedReset != KErrNone)
		{
		// iE was reset to 128 zero bits in previous call to Reset which leaved.
		iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize));
		iDelayedReset = KErrNone; 
		}
	
	if (aMessage!=KNullDesC8())
		{
		DoUpdateL(aMessage);			
		}
	
	// Calculate MAC
	TPtrC8 macPtr(KNullDesC8());
	macPtr.Set(DoFinalL());

	// Restore the internal state.
	// We don't want to save any state change happened in 
	// DoFinalL.
	// iE is not updated in DoFinalL function and hence
	// can be used to reset iCipherImpl to previous state.
	iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize));
	
	return macPtr;		
	}

TPtrC8 CCMacImpl::FinalL(const TDesC8& aMessage)
	{
	// Reset the cipher with iE as 128 zero bits as it leaved in previous call to Reset. 
	if (iDelayedReset == KErrNone)
		{
		// iE was reset to 128 zero bits in previous call to Reset which leaved.
		iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize));
		iDelayedReset = KErrNone;
		}

	if (aMessage!=KNullDesC8())
		{
		DoUpdateL(aMessage);			
		}
	TPtrC8 macPtr(KNullDesC8());
	macPtr.Set(DoFinalL());
	Reset();
	return macPtr;
	}

void CCMacImpl::UpdateL(const TDesC8& aMessage)
	{
	// Reset the cipher with iE as 128 zero bits as it leaved in previous call to Reset. 
	if (iDelayedReset == KErrNone)
		{
		// iE was reset to 128 zero bits in previous call to Reset which leaved.
		iCipherImpl->SetIvL(TPtrC8(iE, KMacBlockSize));
		iDelayedReset = KErrNone;
		}

	if (aMessage!=KNullDesC8())
		{
		DoUpdateL(aMessage);			
		}
	}

void CCMacImpl::ProcessBlockL()
	{
	TPtrC8 dataPtr(iData, KMacBlockSize);
	TPtr8 intermediateCipherPtr(iE,0,KMacBlockSize);
	// iData (Block) should be XORed with iE calculated
	// from previoue processing. If it's the first processing
	// then iE will be zero.
	// Here we are not doing explicit XORing because iCpherImpl 
	// is set in CBC mode. Therefore this operation will be
	// done by iCipherImpl
	iCipherImpl->ProcessL(dataPtr, intermediateCipherPtr);
	// After processing discard the block.
	iCurrentTotalLength = 0;
	}

void CCMacImpl::DoUpdateL(const TDesC8& aMessage)
	{
	TInt curLength = aMessage.Length();
	const TUint8* msgPtr = aMessage.Ptr();
	
	while(curLength > 0)
		{
		// If block is formed then process it.
		if(iCurrentTotalLength == KMacBlockSize)
			ProcessBlockL();
		
		// Check the space left in the block.
		TUint remainingLength = KMacBlockSize - iCurrentTotalLength;
		// If unprocesed message length is less then remainingLength
		// then copy the entire data to iData else copy till iData
		// if full.
		TUint length = Min(curLength, remainingLength);
		Mem::Copy(iData+iCurrentTotalLength, msgPtr, length);
		// Update data offset
		iCurrentTotalLength += length;
		curLength -= length;
		msgPtr += length;
		}
 	}

TPtrC8 CCMacImpl::DoFinalL()
	{
	TBuf8<KMacBlockSize> finalBlock;
	finalBlock.SetLength(KMacBlockSize);
	
	// If padding is required then use Key3
	// else use Key2.
	if(iCurrentTotalLength < KMacBlockSize)
		{
		PadMessage();
		XORKeyWithData(iKey3, finalBlock);
		}
	else
		{
		XORKeyWithData(iKey2, finalBlock);
		}

	// cipher class expects the output buffer to be empty.
	iMacValue.Zero();

	iCipherImpl->ProcessFinalL(finalBlock, iMacValue);
	
    return (iImplementationUid == CryptoSpi::KAlgorithmCipherAesXcbcMac96)? iMacValue.Left(KAesXcbcMac96Size): TPtrC8(iMacValue);
	}

void CCMacImpl::ReInitialiseAndSetKeyL(const CKey& aKey)
	{
	Reset();
	SetKeyL(aKey);
	}


CCMacImpl* CCMacImpl::CopyL()
	{
	CCMacImpl* clone = new(ELeave) CCMacImpl(*this);
	CleanupStack::PushL(clone);
	clone->iKey = CKey::NewL(*iKey);
	CryptoSpi::CSymmetricCipherFactory::CreateSymmetricCipherL(clone->iCipherImpl,
												CryptoSpi::KAesUid,
												*iKey,
												CryptoSpi::KCryptoModeEncryptUid,
												CryptoSpi::KOperationModeCBCUid,
												CryptoSpi::KPaddingModeNoneUid,
												NULL);
	clone->iCipherImpl->SetIvL(TPtrC8(clone->iE, KMacBlockSize));
	CleanupStack::Pop();
	return clone;	
	}
	
CCMacImpl* CCMacImpl::ReplicateL()
	{
	CCMacImpl* replica = CopyL();
	replica->Reset();
	return replica;
	}
