#include <map>
#include <string>
#include "byteBuffer.h"
#include "Util.h"

#include "CertificateObject.h"
#include "PublicKeyObject.h"
#include "PrivateKeyObject.h"

#include "CertificateObjectCeres.h"
#include "PublicKeyObjectCeres.h"
#include "PrivateKeyObjectCeres.h"

map<string,byteBuffer *>	mapP15name2file;

void loadP15( char * pFileName );
void dumpData( byte * pData, long lenData );

int main(int argc, char * argv[])
{
	long			idx;
	long			lTag;
	long			lDataLen;
	byteBuffer *	pbb;
	asn_dec_rval_t	decode_rv;
	asn_enc_rval_t	encode_rv;
	byteBuffer		bbEncode;

	//
	// Cargamos el fichero P15 (de viewPkcs15.pl)
	//
	for( idx = 1; idx<argc; ++idx )
		loadP15( argv[idx] );

	if(0) 
	{	
		// Se trata de una tarjeta DNIe
		// Fichero: P15_861641a1688c22.txt
		//
		// Analizamos una entrada del CDF
		//
		pbb = mapP15name2file["cdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		CertificateObject * asn1CDF			  = NULL;
		
		//pbb->at(0x5D) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_CertificateObject,
							  (void**)&asn1CDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el CDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_CertificateObject,
							  (void*)asn1CDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}


		//
		// Analizamos una entrada del PUKDF
		//
		pbb->zeroClear();
		pbb = mapP15name2file["pukdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		PublicKeyObject * asn1PukDF			  = NULL;
		
		//pbb->at(0x33) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_PublicKeyObject,
							  (void**)&asn1PukDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el PuKDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_PublicKeyObject,
							  (void*)asn1PukDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}


		//
		// Analizamos una entrada del PRKDF
		//
		pbb->zeroClear();
		pbb = mapP15name2file["prkdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		PrivateKeyObject * asn1PrkDF			  = NULL;
		
		//pbb->at(0xf6) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_PrivateKeyObject,
							  (void**)&asn1PrkDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el PuKDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_PrivateKeyObject,
							  (void*)asn1PrkDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}


	}
	else 
	{
		// Se trata de una TARJETA CERES
		// Fichero:	P15_aed541c8407bca.txt
		//
		// Analizamos una entrada del CDF
		//
		pbb = mapP15name2file["cdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		CertificateObjectCERES * asn1CDF			  = NULL;
		
		pbb->at(0x5D) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_CertificateObjectCERES,
							  (void**)&asn1CDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el CDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_CertificateObjectCERES,
							  (void*)asn1CDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}


		//
		// Analizamos una entrada del PUKDF
		//
		pbb->zeroClear();
		pbb = mapP15name2file["pukdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		PublicKeyObjectCERES * asn1PukDF			  = NULL;
		
		pbb->at(0x33) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_PublicKeyObjectCERES,
							  (void**)&asn1PukDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el PuKDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_PublicKeyObjectCERES,
							  (void*)asn1PukDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}


		//
		// Analizamos una entrada del PRKDF
		//
		pbb->zeroClear();
		pbb = mapP15name2file["prkdf"];
		lDataLen += CUtil::getTLV( &pbb->at(0), &lTag, &lDataLen );
		dumpData( &pbb->at(0), lDataLen );

		PrivateKeyObjectCERES * asn1PrkDF			  = NULL;
		
		pbb->at(0xf6) = 0x30;

		// Realizamos la decodificación BER de los datos miembros
		decode_rv = ber_decode( NULL,
							  &asn_DEF_PrivateKeyObjectCERES,
							  (void**)&asn1PrkDF,
							  (void*)&pbb->at(0),
							  lDataLen );
		if( decode_rv.code != RC_OK )
		{
			printf( "Error al decodificar el PuKDF. offset = 0x%x.\n", decode_rv.consumed );
			return -1;
		}

		//
		// Codificamos de nuevo
		//
		bbEncode.resize(2000);
		encode_rv = der_encode_to_buffer( &asn_DEF_PrivateKeyObjectCERES,
							  (void*)asn1PrkDF,
							  (void*)&bbEncode[0],
							  bbEncode.size() );

		if( encode_rv.encoded == -1 )
		{
			printf( "Error al codificar %s: %s\n", encode_rv.failed_type->name, strerror(errno) );
		}
		else
		{
			dumpData( &bbEncode[0], encode_rv.encoded );
		}

		if( encode_rv.encoded != lDataLen )
			printf( "\n\nTamaño diferentes al re-codificar: 0x%x --> 0x%x\n\n", lDataLen, encode_rv.encoded );

		lDataLen = min(encode_rv.encoded,lDataLen);

		for( idx=0; idx<lDataLen; ++idx )
		{
			if( pbb->at(idx) != bbEncode.at(idx) )
			{
				printf( "Diferentes en offset 0x%x\n", idx );
				break;
			}
		}
	}

	return 0;
}

void loadP15( char * pFileName )
{
	FILE * fd = fopen( pFileName, "r" );
	char	name[10];
	char	data[50000];

	if( !fd )
	{
		printf( "Error al abrir %s\n", pFileName );
		return;
	}

	while( fscanf( fd, "$%[a-z]=hex2os('%[0-9a-f]'); ", name, data )==2 )
	{
		mapP15name2file[name] = new byteBuffer( data );
	}

	fclose(fd);
}

void dumpData( byte * pData, long lenData )
{
	long	i, j;
	int		pos;
	char	buffer[256];

	for (i=0; i<lenData; i+= 16)
	{
		pos = sprintf( buffer, "FNMT_P11 %6x - ",i);
		for (j = 0;j<16;j++) 
		{
			if ((i +j) < lenData)
				pos += sprintf( &buffer[pos], "%02x ", pData[i+j] );
			else
				pos += sprintf( &buffer[pos], "   " );
		}

		pos += sprintf( &buffer[pos], "    " );
		for (j = 0;j<16;j++) 
		{
			if ((i +j) < lenData)
				pos += sprintf( &buffer[pos], "%c", isalnum(pData[i+j]) ? pData[i+j] : '.' );
		}
		pos += sprintf( &buffer[pos], "\n" );
		printf(buffer);
		OutputDebugString(buffer);
	}

	printf("\n");
}

