/*
* Copyright (c) 1998-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: 
* -- FingerPrint Note:
* Developers have to be aware that if they are going to change the fingerprint for this certificate
* for a different hash, then there are other places that need to reflect this change
* -- Location
* void CX509Certificate::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
* EXPORT_C void CX509Certificate::InternalizeL(RReadStream& aStream)
* Also change the CWTLSCertificate and search for other occurences of the current
* hash.
* -- TeletexString type support Note:
* If the certificate has decoded the members from TeletexString then the return value 
* may be incorrect because TeletexString type is not fully supported by this library.
* Instead the decode methods perform a direct conversion from 8 to 16bits by adding 
* null characters in the second byte of each character. This will work as expected 
* for cases where the string contains ASCII data.
*
*/




#include <x509cert.h>
#include <x509certext.h>
#include "X509time.h"
#include <x509keys.h>
#include <keyidentifierutil.h>
#include <asn1enc.h>
#include <asn1dec.h>

EXPORT_C CPKCS1SignatureResult* CPKCS1SignatureResult::NewL(const CAlgorithmIdentifier& aDigestAlgorithm, const TDesC8& aDigest)
	{
	CPKCS1SignatureResult* self = CPKCS1SignatureResult::NewLC(aDigestAlgorithm, aDigest);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CPKCS1SignatureResult* CPKCS1SignatureResult::NewLC(const CAlgorithmIdentifier& aDigestAlgorithm, const TDesC8& aDigest)
	{
	CPKCS1SignatureResult* self = new(ELeave) CPKCS1SignatureResult;
	CleanupStack::PushL(self);
	self->ConstructL(aDigestAlgorithm, aDigest);
	return self;
	}

void CPKCS1SignatureResult::ConstructL(const CAlgorithmIdentifier& aDigestAlgorithm, const TDesC8& aDigest)
	{
	iDigestAlgorithm = CAlgorithmIdentifier::NewL(aDigestAlgorithm);
	iDigest = aDigest.AllocL();
	}

EXPORT_C TBool CPKCS1SignatureResult::VerifyL(const TDesC8& aResult)
	{

	TBool res = EFalse;
	TRAPD(err, res = DoVerifyL(aResult));
	if ((err != KErrNone) && (err != KErrArgument))
		{
		User::Leave(err);
		}
	return res;
	}

TBool CPKCS1SignatureResult::DoVerifyL(const TDesC8& aResult)
	{
	TBool res = EFalse;
	TASN1DecSequence decSeq;
	TInt pos = 0;
	TInt len = aResult.Length();

	const CArrayPtrFlat<TASN1DecGeneric>* seq = decSeq.DecodeDERLC(aResult, pos);
	if (seq->Count() == 2)
		{
		const TASN1DecGeneric* gen1 = seq->At(0);
		const TASN1DecGeneric* gen2 = seq->At(1);
		CAlgorithmIdentifier* algId = CX509AlgorithmIdentifier::NewLC(gen1->Encoding());
		HBufC8* digest = gen2->GetContentDER().AllocL();
		if ((*algId == *iDigestAlgorithm) && (*digest == *iDigest)  && (pos == len))
			{
			res = ETrue;
			}
		delete digest;
		CleanupStack::PopAndDestroy();
		}
	CleanupStack::PopAndDestroy();
	return res;
	}


EXPORT_C CRSAPublicKey* TX509KeyFactory::RSAPublicKeyL(const TDesC8& aEncoding) const
	{
	return CX509RSAPublicKey::NewL(aEncoding);
	}

EXPORT_C CRSASignatureResult* TX509KeyFactory::RSASignatureResultL(const CAlgorithmIdentifier& aDigestAlgorithm, TDesC8& aDigest) const
	{
	return CPKCS1SignatureResult::NewL(aDigestAlgorithm, aDigest);
	}

EXPORT_C CDSAPublicKey* TX509KeyFactory::DSAPublicKeyL(const TDesC8& aParamsEncoding, const TDesC8& aEncoding) const
	{
	return CX509DSAPublicKey::NewL(aParamsEncoding, aEncoding);
	}

EXPORT_C CDSAPublicKey* TX509KeyFactory::DSAPublicKeyL(const CDSAParameters& aParams, const TDesC8& aEncoding) const
	{
	return CX509DSAPublicKey::NewL(aParams, aEncoding);
	}


EXPORT_C CDSASignature* TX509KeyFactory::DSASignatureL(const TDesC8& aEncoding) const
	{
	return CX509DSASignature::NewL(aEncoding);
	}

EXPORT_C CDSAParameters* TX509KeyFactory::DSAParametersL(const TDesC8& aParamsEncoding) const
{
	return (CX509DSAPublicKey::DSAParametersL(aParamsEncoding));
}

//CX509ValidityPeriod
EXPORT_C CX509ValidityPeriod* CX509ValidityPeriod::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509ValidityPeriod::NewL(aBinaryData, pos);
	}

EXPORT_C CX509ValidityPeriod* CX509ValidityPeriod::NewLC(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509ValidityPeriod::NewLC(aBinaryData, pos);
	}

EXPORT_C CX509ValidityPeriod* CX509ValidityPeriod::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509ValidityPeriod* self = CX509ValidityPeriod::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509ValidityPeriod* CX509ValidityPeriod::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509ValidityPeriod* self = new(ELeave) CX509ValidityPeriod;
	CleanupStack::PushL(self);
	self->ConstructL(aBinaryData, aPos);
	return self;
	}

