/*
* Copyright (c) 2004-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 <asn1enc.h>
#include <pbe.h>
#include <asymmetrickeys.h>
#include <bigint.h>
#include "asnpkcs.h"

const TInt KPkcs8Version = 0;      // PKCS#8 version 0 
const TInt KPkcs8RSATwoPrime = 0;  // We support only two-prime RSA. 


/*static*/ void TASN1EncPKCS8::AddAttributesL(CASN1EncSequence& aSeq, const TDesC8& aAttribute)
	{
	if (aAttribute != KNullDesC8)
		{
		CASN1EncEncoding* encoded = CASN1EncEncoding::NewLC(aAttribute);
		aSeq.AddAndPopChildL(encoded);		
		}
	}

/**
 * Given a ASN1 sequence representing a private key and a CPBEncryptor object,
 * it returns an ASN1 octet string containing the key sequence encrypted by
 * the given encryptor.
 *
 * @param aKeySeq The key sequence to be encrypted.
 * @param aEncryptor The CPBEncryptor object used to encrypt the given key.
 * @return An ASN1 octet string containing the encrypted key.
 */
/*static*/ CASN1EncOctetString* TASN1EncPKCS8::EncryptKeySequenceL(CASN1EncSequence& aKeySeq, CPBEncryptor& aEncryptor)
	{
	//	Make an octet string of the key sequence
	TUint seqLen = aKeySeq.LengthDER();
	HBufC8* octetData = HBufC8::NewMaxLC(seqLen);
	TPtr8 oct(octetData->Des());
	oct.FillZ();
	TUint writePos = 0;
	aKeySeq.WriteDERL(oct, writePos);
	
	// Encrypt it
        HBufC8* encryptedData = HBufC8::NewLC(aEncryptor.MaxFinalOutputLength(seqLen)); 
        TPtr8 encrypted(encryptedData->Des());  

	aEncryptor.ProcessFinalL(*octetData, encrypted);
		
	CASN1EncOctetString* octet = CASN1EncOctetString::NewL(encrypted);
	
	CleanupStack::PopAndDestroy(2, octetData); // encryptedData, octetData
	
	return octet;
	}


/*static*/ CASN1EncOctetString* TASN1EncPKCS8::ElementToOctetL(CASN1EncBase& aAsnElement)
	{
	//	Make an octet string from the structure
	HBufC8* octetData = HBufC8::NewMaxLC(aAsnElement.LengthDER());
	TPtr8 oct(octetData->Des());
	oct.FillZ();

	TUint writePos = 0;
	aAsnElement.WriteDERL(oct, writePos);
	
	CASN1EncOctetString* octet = CASN1EncOctetString::NewL(oct);
	CleanupStack::PopAndDestroy(octetData);
	return octet;
	}


/*static*/ CASN1EncSequence* TASN1EncPKCS8::EncryptedSequenceL(CASN1EncSequence& aPrivateKeySequence, 
                                                               CPBEncryptor& aEncryptor, CPBEncryptParms& aData)
	{
	// Main ASN1 sequence
	CASN1EncSequence* seq = CASN1EncSequence::NewLC();
	
	// PKCS#5 PBE sequence
	CASN1EncSequence* pkcs5Seq = TASN1EncPKCS5::EncodeDERL(aData);
	 
	CleanupStack::PushL(pkcs5Seq);
	seq->AddAndPopChildL(pkcs5Seq);
	
	// Encrypt the Private Key Sequence
	CASN1EncOctetString* octet = EncryptKeySequenceL(aPrivateKeySequence, aEncryptor);
	CleanupStack::PushL(octet);
	seq->AddAndPopChildL(octet);

	CleanupStack::Pop(seq);

	return seq;
	}


/*static*/ const RInteger TASN1EncPKCS8::CalculateRSAPrivExpL(const CRSAPrivateKeyCRT& aPrivateKey, const CRSAPublicKey& aPublicKey)
	{
	const TInteger& e = aPublicKey.E();

	RInteger pTemp = RInteger::NewL(aPrivateKey.P());
	CleanupStack::PushL(pTemp);
	RInteger qTemp = RInteger::NewL(aPrivateKey.Q());
	CleanupStack::PushL(qTemp);
	
  	--pTemp;
    	--qTemp;
 
	//phi = (p-1)(q-1)
	RInteger phi = pTemp.TimesL(qTemp);

	CleanupStack::PopAndDestroy(2, &pTemp);  // qTemp, pTemp
 	CleanupStack::PushL(phi);
    	
    	//d = e^(-1) mod ((p-1)(q-1))
   	 RInteger d = e.InverseModL(phi);
    	CleanupStack::PopAndDestroy(&phi); 
	return d;	
	}

