/*
* Copyright (c) 2005-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 "pkcs12macdata.h"

using namespace PKCS12;

CDecPkcs12MacData::CDecPkcs12MacData()
	{
	}
	
EXPORT_C CDecPkcs12MacData* CDecPkcs12MacData::NewL(const TDesC8& aMacData, const TDesC8& aAuthSafeData)
	{
	CDecPkcs12MacData* self = new(ELeave) CDecPkcs12MacData();
	CleanupStack::PushL(self);
	self->ConstructL(aMacData, aAuthSafeData);
	CleanupStack::Pop(self);
	return self;
	}

CDecPkcs12MacData::~CDecPkcs12MacData()
	{
	delete iDigestInfo;
	}
	
void CDecPkcs12MacData::ConstructL(const TDesC8& aMacData, const TDesC8& aAuthSafeData)
	{
	iAuthSafeDataPtr.Set(aAuthSafeData);
		
	// MacData is a Sequence
	TASN1DecGeneric seqGen(aMacData);
	seqGen.InitL();
	if (seqGen.Tag() != EASN1Sequence || seqGen.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
		
	// Decode the MacData Sequence
	TASN1DecSequence seq;
	CArrayPtr<TASN1DecGeneric>* macDataSequence = seq.DecodeDERLC(seqGen);
	TInt macDataCount = macDataSequence->Count();
	// Check if Mac, MacSalt and Iteration Count are present
	// Iteration Count is 1 by default
	if (macDataCount < 2 || macDataCount > 3)
		{
		User::Leave(KErrArgument);
		}
	
	// DigestInfo
	const TASN1DecGeneric* macDataSequenceAt0 = macDataSequence->At(0);
	// DigestInfo is a Sequence
	if (macDataSequenceAt0->Tag() != EASN1Sequence || macDataSequenceAt0->Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	iDigestInfo = CPKCS7DigestInfo::NewL(macDataSequenceAt0->Encoding());
		
	// MacSalt
	const TASN1DecGeneric* macDataSequenceAt1 = macDataSequence->At(1);
	// MacSalt is an OctetString
	if (macDataSequenceAt1->Tag() != EASN1OctetString || macDataSequenceAt1->Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}  
    iMacSalt.Set(macDataSequenceAt1->GetContentDER());
    
    // Iteration Count
	if(macDataSequence->Count() == 3)
		{
		// Check for IterationCount
		const TASN1DecGeneric* macDataSequenceAt2 = macDataSequence->At(2);
		if (macDataSequenceAt2->Tag() != EASN1Integer || macDataSequenceAt2->Class() != EUniversal)
			{
			User::Leave(KErrArgument);
			}
		
		TASN1DecInteger intDecoder;
		iIterationCount = intDecoder.DecodeDERShortL(*macDataSequenceAt2);
		if(iIterationCount <= 0)
			{
			User::Leave(KErrArgument);
			}
		}
    // Assign the Default value as 1 if Iteration Count is not present.
	else
		{
		iIterationCount = KDefaultIterationCount;
		}	
	CleanupStack::PopAndDestroy(macDataSequence);
    }
	
EXPORT_C const CPKCS7DigestInfo& CDecPkcs12MacData::DigestInfo() const
	{
	return *iDigestInfo;
	}
	
EXPORT_C const TDesC8& CDecPkcs12MacData::MacSalt() const
	{
	return iMacSalt;	
	}
	
EXPORT_C TInt CDecPkcs12MacData::IterationCount() const
	{
	return iIterationCount;	
	}

EXPORT_C TBool CDecPkcs12MacData::VerifyIntegrityL(const TDesC& aPassword) const
	{	
	__UHEAP_MARK;
	HBufC8* encryptKey = HBufC8::NewMaxLC(KSha1HmacKeyLength);
	TPtr8 encryptedKey(encryptKey->Des());
	
	// Convert the supplied string to a byte string. Each character is converted to a big 
	// endian two-byte value, and a terminating NULL character is appended to the end.
	HBufC8* password = PKCS12KDF::GeneratePasswordLC(aPassword);
		
	PKCS12KDF::DeriveKeyL(encryptedKey, PKCS12KDF::EIDByteMACKey, *password ,MacSalt(), IterationCount());
	
	CMessageDigest* digest1 = NULL;
	TAlgorithmId algorithmId = DigestInfo().Algorithm();
	if (algorithmId != ESHA1)
		{
		User::Leave(KErrNotSupported);
		}
	digest1 = CSHA1::NewL();
	CleanupStack::PushL( digest1 );
	
	CHMAC* hmac1 = CHMAC::NewL( encryptedKey, digest1);
	CleanupStack::PushL( hmac1 );
	
	TPtrC8 tmpHash = hmac1->Final(iAuthSafeDataPtr);
	TInt ret = tmpHash.Compare(DigestInfo().Digest());
	TBool result;
	if(ret)
		{
		result = EFalse;
		}
	else
		{
		result = ETrue;
		}
	
	CleanupStack::PopAndDestroy(hmac1);
	CleanupStack::Pop(digest1);
	CleanupStack::PopAndDestroy(2,encryptKey); // encryptKey, password
			
	__UHEAP_MARKEND;

	return result;	
	}
	