CX509ValidityPeriod::CX509ValidityPeriod()
	{
	}

void CX509ValidityPeriod::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecSequence encSeq;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, aPos);
	if (seq->Count() != 2)
		{
		User::Leave(KErrArgument);
		}
	TASN1DecX509Time decTime;
	iStart = decTime.DecodeDERL(*(seq->At(0)));
	iFinish = decTime.DecodeDERL(*(seq->At(1)));
	CleanupStack::PopAndDestroy();//seq + contents
	}

//algorithm id
EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509AlgorithmIdentifier::NewL(aBinaryData, pos);
	}

EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewLC(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509AlgorithmIdentifier::NewLC(aBinaryData, pos);
	}

EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509AlgorithmIdentifier* self = CX509AlgorithmIdentifier::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509AlgorithmIdentifier* self = new(ELeave) CX509AlgorithmIdentifier;
	CleanupStack::PushL(self);
	self->InitializeL(aBinaryData, aPos);
	return self;
	}

EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewL(TAlgorithmId aAlgorithmId, const TDesC8& aEncodedParams)
 	{
	CX509AlgorithmIdentifier* self = CX509AlgorithmIdentifier::NewLC(aAlgorithmId, aEncodedParams);
 	CleanupStack::Pop();
 	return self;	
 	}
 	
EXPORT_C CX509AlgorithmIdentifier* CX509AlgorithmIdentifier::NewLC(TAlgorithmId aAlgorithmId, const TDesC8& aEncodedParams)
 	{
 	CX509AlgorithmIdentifier* self = new(ELeave) CX509AlgorithmIdentifier(aAlgorithmId);
 	CleanupStack::PushL(self);
 	self->ConstructL(aEncodedParams);
 	return self;				
 	}

CX509AlgorithmIdentifier::CX509AlgorithmIdentifier(TAlgorithmId& aAlgorithmId):CAlgorithmIdentifier(aAlgorithmId)
 	{
 	}


CX509AlgorithmIdentifier::CX509AlgorithmIdentifier()
	{
	}

void CX509AlgorithmIdentifier::InitializeL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecSequence encSeq;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, aPos, 1, KMaxTInt);
	TInt count = seq->Count();
	TASN1DecObjectIdentifier encOID;
	HBufC* oid = encOID.DecodeDERL(*(seq->At(0)));
	CleanupStack::PushL(oid);
	TPtrC oidDes(oid->Des()); 
	if (oidDes == KDSA)
		{
		//optional params
		if (count > 1)//if we still have stuff left
			{
			TASN1DecGeneric* gen = seq->At(1);
			iEncodedParams = gen->Encoding().AllocL();
			}
		else
			{
			iEncodedParams = HBufC8::NewL(1);
			*iEncodedParams = KNullDesC8;
			}
		iAlgorithmId = EDSA;
		CleanupStack::PopAndDestroy(2);//seq, oid
		return;
		}
	if (count > 1)
		{
		TASN1DecGeneric* gen = seq->At(1);
		if (oidDes == KDH)
			{
			iEncodedParams = gen->Encoding().AllocL();
			iAlgorithmId = EDH;
			CleanupStack::PopAndDestroy(2);//seq, oid
			return;
			}
		if (oidDes == KRSA)
			{
			iAlgorithmId = ERSA;
			TASN1DecNull null;
			null.DecodeDERL(*gen);//just to check the syntax is OK
			iEncodedParams = HBufC8::NewL(1);
			*iEncodedParams = KNullDesC8;
			CleanupStack::PopAndDestroy(2);//seq, oid
			return;
			}
		if (oidDes == KMD5)
			{
			iAlgorithmId = EMD5;
			TASN1DecNull null;
			null.DecodeDERL(*gen);//just to check the syntax is OK
			iEncodedParams = HBufC8::NewL(1);
			*iEncodedParams = KNullDesC8;
			CleanupStack::PopAndDestroy(2);//seq, oid
			return;
			}
		if (oidDes == KMD2)
			{
			iAlgorithmId = EMD2;
			TASN1DecNull null;
			null.DecodeDERL(*gen);//just to check the syntax is OK
			iEncodedParams = HBufC8::NewL(1);
			*iEncodedParams = KNullDesC8;
			CleanupStack::PopAndDestroy(2);//seq, oid
			return;
			}
		if (oidDes == KSHA1)
			{
			iAlgorithmId = ESHA1;
			TASN1DecNull null;
			null.DecodeDERL(*gen);//just to check the syntax is OK
			iEncodedParams = HBufC8::NewL(1);
			*iEncodedParams = KNullDesC8;
			CleanupStack::PopAndDestroy(2);//seq, oid
			return;
			}
		}
	User::Leave(KErrNotSupported);
	}
	