/**
 * Encodes the given private key using the pkcs#8 standard.
 *
 * The returned ASN1 sequence respects the following grammar:
 * 
 *	PrivateKeyInfo ::= SEQUENCE {
 *	version Version,
 *	privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
 *	privateKey PrivateKey,
 *	attributes [0] IMPLICIT Attributes OPTIONAL }
 *	
 *	Version ::= INTEGER
 *	PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
 *	PrivateKey ::= OCTET STRING
 *	Attributes ::= SET OF Attribute
 *
 *
 * @param aKeyPair A key pair containing the private key we wish to encode.
 * @return A CASN1EncSequence encoding the private key. The sequence
 *         follows the grammar specified above. 
 * @internalComponent
 * @released
 */
/*static*/ EXPORT_C CASN1EncSequence* TASN1EncPKCS8::EncodeL(const CRSAPrivateKeyCRT& aPrivateKey, 
														  const CRSAPublicKey& aPublicKey, 
														  const TDesC8& aAttributes)
	{
	// Main ASN1 sequence
	CASN1EncSequence* seq = CASN1EncSequence::NewLC();

	// Start with PKCS#8 header
	CASN1EncInt* version = CASN1EncInt::NewLC(KPkcs8Version); 
	seq->AddAndPopChildL(version);
	
	//	Algorithm sequence
	CASN1EncSequence* algSeq = CASN1EncSequence::NewLC();

	CASN1EncObjectIdentifier* oid = CASN1EncObjectIdentifier::NewLC(KRSA);
	algSeq->AddAndPopChildL(oid);

	//	No other parameters
	CASN1EncNull* nullparams = CASN1EncNull::NewLC();
	algSeq->AddAndPopChildL(nullparams);

	seq->AddAndPopChildL(algSeq);
	
	// The actual private key sequence		
	CASN1EncSequence* keySeq = CASN1EncSequence::NewLC();

	//	TASN1DecGeneric* versionData = theData->operator[](seqIndex);
	CASN1EncInt* rsaVersion = CASN1EncInt::NewLC(KPkcs8RSATwoPrime); 
	keySeq->AddAndPopChildL(rsaVersion);
	
	//	The modulus	
	const TInteger& modulus = aPrivateKey.N();
	CASN1EncBigInt* mod = CASN1EncBigInt::NewLC(modulus);
	keySeq->AddAndPopChildL(mod);

	//	The public exponent
	const TInteger& e = aPublicKey.E();
	CASN1EncBigInt* publicExp = CASN1EncBigInt::NewLC(e);
	keySeq->AddAndPopChildL(publicExp);

	// The private exponent
	RInteger d = CalculateRSAPrivExpL(aPrivateKey, aPublicKey);
	CleanupStack::PushL(d);

	CASN1EncBigInt* privateExp = CASN1EncBigInt::NewLC(d);

	keySeq->AddAndPopChildL(privateExp);
	CleanupStack::PopAndDestroy(&d);  // RSA Private Exponent 

	//	The prime1
	const TInteger& p = aPrivateKey.P();
	CASN1EncBigInt* prime1 = CASN1EncBigInt::NewLC(p);
	keySeq->AddAndPopChildL(prime1);

	//	The prime2
	const TInteger& q = aPrivateKey.Q();
	CASN1EncBigInt* prime2 = CASN1EncBigInt::NewLC(q);
	keySeq->AddAndPopChildL(prime2);

	//	The exponent 1	
	const TInteger& dp = aPrivateKey.DP();
	CASN1EncBigInt* theExp1 = CASN1EncBigInt::NewLC(dp);
	keySeq->AddAndPopChildL(theExp1);

	//	The exponent 2
	const TInteger& dq = aPrivateKey.DQ();
	CASN1EncBigInt* theExp2 = CASN1EncBigInt::NewLC(dq);
	keySeq->AddAndPopChildL(theExp2);

	//	The coefficient
	const TInteger& coeff = aPrivateKey.QInv();
	CASN1EncBigInt* coefficient = CASN1EncBigInt::NewLC(coeff);
	keySeq->AddAndPopChildL(coefficient);
	
	CASN1EncOctetString* octet = ElementToOctetL(*keySeq);
	CleanupStack::PushL(octet);
	
	//	Now add the octet of the key sequence to the sequence
	seq->AddAndPopChildL(octet);
	
  	CleanupStack::PopAndDestroy(keySeq);

	AddAttributesL(*seq, aAttributes);

  	CleanupStack::Pop(seq);
					
	return seq;
	}

