#include "Comm_DNIe.h"

#include "integer.h"
#include "sha.h"
#include "asn.h"
#include "oids.h"
#include "modes.h"
#include "des.h"

DEFINE_OID(CryptoPP::ASN1::pkcs_1()+5, sha1withRSAEncryption);

using namespace CryptoPP;


void CUtil::verify_X509_cert( byteBuffer & X509_certificate, RSA_PublicKey & caKey, RSA_PublicKey & outKey )
{
	ByteQueue		certin;
	ByteQueue		toBeSigned;
	SecByteBlock	signature;
	SecByteBlock	plain;
	OID				sigAlgOID;
	ByteQueue		keyout;

	certin.Put( &X509_certificate[0], X509_certificate.size() );

	//
	// Analizamos el primer nivel del certificado, para comprobar su firma
	//
	try {
		BERSequenceDecoder x509CertDec(certin);
			//
			// Extraemos la parte a ser firmada
			//
			BERSequenceDecoder toBeSignedDec(x509CertDec);
			DERSequenceEncoder toBeSignedEnc(toBeSigned);
			toBeSignedDec.TransferAllTo(toBeSignedEnc);
			toBeSignedEnc.MessageEnd();

			//
			// Extraemos el algoritmo de firma
			//
			BERSequenceDecoder sigAlgDec(x509CertDec);
			sigAlgOID.BERDecode(sigAlgDec);
			sigAlgDec.SkipAll();

			//
			// Extraemos la firma
			//
			unsigned int unused = 0;
			BERDecodeBitString( x509CertDec, signature, unused );

		x509CertDec.SkipAll();
	}
	catch( exception & )
	{
		throw Pkcs11Exception( CKR_DATA_INVALID );
	}
	//
	// El algoritmo debe ser RSA+SHA1
	//
	if( sigAlgOID != sha1withRSAEncryption() )
		throw Pkcs11Exception( CKR_DATA_INVALID );

	//
	// Calculamos el Hash de los datos en toBeSigned
	//
	SHA1		sha1;
	byteBuffer	tbs_hash(sha1.DigestSize());
	byteBuffer	tbs_data((unsigned int)toBeSigned.MaxRetrievable());

	toBeSigned.Get(&tbs_data[0],tbs_data.size());

	sha1.CalculateDigest(&tbs_hash[0], &tbs_data[0], tbs_data.size() );


	//
	// Desciframos la firma
	//
	RSA_public( caKey, signature, plain );

	//
	// Comparamos el Hash calculado con el de la firma
	//
	pkcs1_5_Unpad( plain );

	if( plain.size() != 20 || memcmp(&plain[0],&tbs_hash[0],20) )
		throw Pkcs11Exception( CKR_DATA_INVALID );

	//
	// La firma es correcta. Vamos a extraer la clave pública.
	//
	toBeSigned.Put( &tbs_data[0], tbs_data.size() );
	try
	{
        // ASN.1 from RFC 3280
        // TBSCertificate  ::=  SEQUENCE  {
		BERSequenceDecoder toBeSignedDec(toBeSigned);
	 
			// version         [0]  EXPLICIT Version DEFAULT v1,
			BERGeneralDecoder contextDec(toBeSignedDec,0xa0); // consume the context tag on the version
			word32 ver;
			BERDecodeUnsigned<word32>(contextDec,ver,INTEGER,2,2); // only want a v3 cert         

			// serialNumber         CertificateSerialNumber,
			Integer serial;
			serial.BERDecode(toBeSignedDec);

			// signature            AlgorithmIdentifier,
			BERSequenceDecoder signatureDec(toBeSignedDec);
			signatureDec.SkipAll();

			// issuer               Name,
			BERSequenceDecoder issuerNameDec(toBeSignedDec);
			issuerNameDec.SkipAll();

			// validity             Validity,
			BERSequenceDecoder validityDec(toBeSignedDec);
			validityDec.SkipAll();

			// subject              Name,
			BERSequenceDecoder subjectNameDec(toBeSignedDec);
			subjectNameDec.SkipAll();

			// subjectPublicKeyInfo SubjectPublicKeyInfo,
			BERSequenceDecoder subjectPublicKeyInfoDec(toBeSignedDec);
		
				BERSequenceDecoder algorithmDec(subjectPublicKeyInfoDec);
				sigAlgOID.BERDecode(algorithmDec);
				algorithmDec.SkipAll();

				BERGeneralDecoder subjectPublicKeyDec(subjectPublicKeyInfoDec, BIT_STRING);
					subjectPublicKeyDec.CheckByte(0);	// unused bits
					
					BERSequenceDecoder rsaPubKeyDec(subjectPublicKeyDec);
						outKey.m_n.BERDecode(rsaPubKeyDec);
						outKey.m_e.BERDecode(rsaPubKeyDec);
					rsaPubKeyDec.SkipAll();
				subjectPublicKeyDec.MessageEnd();


			subjectPublicKeyInfoDec.SkipAll();
		toBeSignedDec.SkipAll();
	}
	catch ( exception & )
	{
		throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

void CUtil::RSA_public( RSA_PublicKey & key, byteBuffer & in, byteBuffer & out )
{
	Integer	integer_in(&in[0],in.size());
	Integer	integer_out;

	integer_out = a_exp_b_mod_c(integer_in, key.m_e, key.m_n );

	out.clear();
	out.resize( key.m_n.ByteCount(), 0 );

	integer_out.Encode( &out[key.m_n.ByteCount()-integer_out.ByteCount()], integer_out.ByteCount() );
}

void CUtil::RSA_public( RSA_PublicKey & key, SecByteBlock & in, SecByteBlock & out )
{
	Integer	integer_in(&in[0],in.size());
	Integer	integer_out;

	integer_out = a_exp_b_mod_c(integer_in, key.m_e, key.m_n );

	out.CleanNew( key.m_n.ByteCount() );

	integer_out.Encode( &out[key.m_n.ByteCount()-integer_out.ByteCount()], integer_out.ByteCount() );
}

void CUtil::RSA_private( RSA_PrivateKey & key, byteBuffer & in, byteBuffer & out )
{
	Integer	integer_in(&in[0],in.size());
	Integer	integer_out;

	integer_out = a_exp_b_mod_c(integer_in, key.m_d, key.m_n );

	out.clear();
	out.resize( key.m_n.ByteCount(), 0 );

	integer_out.Encode( &out[key.m_n.ByteCount()-integer_out.ByteCount()], integer_out.ByteCount() );
}

void CUtil::sha1Update( SHA1 & sha1, byteBuffer & data )
{
	sha1.Update(&data[0], data.size());
}

void CUtil::sha1Final( SHA1 & sha1, byteBuffer & signature )
{
	sha1.Final(&signature[0]);
}

byteBuffer CUtil::sha1( byteBuffer & hashIn )
{
	SHA1		sha1;

	byteBuffer	hash(sha1.DigestSize());

	sha1.CalculateDigest( &hash[0], &hashIn[0], hashIn.size() );

	return hash;
}

byteBuffer CUtil::sha1( byte * hashIn, DWORD len )
{
	SHA1		sha1;

	byteBuffer	hash(sha1.DigestSize());

	sha1.CalculateDigest( &hash[0], hashIn, len );

	return hash;
}

byteBuffer CUtil::sigmin( byteBuffer a, Integer ib )
{
	Integer		ia;
	byteBuffer	result;

	ia.Decode( &a[0], a.size() );

	ib -= ia;
	if( ia < ib )
	{
		result.resize( ia.ByteCount() );
		ia.Encode( &result[0], result.size() );
	}
	else
	{
		result.resize( ib.ByteCount() );
		ib.Encode( &result[0], result.size() );
	}

	return result;
}


void CUtil::DES_EDE2_cipher( byteBuffer & data, byteBuffer desKey )
{
	byteBuffer	iv(8);
	CBC_Mode< DES_EDE2 >::Encryption Encryptor; 

	if( desKey.size() != 16 )
		throw Pkcs11Exception( CKR_DATA_INVALID );

	memset(&iv[0],0,iv.size());

	Encryptor.SetKeyWithIV( &desKey[0], desKey.size(), &iv[0], iv.size() ); 
	Encryptor.ProcessData( &data[0],&data[0], data.size() ); 
}

void CUtil::DES_EDE2_decipher( byteBuffer & data, byteBuffer desKey )
{
	byteBuffer	iv(8);
	CBC_Mode< DES_EDE2 >::Decryption Decryptor; 

	if( desKey.size() != 16 )
		throw Pkcs11Exception( CKR_DATA_INVALID );

	memset(&iv[0],0,iv.size());

	Decryptor.SetKeyWithIV( &desKey[0], desKey.size(), &iv[0], iv.size() ); 
	Decryptor.ProcessData( &data[0],&data[0], data.size() ); 
}

void CUtil::GenerateRandomBlock(byte *output, size_t size)
{
	static AutoSeededRandomPool rng;

	rng.GenerateBlock( output, size );
}