EXPORT_C CASN1EncSequence* CX509AlgorithmIdentifier::EncodeASN1DERLC() const
	{
	// the root sequence contains the signed object
	CASN1EncSequence* root = CASN1EncSequence::NewLC();
	
	//encode the oid
	CASN1EncObjectIdentifier* oid(NULL);
	switch (iAlgorithmId)
		{
	case EDSA:
		oid=CASN1EncObjectIdentifier::NewLC(KDSA);
		break;
		
	case EDH:
		oid=CASN1EncObjectIdentifier::NewLC(KDH);
		break;
		
	case ERSA:
		oid=CASN1EncObjectIdentifier::NewLC(KRSA);
		break;
		
	case EMD5:
		oid=CASN1EncObjectIdentifier::NewLC(KMD5);
		break;
		
	case EMD2:
		oid=CASN1EncObjectIdentifier::NewLC(KMD2);
		break;
		
	case ESHA1:
		oid=CASN1EncObjectIdentifier::NewLC(KSHA1);
		break;
		
	default:
		User::Leave(KErrNotSupported);
		}
	
	root->AddAndPopChildL(oid);
	
	//Encode parameters
	if (*iEncodedParams!=KNullDesC8)
		{
		CASN1EncEncoding* enc=CASN1EncEncoding::NewLC(*iEncodedParams);
		root->AddAndPopChildL(enc);
		}
	else
		{
		if (iAlgorithmId!=EDSA)
			{
			CASN1EncNull* enc=CASN1EncNull::NewLC();
			root->AddAndPopChildL(enc);				
			}
		}
		
	return root;		
	}
	

//signing algorithm id
EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509SigningAlgorithmIdentifier::NewL(aBinaryData, pos);
	}

EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewLC(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509SigningAlgorithmIdentifier::NewLC(aBinaryData, pos);
	}

EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509SigningAlgorithmIdentifier* self = CX509SigningAlgorithmIdentifier::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509SigningAlgorithmIdentifier* self = new(ELeave) CX509SigningAlgorithmIdentifier;
	CleanupStack::PushL(self);
	self->ConstructL(aBinaryData, aPos);
	return self;
	}

EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewL(const CAlgorithmIdentifier& aAsymmetricAlgorithm, const CAlgorithmIdentifier& aDigestAlgorithm)
 	{
 	CX509SigningAlgorithmIdentifier* self = CX509SigningAlgorithmIdentifier::NewLC(aAsymmetricAlgorithm, aDigestAlgorithm);
 	CleanupStack::Pop();
 	return self;
 	}
 
EXPORT_C CX509SigningAlgorithmIdentifier* CX509SigningAlgorithmIdentifier::NewLC(const CAlgorithmIdentifier& aAsymmetricAlgorithm, const CAlgorithmIdentifier& aDigestAlgorithm)
 	{
 	CX509SigningAlgorithmIdentifier* self = new(ELeave) CX509SigningAlgorithmIdentifier;
 	CleanupStack::PushL(self);
 	self->ConstructL(aAsymmetricAlgorithm, aDigestAlgorithm);
 	return self;
 	}

CX509SigningAlgorithmIdentifier::CX509SigningAlgorithmIdentifier()
	{
	}

void CX509SigningAlgorithmIdentifier::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecSequence encSeq;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, aPos, 1, KMaxTInt);
	TInt count = seq->Count();
	TASN1DecObjectIdentifier encOID;
	HBufC* oid = encOID.DecodeDERL(*(seq->At(0)));
	CleanupStack::PushL(oid);
	TPtrC oidDes(oid->Des()); 
	//none of the signing algorithms we support have parameters here...
	HBufC8* encodedParams = HBufC8::NewLC(1);
	*encodedParams = KNullDesC8;

	if (oidDes == KDSAWithSHA1)
		{
		//should be no params, but we allow params encoded as NULL for interop reasons
		TAlgorithmId algId = EDSA;
		TAlgorithmId digestId = ESHA1;
		iDigestAlgorithm = CAlgorithmIdentifier::NewL(digestId, encodedParams->Des());
		iAsymmetricAlgorithm = CAlgorithmIdentifier::NewL(algId, encodedParams->Des());
		if (count == 1)
			{			
			CleanupStack::PopAndDestroy(3);//seq, oid, encodedParams
			return;
			}
		}

	if (oidDes == KMD2WithRSA)
		{
		TAlgorithmId algId = ERSA;
		TAlgorithmId digestId = EMD2;
		iDigestAlgorithm = CAlgorithmIdentifier::NewL(digestId, encodedParams->Des());
		iAsymmetricAlgorithm = CAlgorithmIdentifier::NewL(algId, encodedParams->Des());
		}

	if (oidDes == KMD5WithRSA)
		{
		TAlgorithmId algId = ERSA;
		TAlgorithmId digestId = EMD5;
		iDigestAlgorithm = CAlgorithmIdentifier::NewL(digestId, encodedParams->Des());
		iAsymmetricAlgorithm = CAlgorithmIdentifier::NewL(algId, encodedParams->Des());
		}

	if (oidDes == KSHA1WithRSA)
		{
		TAlgorithmId algId = ERSA;
		TAlgorithmId digestId = ESHA1;
		iDigestAlgorithm = CAlgorithmIdentifier::NewL(digestId, encodedParams->Des());
		iAsymmetricAlgorithm = CAlgorithmIdentifier::NewL(algId, encodedParams->Des());
		}
		//???not sure if we should just leave here...
	if (iDigestAlgorithm == NULL)
		{	
		User::Leave(KErrNotSupported);
		}
	else
		{
		if (count != 2)
			{
			// Shouldn't ever get here, since RFC2459 mandates having 2
			// data items.  However, some people miss out the second
			// when it's NULL, so we'll not report and error here
			}
		else
			{
			TASN1DecGeneric* gen = seq->At(1);
			TASN1DecNull null;
			null.DecodeDERL(*gen);//just to check the syntax is OK
			}
		}
	CleanupStack::PopAndDestroy(3);//seq, oid, encodedParams
	}