/**
 * Encodes an RSA key in encrypted format.
 * 
 * @param aPrivateKey The private key to be encoded (must be in CRT format)
 * @param aPublicKey The corresponding public key.
 * @param aEncryptor The object used to encrypt the data.
 * @param aData The encryption parameters of the given encryptor.
 *              These parameters are stored in the resulting sequence.
 *
 * @return An ASN1 Sequence encoding the encrypted key.
 */
/*static*/ EXPORT_C CASN1EncSequence* TASN1EncPKCS8::EncodeEncryptedL(const CRSAPrivateKeyCRT& aPrivateKey, const CRSAPublicKey& aPublicKey, 
                                                                      CPBEncryptor& aEncryptor, CPBEncryptParms& aData, const TDesC8& aAttributes)
	{
	// Now generate a PrivateKeyInfo type 
	CASN1EncSequence* privKeySeq = EncodeL(aPrivateKey, aPublicKey, aAttributes);
	CleanupStack::PushL(privKeySeq);
	
	CASN1EncSequence* seq = EncryptedSequenceL(*privKeySeq, aEncryptor, aData);
	
	CleanupStack::PopAndDestroy(privKeySeq);
		
	return seq;
	}


/**
 * Return the ASN1 PKCS#8 encoding of a DSA private key.
 *
 * The private key syntax for this key type is defined in 
 * the PKCS#11 document. 
 *
 * @param aPrivateKey The private key to be encoded (must be in CRT format)
 *
 * @return An ASN1 Sequence encoding the key.
 */	
/*static*/ EXPORT_C CASN1EncSequence* TASN1EncPKCS8::EncodeL(const CDSAPrivateKey& aPrivateKey, 
													          const TDesC8& aAttributes)
	{
	// Main ASN1 sequence
	CASN1EncSequence* seq = CASN1EncSequence::NewLC();

	//	version
	CASN1EncInt* version = CASN1EncInt::NewLC(KPkcs8Version);
	seq->AddAndPopChildL(version);
	
	//	Algorithm sequence
	CASN1EncSequence* algSeq = CASN1EncSequence::NewLC();

	CASN1EncObjectIdentifier* oid = CASN1EncObjectIdentifier::NewLC(KDSA);
	algSeq->AddAndPopChildL(oid);

	seq->AddAndPopChildL(algSeq);

	// DSA Parameters	
	CASN1EncSequence* keySeq = CASN1EncSequence::NewLC();

	//	The modulus	P
	const TInteger& modulus1 = aPrivateKey.P();

	CASN1EncBigInt* mod = CASN1EncBigInt::NewLC(modulus1);
	keySeq->AddAndPopChildL(mod);

	//	The modulus	Q
	const TInteger& modulus2 = aPrivateKey.Q();
	
	CASN1EncBigInt* mod2 = CASN1EncBigInt::NewLC(modulus2);
	keySeq->AddAndPopChildL(mod2);
	
	//	The base G 
	const TInteger& base = aPrivateKey.G();

	CASN1EncBigInt* b = CASN1EncBigInt::NewLC(base);
	keySeq->AddAndPopChildL(b);
	
	algSeq->AddAndPopChildL(keySeq);
	
	//	The private key X
	const TInteger& priv = aPrivateKey.X();	
	CASN1EncBigInt* x = CASN1EncBigInt::NewLC(priv);

	CASN1EncOctetString* octet = ElementToOctetL(*x);

	CleanupStack::PopAndDestroy(x);
	CleanupStack::PushL(octet);

	seq->AddAndPopChildL(octet);

	AddAttributesL(*seq, aAttributes);
	
	CleanupStack::Pop(seq);

	return seq;	
	}


/**
 * Encodes a DSA key in encrypted format.
 * 
 * @param aPrivateKey The private key to be encoded.
 * @param aEncryptor The object used to encrypt the data.
 * @param aData The encryption parameters of the given encryptor.
 *              These parameters are stored in the resulting sequence.
 *
 * @return An ASN1 Sequence encoding the encrypted key.
 */
/*static*/ EXPORT_C CASN1EncSequence* TASN1EncPKCS8::EncodeEncryptedL(const CDSAPrivateKey& aPrivateKey, CPBEncryptor& aEncryptor, 
                                                                      CPBEncryptParms& aData, const TDesC8& aAttributes)
	{
	// Now generate a PrivateKeyInfo type 
	CASN1EncSequence* privKeySeq = EncodeL(aPrivateKey, aAttributes);
	CleanupStack::PushL(privKeySeq);
	
	CASN1EncSequence* seq = EncryptedSequenceL(*privKeySeq, aEncryptor, aData);
	
	CleanupStack::PopAndDestroy(privKeySeq);
		
	return seq;
	}
