/*
* 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 "pkcs12bags.h"

using namespace PKCS12;
///////////////////////// KeyBag ////////////////////////////////	

CDecPkcs12KeyBag::CDecPkcs12KeyBag()
	{
	}
	
EXPORT_C CDecPkcs12KeyBag* CDecPkcs12KeyBag::NewL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12KeyBag* self = new(ELeave) CDecPkcs12KeyBag;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeBagData);
	CleanupStack::Pop(self);
	return self;
	}

CDecPkcs12KeyBag::~CDecPkcs12KeyBag()
	{		
	}
	
void CDecPkcs12KeyBag::ConstructL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12SafeBag::ConstructL(aSafeBagData);	
	}
	
EXPORT_C CDecPKCS8Data* CDecPkcs12KeyBag::PrivateKeyInfoL() const
	{
	TASN1DecGeneric seqGen(iBagValue);
	seqGen.InitL();
	if (seqGen.Tag() != EASN1Sequence || seqGen.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
			
	return TASN1DecPKCS8::DecodeDERL(seqGen.Encoding());
	}
		
////////////////////////// ShroudedKeyBag ////////////////////////	
CDecPkcs12ShroudedKeyBag::CDecPkcs12ShroudedKeyBag()
	{
	}

EXPORT_C CDecPkcs12ShroudedKeyBag* CDecPkcs12ShroudedKeyBag::NewL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12ShroudedKeyBag* self = new(ELeave) CDecPkcs12ShroudedKeyBag;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeBagData);
	CleanupStack::Pop(self);
	return self;
	}

CDecPkcs12ShroudedKeyBag::~CDecPkcs12ShroudedKeyBag()
	{
	}
	
void CDecPkcs12ShroudedKeyBag::ConstructL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12SafeBag::ConstructL(aSafeBagData);
	TASN1DecGeneric seqGen(iBagValue);
	seqGen.InitL();
	if (seqGen.Tag() != EASN1Sequence || seqGen.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	
	TASN1DecSequence dec;
	CArrayPtrFlat<TASN1DecGeneric>* shroudedKeyBagSeq = dec.DecodeDERLC(seqGen);
	TInt seqIndex = 0;
	TInt shroudedKeyBagSeqCount = shroudedKeyBagSeq->Count();
	if (seqIndex >= shroudedKeyBagSeqCount)
		{
		User::Leave(KErrArgument);		
		}
	CleanupStack::PopAndDestroy(shroudedKeyBagSeq);
	}

EXPORT_C CDecPKCS8Data* CDecPkcs12ShroudedKeyBag::PrivateKeyInfoL(TDesC& aPassword) const
	{
	TASN1DecGeneric seqGen(iBagValue);
	seqGen.InitL();
	TASN1DecSequence dec;
	CArrayPtrFlat<TASN1DecGeneric>* shroudedKeyBagSeq = dec.DecodeDERLC(seqGen);
	// Get the first part of the sequence -> PKCS5 data
	const TASN1DecGeneric* shroudedKeyBagSeqAt0 = shroudedKeyBagSeq->At(0);
	TPtrC8 theContent(shroudedKeyBagSeqAt0->Encoding());//	expect this to be a sequence
	CPBEncryptParms* encryptParams =  TASN1DecPKCS5::DecodeDERL(theContent);
	CleanupStack::PushL(encryptParams);
	
	CPBEncryptElement* encryptElement;
	HBufC8* pkcs12Pwd = PKCS12KDF::GeneratePasswordLC(aPassword);
	if(encryptParams->Kdf() == CPBEncryptParms::EKdfPkcs12)
		{
	 	TPtrC8 iv = encryptParams->IV();
	    HBufC8* ivValue = HBufC8::NewMaxLC(iv.Length());
	    TPtr8 encryptKeyBuf = ivValue->Des();
	 		 
	 	switch(encryptParams->Cipher())	
	 		{
	 		case ECipherARC4_128:
	 		case ECipherARC4_40:
				{
	 		    //derive only key it is unnecessary to derive an IV for RC4
				break;
				}
			case ECipher3DES_CBC:
			case ECipher2Key3DES_CBC:
			case ECipherRC2_CBC_128_16:	 
			case ECipherRC2_CBC_40_5:	
				{
			    PKCS12KDF::DeriveKeyL(encryptKeyBuf, PKCS12KDF::EIDByteIV, *pkcs12Pwd, encryptParams->Salt(), encryptParams->Iterations());
				encryptParams->SetIV(encryptKeyBuf);
			    break;
				}
			default:
				{
			    User::Leave(KErrNotSupported);
			    break;
				}
	 		}
	 	CleanupStack::PopAndDestroy(ivValue);
		// Create the decryptor	
		encryptElement = CPBEncryptElement::NewLC(*pkcs12Pwd, *encryptParams);
		}
	else
		{
		TPBPassword password(aPassword);
		// Create the decryptor	
		encryptElement = CPBEncryptElement::NewLC(password.Password(), *encryptParams);	
		}
	CPBDecryptor* decryptor = encryptElement->NewDecryptLC();
	// Decrypt the final part of the sequence -> encrypted PKCS8 object
	const TASN1DecGeneric* shroudedKeyBagSeqAt1 = shroudedKeyBagSeq->At(1);
	if (shroudedKeyBagSeqAt1->Tag() != EASN1OctetString || shroudedKeyBagSeqAt1->Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	TPtrC8 encryptedKey(shroudedKeyBagSeqAt1->GetContentDER());
	TUint encryptLength = encryptedKey.Length();
	TUint maxDecryptLength = decryptor->MaxOutputLength(encryptLength);
	if ( maxDecryptLength <= 0 )
		{
		User::Leave(KErrGeneral);		
		}
	HBufC8* decryptedContent = HBufC8::NewLC(encryptLength);
	TPtr8 dcDes(decryptedContent->Des());
	decryptor->Process(encryptedKey, dcDes);
	
	CDecPKCS8Data* privateKeyInfo = TASN1DecPKCS8::DecodeDERL(dcDes);	
		
	CleanupStack::PopAndDestroy(6,shroudedKeyBagSeq);//shroudedKeyBagSeq, encryptParams,pkcs12Pwd
													// encryptElement, decryptor, decryptedContent.
	return privateKeyInfo;
	}
	
///////////////////////////// CertBag ///////////////////////////////

CDecPkcs12CertBag::CDecPkcs12CertBag()
	{
	}
	
EXPORT_C CDecPkcs12CertBag* CDecPkcs12CertBag::NewL(const TDesC8& aSafeBagData) 
    {
    CDecPkcs12CertBag* self = new (ELeave) CDecPkcs12CertBag;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeBagData);
	CleanupStack::Pop(self);
	return self;
	}

CDecPkcs12CertBag::~CDecPkcs12CertBag()
	{	
	delete iCertId;
	}

void CDecPkcs12CertBag::ConstructL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12SafeBag::ConstructL(aSafeBagData);
	
	TASN1DecGeneric seqGen(iBagValue);
	seqGen.InitL();
		if (seqGen.Tag() != EASN1Sequence || seqGen.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	
    TASN1DecSequence seq;
	CArrayPtr<TASN1DecGeneric>* certBagSequence = seq.DecodeDERLC(seqGen);
  	const TASN1DecGeneric* certBagSequenceAt0 = certBagSequence->At(0);
  	if (certBagSequenceAt0->Tag() != EASN1ObjectIdentifier || certBagSequenceAt0->Class() != EUniversal)
  		{
		User::Leave(KErrArgument);
		}
	  			
  	// Obtain the CertId		
  	TASN1DecObjectIdentifier oid;
  	iCertId = oid.DecodeDERL(*certBagSequenceAt0);

	const TASN1DecGeneric* certBagSequenceAt1 = certBagSequence->At(1);
	if (certBagSequenceAt1->Tag() != EASN1EOC || certBagSequenceAt1->Class() != EContextSpecific)
  		{
		User::Leave(KErrArgument);
		}
	
	TASN1DecGeneric certBagSeq(certBagSequenceAt1->GetContentDER());
	certBagSeq.InitL();
	if (certBagSeq.Tag() != EASN1OctetString || certBagSeq.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	else 
		{
		iCertValue.Set(certBagSeq.GetContentDER());
		}
    CleanupStack::PopAndDestroy(certBagSequence);
	}
	
EXPORT_C const TDesC& CDecPkcs12CertBag::CertId() const
	{
	return *iCertId;	
   	}

EXPORT_C const TDesC8& CDecPkcs12CertBag::CertValue() const
   	{
   	return iCertValue;		
   	}
   	
EXPORT_C CX509Certificate* CDecPkcs12CertBag::X509CertificateL() const
	{
	return (*iCertId == KX509CertificateOID) ? CX509Certificate::NewL(iCertValue) : NULL;
	}

/////////////////////// SafeContentsBag ///////////////////////

CDecPkcs12SafeContentsBag::CDecPkcs12SafeContentsBag()
	{
	}
	
EXPORT_C CDecPkcs12SafeContentsBag* CDecPkcs12SafeContentsBag::NewL(const TDesC8& aSafeContentsBagData)
	{
	CDecPkcs12SafeContentsBag* self = new(ELeave) CDecPkcs12SafeContentsBag;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeContentsBagData);
	CleanupStack::Pop(self);
	return self;
	}

CDecPkcs12SafeContentsBag::~CDecPkcs12SafeContentsBag()
	{
	iSafeBags.ResetAndDestroy();
	iSafeBags.Close();
	}

void CDecPkcs12SafeContentsBag::ConstructL(const TDesC8& aSafeBagData)
	{
	CDecPkcs12SafeBag::ConstructL(aSafeBagData);
	// This is SafeBag Sequence containing a SafeContents Bag
	TASN1DecGeneric seqGen(iBagValue);
	seqGen.InitL();
	
	// Check if this is a Sequence
	if (seqGen.Tag() != EASN1Sequence || seqGen.Class() != EUniversal)
		{
		User::Leave(KErrArgument);
		}
	
	TASN1DecSequence seq;
	CArrayPtrFlat<TASN1DecGeneric>* safeContentsBagSeq = seq.DecodeDERLC(seqGen);
			        
    // Find out the number of SafeBags present in the SafeContents Bag
    TInt safeContentsBagCount = safeContentsBagSeq->Count();
    const TASN1DecGeneric* safeContentsBagSeqAtPos;
    for (TInt index = 0; index < safeContentsBagCount; index++)
   		{
   		safeContentsBagSeqAtPos = safeContentsBagSeq->At(index);
   	 	if (safeContentsBagSeqAtPos->Tag() != EASN1Sequence || safeContentsBagSeqAtPos->Class() != EUniversal)
			{
			User::Leave(KErrArgument);
			}
					
		const TDesC8& safeBag(safeContentsBagSeqAtPos->Encoding());
  		// Decode this sequence, This is a SafeBag.
  		CDecPkcs12SafeBag* safeBagObject = CDecPkcs12SafeBag::NewL(safeBag);
  		CleanupStack::PushL(safeBagObject);
   	 	iSafeBags.AppendL(safeBagObject);
   	 	CleanupStack::Pop(safeBagObject);
   		}
   	CleanupStack::PopAndDestroy(safeContentsBagSeq);
    }

EXPORT_C const RPointerArray<CDecPkcs12SafeBag>& CDecPkcs12SafeContentsBag::SafeBags() const
	 {
	 return iSafeBags;
	 } 
	 
///////////////////////////// Safe Contents ///////////////////////////////
CDecPkcs12SafeContents::CDecPkcs12SafeContents()
	{
	}

CDecPkcs12SafeContents::~CDecPkcs12SafeContents()
	{
	delete iDecryptedData;
	iSafeBags.ResetAndDestroy();
	iSafeBags.Close();
	}

EXPORT_C CDecPkcs12SafeContents* CDecPkcs12SafeContents::NewL(const CPKCS7ContentInfo& aSafeContentsBagData)
	{
	CDecPkcs12SafeContents* self = new(ELeave) CDecPkcs12SafeContents;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeContentsBagData.ContentData());
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CDecPkcs12SafeContents* CDecPkcs12SafeContents::NewL(const CPKCS7ContentInfo& aSafeContentsBagData, const TDesC& aPassword)
	{
	CDecPkcs12SafeContents* self = new(ELeave) CDecPkcs12SafeContents;
	CleanupStack::PushL(self);
	self->DecodeEncryptedDataL(aSafeContentsBagData,aPassword);
	CleanupStack::Pop(self);
	return self;
	}
	
EXPORT_C CDecPkcs12SafeContents* CDecPkcs12SafeContents::NewL(const TDesC8& aSafeContent)
	{
	CDecPkcs12SafeContents* self = new(ELeave) CDecPkcs12SafeContents;
	CleanupStack::PushL(self);
	self->ConstructL(aSafeContent);
	CleanupStack::Pop(self);
	return self;
	}

void CDecPkcs12SafeContents::ConstructL(const TDesC8& aSafeContent)
	{
	TASN1DecGeneric decGen(aSafeContent);
    decGen.InitL(); 
			
	if(decGen.Tag() != EASN1Sequence || decGen.Class() != EUniversal)      
		{
		User::Leave(KErrArgument);
		}
          		 
    TASN1DecGeneric decGen2(decGen.GetContentDER());
	decGen2.InitL(); 
			
	if(decGen2.Tag() != EASN1Sequence || decGen.Class() != EUniversal)      
		{
		User ::Leave(KErrArgument);
	    }
				
	// Decode sequence
	TASN1DecSequence seq;
 	CArrayPtr<TASN1DecGeneric>* safeBagSequences = seq.DecodeDERLC(decGen);
 	
 	// A Sequence of SafeBags are present within the SafeContents Bag Sequence
 	TInt safeBagCount = safeBagSequences->Count();
 	
 	for(TInt index = 0; index < safeBagCount; index++)
 	  	{
 	  	CDecPkcs12SafeBag* safeBag = CDecPkcs12SafeBag::NewL(safeBagSequences->At(index)->Encoding());
 	  	CleanupStack::PushL(safeBag);
 	   	iSafeBags.AppendL(safeBag);
 	   	CleanupStack::Pop(safeBag);
 	   	}	
 	CleanupStack::PopAndDestroy(safeBagSequences); // safeBagSequences
	}

void CDecPkcs12SafeContents::DecodeEncryptedDataL(const CPKCS7ContentInfo& aContentInfo, const TDesC& aPassword)
	{
	CPKCS7EncryptedDataObject* pkcs7EncryptedData = CPKCS7EncryptedDataObject::NewL(aContentInfo);
	CleanupStack::PushL(pkcs7EncryptedData);
	iDecryptedData = pkcs7EncryptedData->DecryptDataL(aPassword);	
	ConstructL(iDecryptedData->Des());
	CleanupStack::PopAndDestroy(pkcs7EncryptedData); 
	}

EXPORT_C const RPointerArray<CDecPkcs12SafeBag>& CDecPkcs12SafeContents::SafeContentsBags() const
	{
	return iSafeBags;
	}

EXPORT_C const TDesC8* CDecPkcs12SafeContents::DecryptedData() const
	{
	return iDecryptedData;	
	}