void CX509SigningAlgorithmIdentifier::ConstructL(const CAlgorithmIdentifier& aAsymmetricAlgorithm, const CAlgorithmIdentifier& aDigestAlgorithm)
 	{
 	iDigestAlgorithm = CAlgorithmIdentifier::NewL(aDigestAlgorithm);
 	iAsymmetricAlgorithm = CAlgorithmIdentifier::NewL(aAsymmetricAlgorithm);
 	};

//subject public key info
EXPORT_C CX509SubjectPublicKeyInfo* CX509SubjectPublicKeyInfo::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509SubjectPublicKeyInfo* self = CX509SubjectPublicKeyInfo::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509SubjectPublicKeyInfo* CX509SubjectPublicKeyInfo::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509SubjectPublicKeyInfo* self = new(ELeave) CX509SubjectPublicKeyInfo;
	CleanupStack::PushL(self);
	self->ConstructL(aBinaryData, aPos);
	return self;
	}

EXPORT_C CX509SubjectPublicKeyInfo* CX509SubjectPublicKeyInfo::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509SubjectPublicKeyInfo::NewL(aBinaryData, pos);
	}

EXPORT_C CX509SubjectPublicKeyInfo* CX509SubjectPublicKeyInfo::NewLC(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509SubjectPublicKeyInfo::NewLC(aBinaryData, pos);
	}

CX509SubjectPublicKeyInfo::CX509SubjectPublicKeyInfo()
	{
	}

void CX509SubjectPublicKeyInfo::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecSequence encSeq;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, aPos, 2, KMaxTInt);
	iAlgId = CX509AlgorithmIdentifier::NewL(seq->At(0)->Encoding());
	TASN1DecBitString encBS;
	iEncodedKeyData = encBS.ExtractOctetStringL(*(seq->At(1)));
	CleanupStack::PopAndDestroy();//seq
	}

//generic X 509 extension syntax
EXPORT_C CX509CertExtension* CX509CertExtension::NewL(const CX509CertExtension& aExtension)
	{
	CX509CertExtension* self = CX509CertExtension::NewLC(aExtension);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewLC(const CX509CertExtension& aExtension)
	{
	CX509CertExtension* self = new(ELeave) CX509CertExtension;
	CleanupStack::PushL(self);
	self->ConstructL(aExtension);
	return self;
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509CertExtension* self = CX509CertExtension::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509CertExtension* self = new(ELeave) CX509CertExtension;
	CleanupStack::PushL(self);
	self->ConstructL(aBinaryData, aPos);
	return self;
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509CertExtension::NewL(aBinaryData, pos);
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewLC(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509CertExtension::NewLC(aBinaryData, pos);
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewL(const TDesC& aCertExtOID, 
							const TBool aCritical,
							const TDesC8& aCertExtValue)
	{
	CX509CertExtension* self = CX509CertExtension::NewLC(aCertExtOID, aCritical, aCertExtValue);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509CertExtension* CX509CertExtension::NewLC(const TDesC& aCertExtOID, 
							const TBool aCritical,
							const TDesC8& aCertExtValue)
	{
	CX509CertExtension* self = new(ELeave) CX509CertExtension;
	CleanupStack::PushL(self);
	self->ConstructL(aCertExtOID, aCritical, aCertExtValue);
	return self;
	}

void CX509CertExtension::ConstructL(const CX509CertExtension& aExtension)
	{
	iCritical = aExtension.iCritical;
	iId = aExtension.iId->Des().AllocL();
	iData = aExtension.iData->Des().AllocL();
	}

void CX509CertExtension::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecSequence encSeq;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, aPos, 2, KMaxTInt);

	TASN1DecObjectIdentifier encOID;
	iId = encOID.DecodeDERL(*(seq->At(0)));
	//second is either critical flag, or the ext
	TASN1DecGeneric* second = seq->At(1);
	if (second->Tag() != EASN1Boolean)
		{
		iData = second->Encoding().AllocL();
		aPos += second->LengthDER();
		}
	else
		{
		TASN1DecBoolean encBool;
		iCritical = encBool.DecodeDERL(*second);
		if (seq->Count() != 3)
			{
			User::Leave(KErrArgument);
			}

		TASN1DecGeneric* third = seq->At(2);
		iData = third->Encoding().AllocL();
		}
	CleanupStack::PopAndDestroy();//seq
	}

void CX509CertExtension::ConstructL(const TDesC& aCertExtOID, 
									const TBool aCritical,
									const TDesC8& aCertExtValue)
	{
	iId = aCertExtOID.AllocL();
	iCritical = aCritical;
	iData = aCertExtValue.AllocL();
	}

EXPORT_C CX509CertExtension::~CX509CertExtension()
	{
	delete iData;
	delete iId;
	}

EXPORT_C TBool CX509CertExtension::Critical() const
	{
	return iCritical;
	}

EXPORT_C TPtrC CX509CertExtension::Id() const	//OID for the extension
	{
	return iId->Des();
	}

EXPORT_C TPtrC8 CX509CertExtension::Data() const	 //the extension itself
	{
	return iData->Des();
	}

EXPORT_C CASN1EncSequence* CX509CertExtension::EncodeASN1DERLC() const
	{
	CASN1EncSequence *result = CASN1EncSequence::NewLC();
	CASN1EncObjectIdentifier* oid = CASN1EncObjectIdentifier::NewLC(*iId);
	result->AddAndPopChildL(oid);
	// Encode critical flag only if true
	if (iCritical)
		{
		CASN1EncBoolean *critical = CASN1EncBoolean::NewLC(iCritical);
		result->AddAndPopChildL(critical);
		}	
	CASN1EncOctetString *data = CASN1EncOctetString::NewLC(*iData);
	result->AddAndPopChildL(data);
	return result;
	}

CX509CertExtension::CX509CertExtension()
	:iCritical(EFalse)
	{
	}

//CX509Certificate
EXPORT_C CX509Certificate* CX509Certificate::NewL(const CX509Certificate& aCert)
	{
	CX509Certificate* self = CX509Certificate::NewLC(aCert);
	CleanupStack::Pop();//self
	return self;
	}

EXPORT_C CX509Certificate* CX509Certificate::NewLC(const CX509Certificate& aCert)
	{
	CX509Certificate* self = new(ELeave) CX509Certificate;
	CleanupStack::PushL(self);
	self->ConstructL(aCert);
	return self;
	}

EXPORT_C CX509Certificate* CX509Certificate::NewL(const TDesC8& aBinaryData)
	{
	TInt pos = 0;
	return CX509Certificate::NewL(aBinaryData, pos);
	}

EXPORT_C CX509Certificate* CX509Certificate::NewLC(const TDesC8& aBinaryData)
	{	
	TInt pos = 0;
	return CX509Certificate::NewLC(aBinaryData, pos);
	}

EXPORT_C CX509Certificate* CX509Certificate::NewL(const TDesC8& aBinaryData, TInt& aPos)
	{
	CX509Certificate* self = CX509Certificate::NewLC(aBinaryData, aPos);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CX509Certificate* CX509Certificate::NewLC(const TDesC8& aBinaryData, TInt& aPos)
	{	
	CX509Certificate* self = new(ELeave) CX509Certificate();
	CleanupStack::PushL(self);
	self->ConstructL(aBinaryData, aPos);
	return self;
	}

EXPORT_C CX509Certificate* CX509Certificate::NewL(RReadStream& aStream)
	{
	CX509Certificate* self = CX509Certificate::NewLC(aStream);
	CleanupStack::Pop();//self
	return self;
	}

EXPORT_C CX509Certificate* CX509Certificate::NewLC(RReadStream& aStream)
	{
	CX509Certificate* self = new(ELeave) CX509Certificate;
	CleanupStack::PushL(self);
	self->InternalizeL(aStream);
	return self;
	}

void CX509Certificate::ConstructL(const TDesC8& aBinaryData, TInt& aPos)
	{
	TASN1DecGeneric gen(aBinaryData.Right(aBinaryData.Length() - aPos));
	gen.InitL();
	
	// The outermost tag for X509 certificates is always a sequence.
	// Since this tag does not form part of the signed data it is possible
	// to corrupt the tag by changing it to any other ASN.1 tag and process
	// the rest of the certificate as normal.
	// However, we still reject the certificate anyway to avoid 
	// confusion because the data does not match the X.509 specification.	
	if (gen.Tag() != EASN1Sequence)
		{
		User::Leave(KErrArgument);
		}
	
	aPos += gen.LengthDER();
	iKeyFactory = new(ELeave) TX509KeyFactory;

	iEncoding = gen.Encoding().AllocL();

	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(*iEncoding, pos, 3, 3);	
	TASN1DecGeneric* encSigAlg = seq->At(1);
	iSigningAlgorithm = CX509SigningAlgorithmIdentifier::NewL(encSigAlg->Encoding());
	TASN1DecBitString encBS;
	iSignature = encBS.ExtractOctetStringL(*(seq->At(2)));
	CleanupStack::PopAndDestroy();//seq

	CSHA1* hash = CSHA1::NewL();
	CleanupStack::PushL(hash);
	iFingerprint = hash->Final(Encoding()).AllocL();
	CleanupStack::PopAndDestroy();//hash
	ConstructCertL();
	}

void CX509Certificate::ConstructL(const CX509Certificate& aCertificate)
	{
	iKeyFactory = new(ELeave) TX509KeyFactory;

	iEncoding = aCertificate.iEncoding->AllocL();
	iSignature = aCertificate.iSignature->AllocL();
	iFingerprint = aCertificate.iFingerprint->AllocL();
	iSigningAlgorithm = CSigningAlgorithmIdentifier::NewL(aCertificate.SigningAlgorithm());	
	iSerialNumber = aCertificate.iSerialNumber->Des().AllocL();
	iIssuerName = CX500DistinguishedName::NewL(*(aCertificate.iIssuerName));
	iValidityPeriod = new(ELeave) CValidityPeriod(*(aCertificate.iValidityPeriod));
	iSubjectName = CX500DistinguishedName::NewL(*(aCertificate.iSubjectName));
	iSubjectPublicKeyInfo = CSubjectPublicKeyInfo::NewL(*(aCertificate.iSubjectPublicKeyInfo));
	iIssuerUid = aCertificate.iIssuerUid->Des().AllocL();
	iSubjectUid = aCertificate.iSubjectUid->Des().AllocL();
	iExtensions = new(ELeave) CArrayPtrFlat<CX509CertExtension> (1);
	TInt count = aCertificate.iExtensions->Count();
	for (TInt i = 0; i < count; i++)
		{
		CX509CertExtension* ext = CX509CertExtension::NewLC(*aCertificate.iExtensions->At(i));
		iExtensions->AppendL(ext);
		CleanupStack::Pop();//ext
		}
	iVersion = aCertificate.Version();
	InitDataElementsL(aCertificate);
	}

void CX509Certificate::InitDataElementsL(const CX509Certificate& aCertificate)
	{
	iDataElements = new(ELeave) TFixedArray<TPtrC8*, KX509MaxDataElements>;
	iDataElements->Reset();
	TPtrC8 signedData = SignedDataL();
	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(signedData, pos, 6, KMaxTInt);//6 is the minimum number of elements in an x509 cert
	pos = 0;

	TPtrC8** pElement = iDataElements->Begin();
	*pElement++ = aCertificate.DataElementEncoding(CX509Certificate::EVersionNumber)? new(ELeave) TPtrC8(seq->At(pos++)->Encoding()):NULL;
	for (TInt i = 0; i < 6; i++)	//init all the non-optional elements
		{
		*pElement++ = new(ELeave) TPtrC8(seq->At(pos++)->Encoding());
		}
	*pElement++ = aCertificate.DataElementEncoding(CX509Certificate::EIssuerUID)? new(ELeave) TPtrC8(seq->At(pos++)->Encoding()):NULL;
	*pElement++ = aCertificate.DataElementEncoding(CX509Certificate::ESubjectUID)? new(ELeave) TPtrC8(seq->At(pos++)->Encoding()):NULL;
	*pElement++ = aCertificate.DataElementEncoding(CX509Certificate::EExtensionList)? new(ELeave) TPtrC8(seq->At(pos++)->Encoding()):NULL;
	CleanupStack::PopAndDestroy();
	}

void CX509Certificate::ConstructCertL()
	{
	TPtrC8 signedData = SignedDataL();
	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(signedData, pos, 6, KMaxTInt);//6 is the minimum number of elements in an x509 cert
	TInt count = seq->Count();
	pos = 0;
	TASN1DecGeneric* curr = seq->At(pos);
	pos++;
	iDataElements = new(ELeave) TFixedArray<TPtrC8*, KX509MaxDataElements>;
	iDataElements->Reset();
	TPtrC8** pElement = iDataElements->Begin();
	if ((curr->Class() == EContextSpecific) && (curr->Tag() == 0))
		{
		//version!
		TASN1DecGeneric ver(curr->GetContentDER());
		ver.InitL();
		TPtrC8 pVer8 = ver.GetContentDER();
		if(pVer8.Length() != 1)
			{
			User::Leave(KErrArgument);
			}
		iVersion = (pVer8[0]) + 1;
		if ((iVersion < 1) || (iVersion > 3) || (count < 7))
			{
			User::Leave(KErrArgument);
			}
		*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
		curr = seq->At(pos);
		pos++;
		}
	else
		{
		*pElement++ = NULL;
		}
	if (curr->Tag() != EASN1Integer)
		{
		User::Leave(KErrArgument);
		}
	iSerialNumber = (curr->GetContentDER()).AllocL();
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
	curr = seq->At(pos);
	pos++;
	CX509SigningAlgorithmIdentifier* algorithmId = CX509SigningAlgorithmIdentifier::NewLC(curr->Encoding());
	if (!(SigningAlgorithm() == *(algorithmId)))
		{
		User::Leave(KErrArgument);
		}
	CleanupStack::PopAndDestroy();//algorithmId
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());

	curr = seq->At(pos);
	pos++;
	iIssuerName = CX500DistinguishedName::NewL(curr->Encoding());
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
	curr = seq->At(pos);
	pos++;
	iValidityPeriod = CX509ValidityPeriod::NewL(curr->Encoding());
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
	curr = seq->At(pos);
	pos++;
	iSubjectName = CX500DistinguishedName::NewL(curr->Encoding());
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
	curr = seq->At(pos);
	pos++;
	iSubjectPublicKeyInfo = CX509SubjectPublicKeyInfo::NewL(curr->Encoding());
	*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
	//do issuer uid, subject uid, exts
	//these are all optional
	TBool hasIssuerUid = EFalse;
	TBool hasSubjectUid = EFalse;
	TBool hasExts = EFalse;
	iExtensions = new(ELeave)CArrayPtrFlat<CX509CertExtension> (1);
	if (pos < count)//otherwise there aren't any of 'em
		{
		curr = seq->At(pos);
		pos++;
		if (curr->Class() != EContextSpecific)
			{
			User::Leave(KErrArgument);
			}
		switch(curr->Tag())
			{
			case 1:
				{
				iIssuerUid = DecodeUidL(curr->GetContentDER(), hasIssuerUid);
				*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
				break;
				}
			case 2:
				{
				iSubjectUid = DecodeUidL(curr->GetContentDER(), hasSubjectUid);
				*pElement++ = NULL;
				*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
				break;
				}
			case 3:
				{
				DecodeExtsL(curr->GetContentDER(), hasExts);
				*pElement++ = NULL;
				*pElement++ = NULL;
				*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
				break;
				}
			default:
				{
				User::Leave(KErrArgument);
				}
			}
		if (pos < count)
			{
			curr = seq->At(pos);
			pos++;
			switch(curr->Tag())
				{
				case 2:
					{
					iSubjectUid = DecodeUidL(curr->GetContentDER(), hasSubjectUid);
					*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
					break;
					}
				case 3:
					{
					DecodeExtsL(curr->GetContentDER(), hasExts);
					*pElement++ = NULL;
					*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
					break;
					}
				default:
					{
					User::Leave(KErrArgument);
					}
				}
			if (pos < count)
				{
				curr = seq->At(pos);
				pos++;
				if (curr->Tag() == 3)
					{
					DecodeExtsL(curr->GetContentDER(), hasExts);
					*pElement++ = new(ELeave) TPtrC8(curr->Encoding());
					}
				else
					{
					User::Leave(KErrArgument);
					}
				}
			}
		}
	if (pos != count)
		{
		User::Leave(KErrArgument);
		}
	if (!iIssuerUid)
		{
		iIssuerUid = HBufC8::NewL(1);
		*iIssuerUid = KNullDesC8;
		}
	if (!iSubjectUid)
		{
		iSubjectUid = HBufC8::NewL(1);
		*iSubjectUid = KNullDesC8;
		}
		
	// we have not checked for the certificate version number based on 
	// the certificate contents. This is primarily done to avoid BC for 
	// clients who are still using malformed certificates.
	
	CleanupStack::PopAndDestroy();//seq
	}

CX509Certificate::CX509Certificate()
	:iVersion(1)
	{
	}

EXPORT_C CX509Certificate::~CX509Certificate()
	{
	delete iIssuerName;
	delete iSubjectName;
	delete iIssuerUid;
	delete iSubjectUid;
	
	if (iDataElements != NULL)
		{
		for (TInt i = 0; i < KX509MaxDataElements; i++)
			{
			delete iDataElements->At(i);
			}
		delete iDataElements;
		}
	if (iExtensions != NULL)
		{
		iExtensions->ResetAndDestroy();
		}
	delete iExtensions;
	}

EXPORT_C TBool CX509Certificate::IsEqualL(const CX509Certificate& aCert) const
	{
	return	(	(*(iSerialNumber) == *(aCert.iSerialNumber)) && 
				(iIssuerName->ExactMatchL(*(aCert.iIssuerName)))	);
	}

EXPORT_C void CX509Certificate::InternalizeL(RReadStream& aStream)
	{
	if (iIssuerName != NULL) //just to check cert is uninitialised
		{
		User::Leave(KErrArgument);
		}
	iKeyFactory = new(ELeave) TX509KeyFactory;
	
	TInt len = aStream.ReadInt32L(); //Read the length of the streamed encoding
	HBufC8* temp= HBufC8::NewLC(len);	
	TPtr8 ptr=temp->Des();
	aStream.ReadL(ptr,len);
	iEncoding=temp->AllocL();
	CleanupStack::PopAndDestroy(); // temp

	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(*iEncoding, pos, 3, 3);	
	TASN1DecGeneric* encSigAlg = seq->At(1);
	iSigningAlgorithm = CX509SigningAlgorithmIdentifier::NewL(encSigAlg->Encoding());
	TASN1DecBitString encBS;
	iSignature = encBS.ExtractOctetStringL(*(seq->At(2)));
	CleanupStack::PopAndDestroy();//seq	

	CSHA1* hash = CSHA1::NewL();
	CleanupStack::PushL(hash);
	iFingerprint = hash->Final(Encoding()).AllocL();
	CleanupStack::PopAndDestroy();//hash

	ConstructCertL();
	}

void CX509Certificate::DecodeExtsL(const TDesC8& aBinaryData, TBool& aHasElementAlready)
	{
	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(aBinaryData, pos);
	TInt count = seq->Count();
	for (TInt i = 0; i < count; i++)
		{
		TASN1DecGeneric* gen = seq->At(i);
		CX509CertExtension* ext = CX509CertExtension::NewLC(gen->Encoding());
		iExtensions->AppendL(ext);
		CleanupStack::Pop();//ext
		}
	CleanupStack::PopAndDestroy();//
	aHasElementAlready = ETrue;
	}

HBufC8* CX509Certificate::DecodeUidL(const TDesC8& aBinaryData, TBool& aHasElementAlready)
	{
	if ((aHasElementAlready) || (iVersion ==1))
		{
		User::Leave(KErrArgument);
		}
	aHasElementAlready = ETrue;
	return aBinaryData.AllocL();
	}

//***************************************************************************************//
//extra accessors
EXPORT_C const TPtrC8 CX509Certificate::SignedDataL() const
	{
	TASN1DecSequence encSeq;
	TInt pos = 0;
	CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(*iEncoding, pos, 3, 3);
	TASN1DecGeneric* gen = seq->At(0);
	TPtrC8 res = gen->Encoding();
	CleanupStack::PopAndDestroy();
	return res;
	}

EXPORT_C TInt CX509Certificate::Version() const
	{
	return iVersion;
	}

/**
* If the certificate has decoded the members from TeletexString then the return value 
* may be incorrect because TeletexString type is not fully supported by this library.
* Instead the decode methods perform a direct conversion from 8 to 16bits by adding 
* null characters in the second byte of each character. This will work as expected 
* for cases where the string contains ASCII data.
*/
EXPORT_C const CX500DistinguishedName& CX509Certificate::IssuerName() const
	{
	return *iIssuerName;
	}

/**
* If the certificate has decoded the members from TeletexString then the return value 
* may be incorrect because TeletexString type is not fully supported by this library.
* Instead the decode methods perform a direct conversion from 8 to 16bits by adding 
* null characters in the second byte of each character. This will work as expected 
* for cases where the string contains ASCII data.
*/
EXPORT_C const CX500DistinguishedName& CX509Certificate::SubjectName() const
	{
	return *iSubjectName;
	}

EXPORT_C const CArrayPtrFlat<CX509CertExtension>& CX509Certificate::Extensions() const
	{
	return *iExtensions;
	}

EXPORT_C const CX509CertExtension* CX509Certificate::Extension(const TDesC& aExtensionName) const
	{
	TInt count = iExtensions->Count();
	for (TInt i = 0; i < count; i++)
		{
		CX509CertExtension* ext = iExtensions->At(i);
		if (ext->Id() == aExtensionName)
			return ext;
		}
	return NULL;
	}

EXPORT_C const TPtrC8* CX509Certificate::DataElementEncoding(const TUint aIndex) const
	{
	return iDataElements->At(aIndex);
	}

/**
* If the certificate has decoded the members from TeletexString then the return value 
* may be incorrect because TeletexString type is not fully supported by this library.
* Instead the decode methods perform a direct conversion from 8 to 16bits by adding 
* null characters in the second byte of each character. This will work as expected 
* for cases where the string contains ASCII data.
*/
EXPORT_C HBufC* CX509Certificate::IssuerL() const
	{
	return iIssuerName->DisplayNameL();
	}

/**
* If the certificate has decoded the members from TeletexString then the return value 
* may be incorrect because TeletexString type is not fully supported by this library.
* Instead the decode methods perform a direct conversion from 8 to 16bits by adding 
* null characters in the second byte of each character. This will work as expected 
* for cases where the string contains ASCII data.
*/
EXPORT_C HBufC* CX509Certificate::SubjectL() const
	{
	return iSubjectName->DisplayNameL();
	}

EXPORT_C TBool CX509Certificate::IsSelfSignedL() const
	{
	if (iSubjectName->Count() > 0)
		{
		return iSubjectName->ExactMatchL(*iIssuerName);
		}
	else
		{
		TBool res = EFalse;
		const CX509CertExtension* subjectExt = Extension(KIssuerAltName);
		const CX509CertExtension* issuerExt = Extension(KSubjectAltName);
		if ((subjectExt) && (issuerExt))
			{
			const CX509AltNameExt* issuerAltName = CX509AltNameExt::NewLC(subjectExt->Data());
			const CX509AltNameExt* subjectAltName = CX509AltNameExt::NewLC(issuerExt->Data());
			if (subjectAltName->Match(*issuerAltName))
				{
				res = ETrue;
				}
			CleanupStack::PopAndDestroy(2);//subjectAltName, issuerAltName
			}
		return res;
		} 
	}


EXPORT_C TKeyIdentifier CX509Certificate::KeyIdentifierL() const	
	{
	if (iSubjectPublicKeyInfo->AlgorithmId() != EDSA)
		{
		// The base class handles the RSA case and leaves with KErrNotSupported if
		// it is not RSA.
		return CCertificate::KeyIdentifierL();
		}
	else
		{
		// This handles the DSA case
		CDSAPublicKey* dsaKey = 
						iKeyFactory->DSAPublicKeyL(iSubjectPublicKeyInfo->EncodedParams(), 
													iSubjectPublicKeyInfo->KeyData());
		CleanupStack::PushL(dsaKey);
		TKeyIdentifier retVal;
		KeyIdentifierUtil::DSAKeyIdentifierL(*dsaKey, retVal);
		CleanupStack::PopAndDestroy(dsaKey);
		return retVal;
		}
	}

EXPORT_C TKeyIdentifier CX509Certificate::SubjectKeyIdentifierL() const
	{	
	const CX509CertExtension* subjectKeyIdExtStr = this->Extension(KSubjectKeyId);
	if (subjectKeyIdExtStr)
		{
		TKeyIdentifier ret;
		CX509SubjectKeyIdExt *subjectKeyIdExt = CX509SubjectKeyIdExt::NewLC(subjectKeyIdExtStr->Data());
		// Currently, we do not support key identifiers larger than 160 bits - make sure not to overflow the descriptor
		if (subjectKeyIdExt->KeyId().Length() <= ret.MaxLength())
			{
			ret.Copy(subjectKeyIdExt->KeyId());
			CleanupStack::PopAndDestroy(subjectKeyIdExt);
			return ret;
			}
		CleanupStack::PopAndDestroy(subjectKeyIdExt);
		}
		
	return KeyIdentifierL();
	}
