#include "FNMT_P11.h"
#include "Comm_DNIe.h"
#include "resource.h"
#include "SlotTransaction.h"

#include "Util.h"
#include "modes.h"		// Para modos de cifrado CBC EBC ...
#include "des.h"		// Para el cálculo del MAC
using namespace CryptoPP;


#ifndef WIN32

#ifdef __APPLE__

extern "C" int AskUserAuth()
{
#if defined _CFG_TIF_REAL_
	return system("/Library/Libpkcs11-tif/bin/DialogSign.app/Contents/MacOS/DialogSign tif");
#else
	return system("/Library/Libpkcs11-#CFGDIST#/bin/DialogSign.app/Contents/MacOS/DialogSign dnie");
#endif
}

#else

#include <assuan.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <libintl.h>
#include <config.h>

#define BUFF_LENGTH 500
#define BUFF_LANG_LENGTH 5

#define LANG_ES "es_ES"
#define LANG_CA "ca_ES"
#define LANG_EU "eu_ES"
#define LANG_GL "gl_ES"
#define LANG_PT "pt_PT"

void GetDialogMessage(char **msg)
{
        char lang_detected[BUFF_LANG_LENGTH];
        char *lang;
        lang = getenv("LANG");
	//printf("\n%s\n",lang);

#if defined _CFG_TIF_REAL_
        if(strncmp(lang, LANG_ES, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Est\u00E1 a punto de realizar una firma electr\u00F3nica con su clave de FIRMA de la tarjeta identificativa de funcionario (TIF). \u00BFDesea permitir esta operaci\u00F3n?");
        }else if(strncmp(lang, LANG_CA, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Esteu a punt de generar una signatura electr\u00F2nica utilitzant la vostra clau de signatura (certificat FIRMA) de la vostre targeta identificativa de funcionari (TIF). Voleu autoritzar aquesta operaci\u00F3?");
        }else if(strncmp(lang, LANG_EU, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC TIF elektronikoaren sinadura klabearekin sinadura elektronikoa egingo duzu. Jarraitu nahi duzu?");
        }else if(strncmp(lang, LANG_GL, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Est\u00E1 a piques de xerar unha sinatura electr\u00F3nica coa s\u00FAa sinatura (Firm) clave de s\u00FAa tarxeta de identificaci\u00F3n oficial (TIF). Quere permitir que esta operaci\u00F3n?");
        }else if(strncmp(lang, LANG_PT, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC voc\u00EA est\u00E1 prestes a gerar uma assinatura eletr\u00F4nica com sua assinatura (FIRMA) chave de seu Cart\u00E3o de Identifica\u00E7\u00E3o oficial (TIF). Voc\u00EA quer permitir que essa opera\u00E7\u00E3o?");
        }else{
                sprintf(*msg, "SETDESC You are about to generate an electronic signature using your SIGNATURE (FIRMA) key of your official ID Card (TIF). Do you want to allow this operation?");
        }
#else
        if(strncmp(lang, LANG_ES, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Est\u00E1 a punto de realizar una firma electr\u00F3nica con su clave de FIRMA del DNI electr\u00F3nico. \u00BFDesea permitir esta operaci\u00F3n?");
        }else if(strncmp(lang, LANG_CA, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Esteu a punt de generar una signatura electr\u00F2nica utilitzant la vostra clau de signatura (certificat FIRMA) del vostre DNI electrònic. Voleu autoritzar aquesta operaci\u00F3?");
        }else if(strncmp(lang, LANG_EU, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC NAN elektronikoaren sinadura klabearekin sinadura elektronikoa egingo duzu. Jarraitu nahi duzu?");
        }else if(strncmp(lang, LANG_GL, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC Est\u00E1 a piques de xerar unha sinatura electr\u00F3nica coa s\u00FAa sinatura (Firm) clave de s\u00FAa tarxeta de identificaci\u00F3n Espa\u00F1ol Electr\u00F3nico (DNI electr\u00F3nico). Quere permitir que esta operaci\u00F3n?");
        }else if(strncmp(lang, LANG_PT, BUFF_LANG_LENGTH)==0){
                sprintf(*msg, "SETDESC voc\u00EA est\u00E1 prestes a gerar uma assinatura eletr\u00F4nica com sua assinatura (FIRMA) chave de seu Cart\u00E3o de Identifica\u00E7\u00E3o Espanhol Eletr\u00F4nico (DNI electr\00F3nico). Voc\u00EA quer permitir que essa opera\u00E7\u00E3o?");
        }else{
                sprintf(*msg, "SETDESC You are about to generate an electronic signature using your SIGNATURE (FIRMA) key of your Spanish Electronic ID Card (DNI electrónico). Do you want to allow this operation?");
        }
#endif
}



extern "C" int AskUserAuth()
{
	int r;
	const char *argv[3];
	const char *pgmname = PIN_ENTRY; 
	assuan_context_t ctx;
	char *buf = (char *)malloc(BUFF_LENGTH*sizeof(char));
        char buf_lang[BUFF_LANG_LENGTH];
        char *lang;

	argv[0] = pgmname;
	argv[1] = NULL;

	assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); 
	r = assuan_new(&ctx); 
	if (r) { 
		printf("Can't initialize assuan context: %s\n)", gpg_strerror(r));
		goto err; 
	} 

	r = assuan_pipe_connect(ctx, pgmname, (const char **) argv, 0, NULL, NULL, 0);
	if (r) {
		printf("Can't connect to the PIN entry module: %s\n", gpg_strerror(r));
		goto err;
	}

	GetDialogMessage(&buf);

	r = assuan_transact(ctx,buf, NULL, NULL, NULL, NULL, NULL, NULL);
	if (r) {
		printf("SETDESC: %s\n",gpg_strerror(r));
		goto err;
	}

	while (1) {
		r = assuan_transact(ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
		if (gpg_err_code(r) == GPG_ERR_ASS_CANCELED or gpg_err_code(r) == GPG_ERR_CANCELED) { 
			assuan_release(ctx);
			return CKR_CANCEL;
		}
		if (r) {
			printf("SETERROR: %s\n", gpg_strerror(r));
			goto err;
		}
		if (r == CKR_OK)
			break;
	}
	
	free(buf);

	assuan_release(ctx);
	return CKR_OK;
	err:	
	assuan_release(ctx);
	return CKR_GENERAL_ERROR;
}

#endif
#endif

extern CSessionList & gSessionList;

#define MAX_CLAVES 40

#define INCLUIR_TEMPLATE(pTemplate, tipo, valor, len, cont) \
	pTemplate = (CK_ATTRIBUTE_PTR)realloc(pTemplate, \
			cont*sizeof(CK_ATTRIBUTE)); \
	pTemplate[cont-1].type = tipo; \
	pTemplate[cont-1].pValue = valor; \
	pTemplate[cont-1].ulValueLen = len;


CSession::CSession( CK_SLOT_ID slotID)
	:m_Slot(g_rSlotList.GetSlot(slotID))//, m_CDF(m_Slot)
{
	// Inicializamos las variables miembro de la clase
	m_pSigningMechanism		= NULL;
	m_pVerifyingMechanism	= NULL;
	m_pDecryptingMechanism	= NULL;

	m_pComm				= NULL;
	m_bFindOnGoing		= false;
	m_bSignOnGoing		= false;
	m_bVerifyOnGoing	= false;
	m_bDecryptOnGoing	= false;

	m_slotID			= slotID;
	m_state				= -1;

	m_sessionFlags		= 0;
	m_ulDeviceError		= 0;			// ¿?

	// Configuración ASN1, que será en función del TokenType
	
	m_sessionASN1Type			= m_Slot.getTokenType().GetASN1Type();
	m_sessionASN1PathEncType	= m_Slot.getTokenType().GetPathEncodingType();

	switch( m_Slot.getTokenType().getCommType() )
	{
	case CTokenType::COMM_TYPE_DNIE:
			m_pComm = new CComm_DNIe( m_Slot );
			break;
	case CTokenType::COMM_TYPE_CERES1_0:
			m_pComm = new CComm_Ceres1_0( m_Slot );
			break;
	default:
			m_pComm = new CComm_Plain( m_Slot );
			break;
	};
}

CSession::~CSession(void)
{
	if (m_pComm)
		delete m_pComm;

	m_pComm = NULL;

	if (m_pSigningMechanism)
	{
		delete m_pSigningMechanism;
		m_pSigningMechanism = NULL;
	}

	if (m_pVerifyingMechanism)
	{
		delete m_pVerifyingMechanism;
		m_pVerifyingMechanism = NULL;
	}

	if (m_pDecryptingMechanism)
	{
		delete m_pDecryptingMechanism;
		m_pDecryptingMechanism = NULL;
	}

	map<CK_SESSION_HANDLE, CK_FLAGS>::iterator it;

	// Eliminamos de la tabla todos los handles asociados a este Slot
	for(it = m_maphSession.begin();  it != m_maphSession.end(); it++)
	{
		try
		{
			gSessionList.DeleteHandle(it->first);
		}
		catch (Pkcs11Exception)
		{
		}
	}
}


bool CSession::IsLogged()
{
	return	m_state == CKS_RO_USER_FUNCTIONS ||
			m_state == CKS_RW_USER_FUNCTIONS ||
			m_state == CKS_RW_SO_FUNCTIONS;
}


ulong CSession::GetNumberOfHandles( void )
{
	return (ulong)m_maphSession.size();
}



CK_RV CSession::OpenSession( CK_FLAGS              flags,
							 CK_VOID_PTR		   pApplication,
							 CK_NOTIFY             Notify,
							 CK_SESSION_HANDLE_PTR phSession)
{
	CK_RV rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	// Si no habíamos abierto antes ninguna otra sesión
	if( m_state == -1 )
	{
		//
		// Inicializamos el estado y los flags de la sesión
		if(flags & CKF_RW_SESSION)
			m_state	= CKS_RW_PUBLIC_SESSION;
		else
			m_state = CKS_RO_PUBLIC_SESSION;

		m_sessionFlags = flags;
	}
	else
	//
	// Ya teníamos alguna sesión abierta. Comprobamos coherencia
	//
	{
		//
		// Intentamos abrir una sesión ReadOnly cuando ya teníamos abierta una sesión RW-SO
		// Ver PKCS#11 v2.20 pag 118
		//
		if( !(flags & CKF_RW_SESSION)&&
			 (m_state == CKS_RW_SO_FUNCTIONS) )
			throw Pkcs11Exception(CKR_SESSION_READ_WRITE_SO_EXISTS);

		if( flags & CKF_RW_SESSION )
		{
			if( m_state == CKS_RO_PUBLIC_SESSION )
				m_state = CKS_RW_PUBLIC_SESSION;
			else if( m_state == CKS_RO_USER_FUNCTIONS )
				m_state = CKS_RW_USER_FUNCTIONS;
		}
		else // Es una sesion RO
		{
			//
			// No cambiamos el estado. Si ya había una sesión RW, quedarán todas como RW
			//
		}

		//
		// Actualizamos los flags. Alguna RW implica RW para todas.
		//
		m_sessionFlags &= flags;

	}

	// Cargamos el parseo del tokeninfo en el objeto sesión
	m_Slot.ReadTokenInfo(*m_pComm);

	/////////////////////////////////////////////////////////////////////////////
	//
	// Carga y parseo del CDF
	//

	// Nos aseguramos de haber leído el CDF y después trataremos de decodificarlo
 	if(!m_CDF.m_loaded)
	{
		m_CDF.LoadP15File(*m_pComm);

		m_CDF.AddObjects2List(m_ObjectList);

		// Si algún elemento utiliza formatos no-ISO o path en ASCII,
		// entonces todas las escrituras deberán seguir ese formato.
		if( m_CDF.GetPathEncodingType() != PATHENC_BIN )
			m_sessionASN1PathEncType = m_CDF.GetPathEncodingType();	//PATHENC_ASCII;

		if( m_CDF.GetASN1Type() != ASN1TYPE_ISO )
			m_sessionASN1Type = m_CDF.GetASN1Type();		//ASN1TYPE_CERES;
	}

	/////////////////////////////////////////////////////////////////////////////
	//
	// Carga y parseo del PrKDF
	//

	// Cargamos el PrKDF de la tarjeta para dejarlo en memoria
	// Nos aseguramos de haber leído el PrKDF y después trataremos de decodificarlo
	if(!m_PrKDF.m_loaded)
	{
		m_PrKDF.LoadP15File(*m_pComm);

		m_PrKDF.AddObjects2List(m_ObjectList);

		// Si algún elemento utiliza formatos no-ISO o path en ASCII,
		// entonces todas las escrituras deberán seguir ese formato.
		if( m_PrKDF.GetPathEncodingType() != PATHENC_BIN )
			m_sessionASN1PathEncType = m_PrKDF.GetPathEncodingType();	//PATHENC_ASCII;

		if( m_PrKDF.GetASN1Type() != ASN1TYPE_ISO )
			m_sessionASN1Type = m_PrKDF.GetASN1Type();	//ASN1TYPE_CERES;
	}

	/////////////////////////////////////////////////////////////////////////////
	//
	// Carga y parseo del PuKDF
	//

	// Cargamos el PuKDF de la tarjeta para dejarlo en memoria
	// Nos aseguramos de haber leído el PuKDF y después trataremos de decodificarlo
	if(!m_PuKDF.m_loaded)
	{
		m_PuKDF.LoadP15File(*m_pComm);

		m_PuKDF.AddObjects2List(m_ObjectList);

		// Si algún elemento utiliza formatos no-ISO o path en ASCII,
		// entonces todas las escrituras deberán seguir ese formato.
		if( m_PuKDF.GetPathEncodingType() != PATHENC_BIN )
			m_sessionASN1PathEncType = m_PuKDF.GetPathEncodingType();	//PATHENC_ASCII;

		if( m_PuKDF.GetASN1Type() != ASN1TYPE_ISO )
			m_sessionASN1Type = m_PuKDF.GetASN1Type();	//ASN1TYPE_CERES;
	}

	//
	// Carga y parseo del DODF
	//

	// Cargamos el DODF de la tarjeta para dejarlo en memoria
	// Nos aseguramos de haber leído el DODF y después trataremos de decodificarlo
	if(!m_DODF.m_loaded)
	{
		m_DODF.LoadP15File(*m_pComm);

		m_DODF.AddObjects2List(m_ObjectList);

		// Si algún elemento utiliza formatos no-ISO o path en ASCII,
		// entonces todas las escrituras deberán seguir ese formato.
		if( m_DODF.GetPathEncodingType() != PATHENC_BIN )
			m_sessionASN1PathEncType = m_DODF.GetPathEncodingType();	//PATHENC_ASCII;

		if( m_DODF.GetASN1Type() != ASN1TYPE_ISO )
			m_sessionASN1Type = m_DODF.GetASN1Type();	//ASN1TYPE_CERES;
	}

	// Generamos un nuevo handle y lo apuntamos en el mapa de sesiones
	*phSession = gSessionList.GetNewSessionHandle(m_slotID);
	m_maphSession.insert(pair<CK_SESSION_HANDLE, CK_FLAGS>(*phSession, flags));

	return rv_result;
}


CK_RV CSession::CloseSession( CK_SESSION_HANDLE hSession )
{
	//
	// Eliminamos el handle de la lista.
	// Si fuera el último, la función C_CloseSession llamará al destructor
	//
	map<CK_SESSION_HANDLE, CK_FLAGS>::iterator it = m_maphSession.find(hSession);

	if( it == m_maphSession.end() )
		return CKR_SESSION_HANDLE_INVALID;

	m_maphSession.erase( it );

	return CKR_OK;
}

CK_RV CSession::GetSessionInfo( CK_SESSION_INFO_PTR pInfo )
{
	pInfo->slotID = m_slotID;
	pInfo->flags = m_sessionFlags;
	pInfo->state = m_state;
	pInfo->ulDeviceError = m_ulDeviceError;

	//DWORD state = m_Slot.GetState().dwCurrentState;

	// Si en la sesión se ha extraído la tarjeta, lo devolvemos como respuesta
	if(!m_Slot.SupportedTokenPresent())
		return CKR_DEVICE_REMOVED;

	return CKR_OK;
}

CK_RV CSession::Login( CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen )
{
	CSlotTransaction	transaction(m_Slot);

	// Posibles estados con los que nos podemos encontrar
	// 1.- CKS_RO_PUBLIC_SESSION
	// 2.- CKS_RO_USER_FUNCTIONS
	// 3.- CKS_RW_PUBLIC_SESSION
	// 4.- CKS_RW_USER_FUNCTIONS
	// 5.- CKS_RW_SO_FUNCTIONS

	// Comprobamos la validez de la petición de login
	if ((m_state == CKS_RW_SO_FUNCTIONS) &&
		(userType == CKU_USER))
		return CKR_USER_ANOTHER_ALREADY_LOGGED_IN;

	if(((m_state==CKS_RO_USER_FUNCTIONS)  ||
		(m_state==CKS_RW_USER_FUNCTIONS)) &&
		(userType == CKU_SO))
		return CKR_USER_ANOTHER_ALREADY_LOGGED_IN;

	if((m_state==CKS_RO_USER_FUNCTIONS)  ||
		(m_state==CKS_RW_USER_FUNCTIONS) ||
		(m_state==CKS_RW_SO_FUNCTIONS))
		return CKR_USER_ALREADY_LOGGED_IN;

	if( (m_state==CKS_RO_PUBLIC_SESSION) &&
		(userType == CKU_SO))
		return CKR_SESSION_READ_ONLY_EXISTS;

	// Realizamos el login
	m_pComm->VerifyPIN( pPin, ulPinLen );

	// Actualizamos el estado de la sesión
	if( userType == CKU_SO )
	{
		if (m_state == CKS_RW_PUBLIC_SESSION)
			m_state = CKS_RW_SO_FUNCTIONS;
	}
	else // CKU_USER
	{
		//
		// La sesión era una '...PUBLIC_SESSION'. Actualizamos a '...USER_FUNCTIONS'
		//
		if( m_state == CKS_RO_PUBLIC_SESSION )
			m_state = CKS_RO_USER_FUNCTIONS;
		else
			m_state = CKS_RW_USER_FUNCTIONS;
	}

	return CKR_OK;
}

CK_RV CSession::Logout( void )
{
	if( !IsLogged() )
		return CKR_USER_NOT_LOGGED_IN;

	if( m_state == CKS_RO_USER_FUNCTIONS )
		m_state = CKS_RO_PUBLIC_SESSION;
	else
		m_state = CKS_RW_PUBLIC_SESSION;

	return CKR_OK;
}

CK_RV CSession::SetPIN( CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen,
						CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen)
{
	CSlotTransaction	transaction(m_Slot);

	// Realizamos el cambio de PIN
	m_pComm->ChangePIN( pOldPin, ulOldLen, pNewPin, ulNewLen );

	return CKR_OK;
}

CK_RV CSession::UnblockPIN( CK_UTF8CHAR_PTR pUnblockCode, CK_ULONG ulUnblockCodeLen,
							CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen )
{
	CSlotTransaction	transaction(m_Slot);

	// Realizamos el desbloqueo con cambio de PIN
	m_pComm->UnblockPIN( pUnblockCode, ulUnblockCodeLen, pNewPin, ulNewLen );

	return CKR_OK;
}

CK_RV CSession::ReadAttributesForSearch( CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount )
{
	//
	// Analizamos si la plantilla incluye CKA_VALUE, o algun componente público
	//
	bool	bSearchValue = false;
	bool	bSearchKeyComponent = false;
	for( CK_ULONG idx=0; idx<ulCount; ++idx )
	{
		switch( pTemplate[idx].type )
		{
		case CKA_VALUE:
			bSearchValue = true;
			break;
		case CKA_MODULUS:
		case CKA_PUBLIC_EXPONENT:
			bSearchKeyComponent = true;
			break;
		}
	}

	if( bSearchValue || bSearchKeyComponent )
	{
		for( ulong idx=0; idx<m_ObjectList.GetNumberOfObjets(); idx++ )
		{
			try
			{
				CPKCS11Object & p11Obj = m_ObjectList.at(idx);

				switch( p11Obj.GetAttributeUlong(CKA_CLASS) )
				{
				case CKO_DATA:
				case CKO_CERTIFICATE:
					if( bSearchValue && !p11Obj.GetAttribute(CKA_VALUE) )
						ReadValue( &p11Obj );
					break;
				case CKO_PUBLIC_KEY:
				case CKO_PRIVATE_KEY:
					if( bSearchKeyComponent && p11Obj.GetAttributeUlong(CKA_KEY_TYPE) == CKK_RSA &&
						(!p11Obj.GetAttribute(CKA_MODULUS) || !p11Obj.GetAttribute(CKA_PUBLIC_EXPONENT)) )
						ReadRSAPublicComponents( (CPKCS11KeyObject *)&p11Obj );
					break;
				}
			}
			catch( Pkcs11Exception &  )
			{
				//
				// Si no conseguimos leer algunos atributos, seguimos con el resto
				//
			}
		}

	}
	return CKR_OK;
}

CK_RV CSession::FindObjectsInit( CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount )
{
	CSlotTransaction	transaction(m_Slot);

	//
	// Comprobamos que no exista una búsqueda en curso
	//
	if( m_bFindOnGoing )
		return CKR_OPERATION_ACTIVE;

	ReadAttributesForSearch( pTemplate, ulCount );

	m_ObjectList.FindMatchingObjets( pTemplate, ulCount, !IsLogged(), m_FindResult );

	m_bFindOnGoing = true;

	return CKR_OK;
}


CK_RV CSession::FindObjects( CK_OBJECT_HANDLE_PTR phObject,
							 CK_ULONG ulMaxObjectCount,
							 CK_ULONG_PTR pulObjectCount )
{
	//
	// Comprobamos que se haya iniciado una búsqueda
	//
	if( !m_bFindOnGoing )
		return CKR_OPERATION_NOT_INITIALIZED;

	//
	// Devolveremos los objetos encontrados, o
	//
	*pulObjectCount = min( ulMaxObjectCount, (CK_ULONG)m_FindResult.size() );

	if( phObject )
	{
		for( ulong idx=0; idx<*pulObjectCount; ++idx )
			phObject[idx] = m_FindResult[idx]->GetHandle();

		if( *pulObjectCount )
			m_FindResult.erase(m_FindResult.begin(),m_FindResult.begin()+*pulObjectCount);
	}

	return CKR_OK;
}

CK_RV CSession::FindObjectsFinal( void )
{
	//
	// Comprobamos que se haya iniciado una búsqueda
	//
	if( !m_bFindOnGoing )
		return CKR_OPERATION_NOT_INITIALIZED;

	//
	// Indicamos el final de la búsqueda
	//
	m_bFindOnGoing = false;

	return CKR_OK;
}

CK_RV CSession::GetAttributeValue(  CK_OBJECT_HANDLE	hObject,
									CK_ATTRIBUTE_PTR	pTemplate,
									CK_ULONG			ulCount )
{
	CK_RV			rv_result = CKR_OK;
	byteBuffer *	pAttribute;
	bool			bSensitive = false;

	CPKCS11Object * pObj = m_ObjectList.GetObjectFromHandle(hObject);
	CK_ULONG ulObjClass = pObj->GetAttributeUlong( CKA_CLASS );

	//
	// Comprobamos si el objeto es "sensitive" o "unextractable"
	//
 	if ( ulObjClass == CKO_PRIVATE_KEY)
	{
		if ( pObj->GetAttributeBool( CKA_SENSITIVE ) ||
			( !pObj->GetAttributeBool( CKA_EXTRACTABLE ) ) )
		{
			bSensitive = true;
		}
	}

	for( ulong idx = 0; idx<ulCount; ++idx )
	{
		//
		// Si el objeto es "sensitive" o "unextractable", comprobamos si el atributo es accesible
		//
		if (bSensitive && pObj->IsSensitive( pTemplate[idx].type ) )
		{
			pTemplate[idx].ulValueLen = -1;
			rv_result = CKR_ATTRIBUTE_SENSITIVE;
			continue;
		}

		pAttribute = pObj->GetAttribute( pTemplate[idx].type );

		if( !pAttribute )
		{
			//
			// Gestión de atributos no incluídos en el P15 (CKA_VALUE, CKA_MODULUS, CKA_PUBLIC_EXPONENT)
			//
			switch ( pTemplate[idx].type )
			{

			case CKA_VALUE:
				if ( ( ulObjClass == CKO_DATA ) || ( ulObjClass == CKO_CERTIFICATE ) )
				{
					CSlotTransaction	transaction(m_Slot);

					ReadValue( pObj );
					pAttribute = pObj->GetAttribute( pTemplate[idx].type );
				}

				break;

			case CKA_MODULUS:
			case CKA_PUBLIC_EXPONENT:
				if ( ( ulObjClass == CKO_PUBLIC_KEY ) || ( ulObjClass == CKO_PRIVATE_KEY ) )
				{
					CSlotTransaction	transaction(m_Slot);

					ReadRSAPublicComponents( (CPKCS11KeyObject *)pObj );
					pAttribute = pObj->GetAttribute( pTemplate[idx].type );
				}
				break;
			}
		}

		if( !pAttribute )
		{
			pTemplate[idx].ulValueLen = -1;
			rv_result = CKR_ATTRIBUTE_TYPE_INVALID;
			continue;
		}

		if( !pTemplate[idx].pValue )
			pTemplate[idx].ulValueLen = pAttribute->size();
		else if( pTemplate[idx].ulValueLen >= pAttribute->size() )
		{
			pTemplate[idx].ulValueLen = pAttribute->size();

			if( pTemplate[idx].ulValueLen )
				memcpy( pTemplate[idx].pValue, &pAttribute->at(0), pTemplate[idx].ulValueLen );
		}
		else
			rv_result = CKR_BUFFER_TOO_SMALL;
	}

	return rv_result;
}

CK_RV CSession::SetAttributeValue( CK_SESSION_HANDLE hSession,
									 CK_OBJECT_HANDLE  hObject,
									 CK_ATTRIBUTE_PTR  pTemplate,
									 CK_ULONG          ulCount )
{
	//CPKCS11Object	*pObject	= NULL;
	CP15File		*pFile		= NULL;
	CK_RV			rv_result	= CKR_OK;

	CSlotTransaction	transaction(m_Slot);

	//	Comprobamos que el template no está vacío.
	if( (pTemplate == NULL)||(ulCount == 0) )
		throw (CK_RV)CKR_ATTRIBUTE_TYPE_INVALID;

	try
	{
		// Comprobamos que los atributos de la plantilla no tienen valores nulos ni están repetidos
		Verificar_Atributos(pTemplate, ulCount);

		// Obtenemos el objeto al que hace referencia el handle. Si no se lanzará una excepción.
		CPKCS11Object * pObject = m_ObjectList.GetObjectFromHandle(hObject);

		// Si el objeto no es modificable devolveremos un error
		if( !pObject->GetAttributeBool(CKA_MODIFIABLE) )
			throw Pkcs11Exception( CKR_ATTRIBUTE_READ_ONLY );

		// Comprobamos que el estado de la sesión permite modificar el objeto PKCS#11
		CK_BBOOL isToken	= pObject->GetAttributeBool(CKA_TOKEN);
		CK_BBOOL isPrivate	= pObject->GetAttributeBool(CKA_PRIVATE);

		if ((isToken) && (isPrivate))
		{
			// PRIVATE TOKEN OBJECT
			// Unicamente puede crear el objeto un usuario autenticado
			// o tras haber presentado la clave Administrativa.
			if (m_state == CKS_RO_USER_FUNCTIONS)
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
			else
				if ( (m_state != CKS_RW_USER_FUNCTIONS) &&
					 (m_state != CKS_RW_SO_FUNCTIONS) )
					throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		else if ((isToken) && (!isPrivate))
		{
			// PUBLIC TOKEN OBJECT
			// Este tipo de objeto puede ser R/W por un usuario autenticado
			if ((m_state == CKS_RO_PUBLIC_SESSION) ||
				(m_state == CKS_RO_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
		}
		else if ((!isToken) && (isPrivate))
		{
			// PRIVATE SESSION OBJECT
			// El usuario debe estar autenticado.
			if ((m_state != CKS_RO_USER_FUNCTIONS) &&
				(m_state != CKS_RW_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		// else
		//    PUBLIC SESSION OBJECT
		//    Es independiente del estado en que se encuentre.

		// Verificamos que los atributos son válidos para ese tipo de objeto
		if(!pObject->ValidAttributes(pTemplate, ulCount))
			throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );

		// Comprobamos que los atributos a cambiar sean modificables
		if(!pObject->VerifyModifiableAttributes(pTemplate, ulCount))
			throw Pkcs11Exception( CKR_ATTRIBUTE_READ_ONLY );

		// Cargamos los atributos en el objeto PKCS#11, modificando los valores actuales
		pObject->CreateObject(pTemplate, ulCount);

		// Si estamos importando desde pfx, Mozilla extrae mal el label, así que lo modificaremos
		if( pObject->EsExportacionDesdeExplorer(pTemplate, ulCount) )
		{
			pObject->ModificarEtiquetaExportacion();
		}

		// Si el objeto es de Token deberemos actualizar los EF y DF del nuevo objeto P11.
		if(isToken)
		{
			// Creamos el puntero al DF adecuado, según el tipo de objeto
			CK_ULONG ulObjClass = pObject->GetAttributeUlong( CKA_CLASS );

			switch(ulObjClass)
			{
				case CKO_DATA:
					// Se crea un DataObject con los valores por defecto
					pFile = &m_DODF;
					break;

				case CKO_CERTIFICATE:
					// Se crea un CertificateObject con los valores por defecto
					pFile = &m_CDF;
					break;

				case CKO_PRIVATE_KEY:
					// Se crea un PrivateKeyObject con los valores por defecto
					pFile = &m_PrKDF;
					break;

				case CKO_PUBLIC_KEY:
					// Se crea un PublicKeyObject con los valores por defecto
					pFile = &m_PuKDF;
					break;

				default:
					throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );
			}

			// Si estamos intentando modificar un atributo especial deberemos cargarlo
			Cargar_AtributosEspeciales(pObject, pTemplate, ulCount);

			// Intentamos reutilizar del EFUS un fichero de características adecuadas. Si no,
			// este método se encarga de crear uno nuevo.
			FindOrCreate( pObject, isPrivate );

			// Indicaremos el tipo de codificación que debemos utilizar para el ASN1 y PATH
			pFile->SetPathEncodingType(m_sessionASN1PathEncType);
			pFile->SetASN1Type(m_sessionASN1Type);

			// Realizamos la codificación ASN1 del objeto PKCS#11 recién construído.
			pFile->WriteP15File(m_pComm);
		}

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	if(pFile)
	{
		//delete (pFile); // Debemos liberar esta memoria
		pFile = NULL;
	}

	return rv_result;
}


void CSession::ReadValue( CPKCS11Object * pObj )
{
	byteBuffer	data;

	m_pComm->readEF_CompressedData( *pObj->GetValuePath(), data );

	pObj->SetAttribute( CKA_VALUE, &data[0], data.size() );
}


void CSession::CompressValue( byteBuffer &data )
{
	m_pComm->CompressData( data );
}


void CSession::ReadRSAPublicComponents( CPKCS11KeyObject * pObj )
{
	byteBuffer	publicExponent;
	byteBuffer	modulus;

	try
	{
		m_pComm->ReadRSAPublicComponents( pObj->GetKeyReference(), publicExponent, modulus );

		pObj->SetAttribute( CKA_PUBLIC_EXPONENT, &publicExponent[0], publicExponent.size() );
		pObj->SetAttribute( CKA_MODULUS, &modulus[0], modulus.size() );
	}
	catch(Pkcs11Exception)
	{
		//
		// Si se produce algún error 7816, seguimos sin cargar los atributos.
		//
	}
}


//
// Funciones de firma de datos
//
CK_RV CSession::SignInit(  CK_MECHANISM_PTR  pMechanism,
						   CK_OBJECT_HANDLE  hKey )
{
	CK_RV	rv_result	= CKR_OK;

	try
	{
		// Comprobamos el handle del objeto hKey que nos pasan.
		CPKCS11Object * pPrivateKey = m_ObjectList.GetObjectFromHandle(hKey);

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pSigningMechanism )
		{
			delete(m_pSigningMechanism);
			m_pSigningMechanism = NULL;
		}

		// Inicializamos los miembros que controlan el proceso de firma.
		m_bSignOnGoing = false;
		m_Signature.zeroClear();
		m_SignatureUpdate.zeroClear();

		// Mostramos el diálogo de confirmación de uso para clave de FIRMA.
		byteBuffer *bpId = pPrivateKey->GetAttribute(CKA_ID);

		if( (bpId != NULL) && (bpId->at(0) == 0x46) )
		{
			// Si el identificador comienza por F mostramos el cuadro de diálogo
			//int salir = MessageBox(NULL, "Está a punto de realizar una firma digital con su clave de FIRMA.\n¿Desea continuar?" , "DNI electrónico" ,MB_ICONWARNING|MB_YESNO);

            #if defined WIN32
                char pBuffer[200];
                char pBuffer2[200];
                memset(pBuffer, 0, sizeof(pBuffer));
                memset(pBuffer2, 0, sizeof(pBuffer2));

                LoadString(g_hDllInst, IDS_INFO_T1, pBuffer,200);
                LoadString(g_hDllInst, IDS_INFO_T2, pBuffer2,200);

                int salir = MessageBox(NULL, pBuffer2 , pBuffer ,MB_ICONWARNING|MB_YESNO);
				if (salir<0x00)
				{
					// Ha ocurrido un error en la creación del cuadro de diálogo
					DWORD dwError = GetLastError();
					throw Pkcs11Exception(CKR_GENERAL_ERROR);
				}

				if(salir == 0)
					throw Pkcs11Exception(CKR_FUNCTION_CANCELED);

				if( (salir != IDYES) &&
					(salir != IDOK) )
					throw Pkcs11Exception(CKR_FUNCTION_FAILED);
            #else
		/* Check if we are using FIRMA private key */
		int salir = AskUserAuth();
		if(salir == CKR_CANCEL)
		{
			throw Pkcs11Exception(CKR_FUNCTION_CANCELED);
		}
		else if(salir!=CKR_OK)
		{
			// Ha ocurrido un error en la creación del cuadro de diálogo
			throw Pkcs11Exception(CKR_GENERAL_ERROR);
		}

            #endif
		}

		// Creamos el nuevo objeto mecanismo en función del indicado
		m_pSigningMechanism = m_Slot.newMechanism(pMechanism->mechanism);

		if(m_pSigningMechanism  == NULL)
			throw Pkcs11Exception(CKR_MECHANISM_INVALID);

		// Inicializamos el proceso de firma
		m_pSigningMechanism->SignInit(pPrivateKey, pMechanism->pParameter, pMechanism->ulParameterLen);

		m_bSignOnGoing = true;

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::Sign( CK_BYTE_PTR       pData,
					  CK_ULONG          ulDataLen,
					  CK_BYTE_PTR       pSignature,
					  CK_ULONG_PTR      pulSignatureLen )
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un proceso de firma.
		if( !m_bSignOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pSigningMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Si no tenemos la firma en memoria, la calculamos.
		if(m_Signature.size()==0)
		{							
			// Realizamos la firma sobre el mecanismo elegido
			byteBuffer bbData( pData, ulDataLen );
			m_pComm->SignMech( m_pSigningMechanism, bbData, m_Signature);
			
			// Ya deberíamos tener una firma válida en memoria. Si no, devolvemos error
			if(m_Signature.size()==0)
				throw Pkcs11Exception(CKR_DEVICE_ERROR);
		}

		// Si no hemos reservado aún memoria, devolveremos el tamaño necesario (en BYTES)
		if ( pSignature == NULL )
		{
			// Devolvemos la longitud en BYTES
			*pulSignatureLen = m_Signature.size();

			return CKR_OK;
		}

		// Si la firma no cabe en el buffer devolveremos el error correspondiente
		if ( *pulSignatureLen < m_Signature.size() )
		{
			*pulSignatureLen = m_Signature.size();

			throw Pkcs11Exception( CKR_BUFFER_TOO_SMALL );
		}

		// Devolvemos los datos y su tamaño.
		*pulSignatureLen = m_Signature.size();
		memcpy(pSignature, &m_Signature[0], *pulSignatureLen);

		// Proceso de firma finalizado. Inicializamos los miembros correspondientes.
		m_bSignOnGoing = false;
		m_Signature.zeroClear();

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pSigningMechanism )
		{
			delete(m_pSigningMechanism);
			m_pSigningMechanism = NULL;
		}

		// Hemos realizado la firma correctamente, 
		// así que si se trata de un DNIe 3.0 debemos indicar que 
		// las sesiones pasan a ser de SOLO LECTURA. Hará falta PIN para firmar de nuevo
		if( m_pComm->GetDniType()==0x02)
			rv_result = CKR_PIN_EXPIRED ;
	}
	catch (Pkcs11Exception & e)
	{
		// Condiciones de seguridad no satisfechas
		if( ( m_pComm->GetDniType()==0x02) && 
			( e.getErrId() == 0x80106982) )	
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );

		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::SignUpdate( CK_BYTE_PTR pPart,
							CK_ULONG    ulPartLen)
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un preoceso de firma.
		if( !m_bSignOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pSigningMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Realizamos la firma sobre el mecanismo elegido
		byteBuffer bbData( pPart, ulPartLen );

		m_pComm->SignMechUpdate( m_pSigningMechanism, bbData);

		// Proceso de firma continúa activo. 
		m_bSignOnGoing = true;
		m_Signature.zeroClear();
	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::SignFinal( CK_BYTE_PTR    pSignature,
						   CK_ULONG_PTR   pulSignatureLen )
{
	CK_RV	rv_result	= CKR_OK;

	try
	{
		// Comprobamos que se haya inicializado un preoceso de firma.
		if( !m_bSignOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pSigningMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Si no tenemos la firma en memoria, la calculamos.
		if(m_Signature.size()==0)
		{

			// Realizamos la firma sobre el mecanismo elegido
			m_pComm->SignMechFinal( m_pSigningMechanism, m_Signature);

			// Ya deberíamos tener una firma válida en memoria. Si no, devolvemos error
			if(m_Signature.size()==0)
				throw Pkcs11Exception(CKR_DEVICE_ERROR);
		}

		// Si no hemos reservado aún memoria, devolveremos el tamaño necesario (en BYTES)
		if ( pSignature == NULL )
		{
			// Devolvemos la longitud en BYTES
			*pulSignatureLen = m_Signature.size();

			//return CKR_OK;
			return CKR_BUFFER_TOO_SMALL;
		}

		// Si la firma no cabe en el buffer devolveremos el error correspondiente
		if ( *pulSignatureLen < m_Signature.size() )
		{
			*pulSignatureLen = m_Signature.size();

			throw Pkcs11Exception( CKR_BUFFER_TOO_SMALL );
		}

		// Devolvemos los datos y su tamaño.
		*pulSignatureLen = m_Signature.size();
		memcpy(pSignature, &m_Signature[0], *pulSignatureLen);

		// Proceso de firma finalizado. Inicializamos los miembros correspondientes.
		m_bSignOnGoing = false;
		m_Signature.zeroClear();
		m_SignatureUpdate.zeroClear();

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pSigningMechanism )
		{
			delete(m_pSigningMechanism);
			m_pSigningMechanism = NULL;
		}
	}
		catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

//
// Funciones de verificación de firma
//
CK_RV CSession::VerifyInit(  CK_MECHANISM_PTR  pMechanism,
							 CK_OBJECT_HANDLE  hKey )
{
	CK_RV	rv_result	= CKR_OK;

	try
	{
		// Comprobamos el handle del objeto hKey que nos pasan.
		CPKCS11Object * pPublicKey = m_ObjectList.GetObjectFromHandle(hKey);

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pVerifyingMechanism )
		{
			delete(m_pVerifyingMechanism);
			m_pVerifyingMechanism = NULL;
		}

		// Inicializamos los miembros que controlan el proceso de verificación.
		m_bVerifyOnGoing = false;

		// Creamos el nuevo objeto mecanismo en función del indicado
		m_pVerifyingMechanism = m_Slot.newMechanism(pMechanism->mechanism);

		if(m_pVerifyingMechanism  == NULL)
			throw Pkcs11Exception(CKR_MECHANISM_INVALID);

		// Inicializamos el proceso de verificación
		m_pVerifyingMechanism->VerifyInit(pPublicKey, pMechanism->pParameter, pMechanism->ulParameterLen);

		m_bVerifyOnGoing = true;

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::Verify( CK_BYTE_PTR       pData,
						  CK_ULONG        ulDataLen,
						  CK_BYTE_PTR     pSignature,
						  CK_ULONG		  ulSignatureLen )
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un preoceso de verificación de firma.
		if( !m_bVerifyOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pVerifyingMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Preparamos los datos para realizar la verificación
		byteBuffer bbData( pData, ulDataLen );
		byteBuffer bbsignature( pSignature, ulSignatureLen );
		m_pComm->VerifySignatureMech( m_pVerifyingMechanism, bbData, bbsignature );

		// Proceso de verificación finalizado. Inicializamos los miembros correspondientes.
		m_bVerifyOnGoing = false;

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pVerifyingMechanism )
		{
			delete(m_pVerifyingMechanism);
			m_pVerifyingMechanism = NULL;
		}

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::VerifyUpdate( CK_BYTE_PTR       pPart,
							  CK_ULONG          ulPartLen)
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un preoceso de verificación de firma.
		if( !m_bVerifyOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pVerifyingMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Preparamos los datos para realizar la verificación
		byteBuffer bbData( pPart, ulPartLen );
		m_pComm->VerifySignatureMechUpdate( m_pVerifyingMechanism, bbData );
	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::VerifyFinal( CK_BYTE_PTR    pSignature,
							 CK_ULONG		ulSignatureLen )
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un preoceso de verificación de firma.
		if( !m_bVerifyOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pVerifyingMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Preparamos los datos para realizar la verificación
		byteBuffer bbsignature( pSignature, ulSignatureLen );
		m_pComm->VerifySignatureMechFinal( m_pVerifyingMechanism, bbsignature );

		// Proceso de verificación finalizado. Inicializamos los miembros correspondientes.
		m_bVerifyOnGoing = false;

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pVerifyingMechanism )
		{
			delete(m_pVerifyingMechanism);
			m_pVerifyingMechanism = NULL;
		}

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}


//
// Funciones de descifrado de datos
//
CK_RV CSession::DecryptInit( CK_MECHANISM_PTR  pMechanism,
							 CK_OBJECT_HANDLE  hKey )
{
	CK_RV	rv_result	= CKR_OK;

	try
	{
		// Comprobamos el handle del objeto hKey que nos pasan.
		CPKCS11Object * pPrivateKey = m_ObjectList.GetObjectFromHandle(hKey);

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pDecryptingMechanism )
		{
			delete(m_pDecryptingMechanism);
			m_pDecryptingMechanism = NULL;
		}

		// Inicializamos los miembros que controlan el proceso de descifrado.
		m_bDecryptOnGoing = false;
		m_DecryptedData.zeroClear();

		// Continuaremos con el proceso de inicialización sólo si la clave privada es de Token
		if(!pPrivateKey->GetAttributeBool(CKA_TOKEN))
			throw Pkcs11Exception(CKR_KEY_HANDLE_INVALID);

		// Creamos el nuevo objeto mecanismo en función del indicado
		m_pDecryptingMechanism = m_Slot.newMechanism(pMechanism->mechanism);

		if(m_pDecryptingMechanism  == NULL)
			throw Pkcs11Exception(CKR_MECHANISM_INVALID);

		// Inicializamos el proceso de firma
		m_pDecryptingMechanism->DecryptInit(pPrivateKey, pMechanism->pParameter, pMechanism->ulParameterLen);

		m_bDecryptOnGoing = true;

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

CK_RV CSession::Decrypt(  CK_BYTE_PTR       pEncryptedData,
						  CK_ULONG          ulEncryptedDataLen,
						  CK_BYTE_PTR       pData,
						  CK_ULONG_PTR      pulDataLen )
{
	CK_RV	rv_result = CKR_OK;

	CSlotTransaction	transaction(m_Slot);

	try
	{
		// Comprobamos que se haya inicializado un preoceso de firma.
		if( !m_bDecryptOnGoing )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		if( m_pDecryptingMechanism  == NULL )
			throw Pkcs11Exception(CKR_OPERATION_NOT_INITIALIZED);

		// Si no tenemos el descifrado en memoria, lo calculamos.
		if(m_DecryptedData.size()==0)
		{
			// Realizamos el descifrado sobre el mecanismo elegido
			byteBuffer bbData( pEncryptedData, ulEncryptedDataLen );
			m_pComm->DecryptMech( m_pDecryptingMechanism, bbData, m_DecryptedData);

			// Ya deberíamos tener los datos en claro en memoria. Si no, devolvemos error
			if(m_DecryptedData.size()==0)
				throw Pkcs11Exception(CKR_DEVICE_ERROR);
		}

		// Si no hemos reservado aún memoria, devolveremos el tamaño necesario (en BYTES)
		if ( pData == NULL )
		{
			// Devolvemos la longitud en BYTES
			*pulDataLen = m_DecryptedData.size();

			return CKR_OK;
		}

		// Si el descifrado no cabe en el buffer devolveremos el error correspondiente
		if ( *pulDataLen < m_DecryptedData.size() )
		{
			*pulDataLen = m_DecryptedData.size();
			throw Pkcs11Exception( CKR_BUFFER_TOO_SMALL );
		}

		// Si teníamos tamaño suficiente, devolvemos los datos y su tamaño exacto.
		*pulDataLen = m_DecryptedData.size();
		memcpy(pData, &m_DecryptedData[0], *pulDataLen);

		// Proceso de firma finalizado. Inicializamos los miembros correspondientes.
		m_bDecryptOnGoing = false;
		m_DecryptedData.zeroClear();

		// Eliminamos el mecanismo que tuviésemos en uso
		if( m_pDecryptingMechanism )
		{
			delete(m_pDecryptingMechanism);
			m_pDecryptingMechanism = NULL;
		}
	}
	catch (Pkcs11Exception & e)
	{
		m_bDecryptOnGoing = false;

		rv_result = e.getErrId();
	}

	return rv_result;
}


CK_RV CSession::GenerateRandom( CK_BYTE_PTR	pRandomData,
								CK_ULONG    ulRandomLen)
{
	CK_RV rv_Result = CKR_OK;

	CSlotTransaction	transaction(m_Slot);

	rv_Result = m_pComm->GetChallenge(pRandomData, ulRandomLen);

	return rv_Result;
}


CK_RV CSession::GenerateKeyPair(  CK_MECHANISM_PTR     pMechanism,
								  CK_ATTRIBUTE_PTR     pPublicKeyTemplate,
								  CK_ULONG             ulPublicKeyAttributeCount,
								  CK_ATTRIBUTE_PTR     pPrivateKeyTemplate,
								  CK_ULONG             ulPrivateKeyAttributeCount,
								  CK_OBJECT_HANDLE_PTR phPublicKey,
								  CK_OBJECT_HANDLE_PTR phPrivateKey)
{
	CK_RV rv_result	= CKR_OK;

	unsigned short usFileLen = 0;

	CK_ULONG ulUsoClave = 0;

	CSlotTransaction	transaction(m_Slot);

	// Comprobaciones de las plantillas de creación
	if ((pPublicKeyTemplate == NULL) || (ulPublicKeyAttributeCount == 0))
		throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );

	if ((pPrivateKeyTemplate == NULL) || (ulPrivateKeyAttributeCount == 0))
		throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );

	try
	{
		// Comprobamos que el mecanismo sea CKM_RSA_PKCS_KEY_PAIR_GEN
		if ( (pMechanism->mechanism) != CKM_RSA_PKCS_KEY_PAIR_GEN)
			throw (CK_RV)CKR_MECHANISM_INVALID;

		// Tipo de template que nos han pasado para la clave pública
		CK_BBOOL isToken_PubKey   = TemplateGetBoolean(CKA_TOKEN, pPublicKeyTemplate, ulPublicKeyAttributeCount);
		CK_BBOOL isPrivate_PubKey = TemplateGetBoolean(CKA_PRIVATE, pPublicKeyTemplate, ulPublicKeyAttributeCount);

		// Tipo de template que nos han pasado para la clave privada
		CK_BBOOL isToken_PrivKey   = TemplateGetBoolean(CKA_TOKEN, pPrivateKeyTemplate, ulPrivateKeyAttributeCount);
		CK_BBOOL isPrivate_PrivKey = TemplateGetBoolean(CKA_PRIVATE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount);

		CK_BBOOL isToken = (isToken_PubKey) || (isToken_PrivKey);
		CK_BBOOL isPrivate = (isPrivate_PubKey) || (isPrivate_PrivKey);

		if ((isToken) && (isPrivate))
		{
			// PRIVATE TOKEN OBJECT
			// Unicamente puede crear el objeto un usuario autenticado
			if (m_state == CKS_RO_USER_FUNCTIONS)
			{
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
			}
			else
				if (m_state != CKS_RW_USER_FUNCTIONS)
				{
					throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
				}
		}

		else if ((isToken) && (!isPrivate))
		{
			// PUBLIC TOKEN OBJECT
			// Este tipo de objeto puede ser R/W por un usuario autenticado
			if ((m_state == CKS_RO_PUBLIC_SESSION) ||
				(m_state == CKS_RO_USER_FUNCTIONS))
			{
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
			}
		}

		else if ((!isToken) && (isPrivate))
		{
			// PRIVATE SESSION OBJECT
			// El usuario debe estar autenticado.
			if ((m_state != CKS_RO_USER_FUNCTIONS) &&
				(m_state != CKS_RW_USER_FUNCTIONS))
			{
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
			}
		}
		// else
		//    PUBLIC SESSION OBJECT
		//    Es independiente del estado en que se encuentre.

		// Comprobamos que las plantillas sean válidas para las claves RSA
		CPKCS11PrivateKeyObject *pPrivateKey = new(CPKCS11PrivateKeyObject);
		pPrivateKey->CreateObject( pPrivateKeyTemplate, ulPrivateKeyAttributeCount );
		if( !pPrivateKey->ValidAttributes(pPrivateKeyTemplate, ulPrivateKeyAttributeCount) )
			throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );

		CPKCS11PublicKeyObject *pPublicKey = new(CPKCS11PublicKeyObject);
		pPublicKey->CreateObject( pPublicKeyTemplate, ulPublicKeyAttributeCount );
		if( !pPublicKey->ValidAttributes(pPublicKeyTemplate, ulPublicKeyAttributeCount) )
			throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );

		/*// Son plantillas válidas así que construiremos los objetos PKCS#11
		pPrivateKey->CreateObject( pPrivateKeyTemplate, ulPrivateKeyAttributeCount );
		pPublicKey->CreateObject( pPublicKeyTemplate, ulPublicKeyAttributeCount );*/

		// Indicaremos que las claves se han generado internamente (CKA_LOCAL = TRUE)
		pPrivateKey->SetAttribute(CKA_LOCAL, true);
		pPublicKey->SetAttribute(CKA_LOCAL, true);

		// La clave a crear debe ser de firma o de intercambio
		if ( pPrivateKey->GetAttributeBool(CKA_SIGN) )
			ulUsoClave =  ulUsoClave | SC_FL_SIGNATURE_KEY;

		if ( pPrivateKey->GetAttributeBool(CKA_DECRYPT) )
			ulUsoClave = ulUsoClave | SC_FL_EXCHANGE_KEY;

		if(ulUsoClave == 0)
			throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );

		// Requisitos de claves privadas:
		//		1.- El objeto de clave privada debe ser de token
		//		3.- Unicamente se generan claves RSA (si existe CKA_TOKEN, debe ser CKK_RSA)
		if ((!(pPrivateKey->GetAttributeBool(CKA_TOKEN))) ||
			( pPrivateKey->GetAttribute(CKA_KEY_TYPE) &&
			  (pPrivateKey->GetAttributeUlong(CKA_KEY_TYPE) != CKK_RSA)) )
		{
			throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );
		}
		// Lo asignamos, por si no existia
		pPrivateKey->SetAttribute(CKA_KEY_TYPE,(CK_ULONG)CKK_RSA);

		// Requisitos de claves públicas:
		//		1.- Unicamente se generan claves RSA (si existe CKA_TOKEN, debe ser CKK_RSA)
		if ( pPublicKey->GetAttribute(CKA_KEY_TYPE) &&
			 (pPublicKey->GetAttributeUlong(CKA_KEY_TYPE) != CKK_RSA) )
		{
			throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );
		}
		// Lo asignamos, por si no existia
		pPublicKey->SetAttribute(CKA_KEY_TYPE,(CK_ULONG)CKK_RSA);

		// Obtenemos la longitud de la clave, que debe ser de 1024 ó 2048
		CK_ULONG ulKeyLen = pPublicKey->GetAttributeUlong(CKA_MODULUS_BITS);
		if ((ulKeyLen != 2048)&&(ulKeyLen != 1024))
			throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );

		// Sacamos el exponente público que vamos a utilizar
		byteBuffer *publicExponent = NULL;
		publicExponent = pPublicKey->GetAttribute( CKA_PUBLIC_EXPONENT );
		if(publicExponent == NULL)
			throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );

		// Buscamos un KeyID libre para hacer la generación
		CK_BYTE keyID = GetFreeKeyRef();

		if(keyID == 0)
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		// Borramos el fichero de la clave y escribiremos la pública
		rv_result = m_pComm->DeleteKey( ulUsoClave, keyID);		
		if(rv_result != CKR_OK)
			throw Pkcs11Exception( rv_result );

		// Intentamos generar la clave con ese identificador
		rv_result = m_pComm->GenerateKey(ulUsoClave, ulKeyLen, *publicExponent, keyID);
		if(rv_result != CKR_OK)
			throw Pkcs11Exception( rv_result );

		// Si hemos podido crear la clave, asignamos los valores PKCS#15 necesarios
		byteBuffer bbPath;
		
		// Datos PKCS#15 de la clave PRIVADA
		usFileLen = m_pComm->SelectRSAFileGetSize( ulUsoClave|SC_FL_PRIVATE_KEY, keyID, bbPath );
		pPrivateKey->SetKeyReference(keyID);
		pPrivateKey->SetKeyLen(ulKeyLen);
		pPrivateKey->SetValuePath(&bbPath[0], bbPath.size(), 0, usFileLen);

		// Datos PKCS#15 de la clave PÚBLICA
		usFileLen = m_pComm->SelectRSAFileGetSize( ulUsoClave|SC_FL_PUBLIC_KEY, keyID, bbPath );
		pPublicKey->SetKeyReference(keyID);
		pPublicKey->SetKeyLen(ulKeyLen);
		pPublicKey->SetValuePath(&bbPath[0], bbPath.size(), 0, usFileLen);

		// Creamos las referencias a los DFs de las claves
		CP15File *pFilePrivKey	=  &m_PrKDF;
		CP15File *pFilePubKey	=  &m_PuKDF;

		// Añadimos el objeto de clave PRIVADA a la lista de objetos, a la lista global y a su DF
		pFilePrivKey->m_P11Objects.push_back( pPrivateKey );
 		m_ObjectList.Insert( pPrivateKey );
		// Indicaremos el tipo de codificación que debemos utilizar para el PATH y el ASN1
		pFilePrivKey->SetPathEncodingType(m_sessionASN1PathEncType);
		pFilePrivKey->SetASN1Type(m_sessionASN1Type);
		
		pFilePrivKey->WriteP15File(m_pComm);

		// Añadimos el objeto de clave PUBLICA a la lista de objetos, a la lista global y a su DF
		pFilePubKey->m_P11Objects.push_back( pPublicKey );
 		m_ObjectList.Insert( pPublicKey );
		// Indicaremos el tipo de codificación que debemos utilizar para el PATH y el ASN1
		pFilePubKey->SetPathEncodingType(m_sessionASN1PathEncType);
		pFilePubKey->SetASN1Type(m_sessionASN1Type);

		pFilePubKey->WriteP15File(m_pComm);

		// Dejamos indicados los handles de los objetos recién creados
		*phPrivateKey = pPrivateKey->GetHandle();
		*phPublicKey  = pPublicKey->GetHandle();

	}
	catch (Pkcs11Exception & e)
	{
		throw Pkcs11Exception( e.getErrId() );
	}

	return rv_result;
}

CK_RV CSession::CreateObject( CK_ATTRIBUTE_PTR  pTemplate,
							  CK_ULONG          ulCount,
							  CK_OBJECT_HANDLE_PTR phObject  )
{
	CK_RV				rv_result = CKR_OK;
	CSlotTransaction	transaction(m_Slot);

	CPKCS11Object *pObject  = NULL;
	CP15File	  *pFile	= NULL;

	if ((pTemplate == NULL) || (ulCount == 0))
		throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );

	try
	{
		CK_BBOOL isToken	= TemplateGetBoolean(CKA_TOKEN, pTemplate, ulCount);
		CK_BBOOL isPrivate	= TemplateGetBoolean(CKA_PRIVATE, pTemplate, ulCount);

		if ((isToken) && (isPrivate))
		{
			// PRIVATE TOKEN OBJECT
			// Unicamente puede crear el objeto un usuario autenticado
			// o tras haber presentado la clave Administrativa.
			if (m_state == CKS_RO_USER_FUNCTIONS)
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
			else if ( (m_state != CKS_RW_USER_FUNCTIONS) &&
					  (m_state != CKS_RW_SO_FUNCTIONS) )
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		else if ((isToken) && (!isPrivate))
		{
			// PUBLIC TOKEN OBJECT
			// Este tipo de objeto puede ser R/W por un usuario autenticado
			if ((m_state == CKS_RO_PUBLIC_SESSION) ||
				(m_state == CKS_RO_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
		}
		else if ((!isToken) && (isPrivate))
		{
			// PRIVATE SESSION OBJECT
			// El usuario debe estar autenticado.
			if ((m_state != CKS_RO_USER_FUNCTIONS) &&
				(m_state != CKS_RW_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		// else
		//    PUBLIC SESSION OBJECT
		//    Es independiente del estado en que se encuentre.

		// Se obtiene la clase de objeto a crear. Esta funcion lanza
		// una excepcion si el template no contiene el atributo
		// CKA_CLASS.
		CK_OBJECT_CLASS ocClass = TemplateGetClass(pTemplate, ulCount);

		switch(ocClass)
		{
			case CKO_DATA:
				// Se crea un DataObject con los valores por defecto
				pObject = new CPKCS11DataObject();
				pFile = &m_DODF;
				break;

			case CKO_CERTIFICATE:
				// Se crea un CertificateObject con los valores por defecto
				pObject = new CPKCS11CertificateObject();
				pFile = &m_CDF;
				break;

			case CKO_PRIVATE_KEY:
				// No soportamos la creación de claves RSA en memoria
				if(!isToken) 
					throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );

				// Se crea un PrivateKey con los valores por defecto
				pObject = new CPKCS11PrivateKeyObject();
				pFile = &m_PrKDF;
				break;

			case CKO_PUBLIC_KEY:
				// No soportamos la creación de claves RSA en memoria
				if(!isToken)
					throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );

				// Se crea un PublicKey con los valores por defecto
				pObject = new CPKCS11PublicKeyObject();
				pFile = &m_PuKDF;
				break;

			default:
				throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );
		}

		// Comprobamos que los atributos de la plantilla sean coherentes con el tipo de objeto
		if(!pObject->ValidAttributes(pTemplate, ulCount))
		{
			// Los atributos del objeto no eran válidos, así que lo destruímos.
			delete(pObject);
			pObject = NULL;

			throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );
		}

		// Completamos el objeto con los atributos de la plantilla de creación
		pObject->CreateObject(pTemplate, ulCount);

		// Si estamos importando desde pfx, Mozilla extrae mal el label, así que lo modifcaremos
		if( pObject->EsExportacionDesdeExplorer(pTemplate, ulCount) )
		{
			pObject->ModificarEtiquetaExportacion();
		}

		// Si el objeto es de token procederemos a almacenarlo en la tarjeta.
		// Si no, simplemente quedará en memoria
		if( isToken )
		{
			// Comprobamos si hay en EFUS un fichero elemental de características adecuadas,
			// y si no crearemos uno, si es necesario.
			FindOrCreate( pObject, isPrivate );

			// Guardamos los componentes de la clave privada, en su caso.
			if(ocClass == CKO_PRIVATE_KEY)
				CreatePrivateRSAKey(pObject);

			// Guardamos los componentes de la clave pública, en su caso.
			if(ocClass == CKO_PUBLIC_KEY)
				CreatePublicRSAKey(pObject);

			// Añadimos el objeto a la lista de objetos (y a la lista global lo haremos fuera del IF)
			pFile->m_P11Objects.push_back( pObject );		// Lista de objetos del DF
 			//m_ObjectList.Insert(pObject);					// Lista global de objetos

			// Indicaremos el tipo de codificación que debemos utilizar para el ASN1 y PATH
			pFile->SetPathEncodingType(m_sessionASN1PathEncType);
			pFile->SetASN1Type(m_sessionASN1Type);

			// Realizamos la codificación ASN1 del objeto PKCS#11 recién construído, según su tipo,
			// y reescribimos al completo su DF.
			pFile->WriteP15File(m_pComm);
		}

		// En cualquier caso insertamos el objeto en la lista de objetos en memoria
		// independientemente de que además tenga correspondencia en un DF.
		m_ObjectList.Insert(pObject);				// Lista global de objetos

		// Dejamos indicado el handle del objeto
		*phObject = pObject->GetHandle();

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

void CSession::CreatePublicRSAKey( CPKCS11Object * pObject )
{
	unsigned long ulUsoClave = 0;

	byteBuffer *bbComponente = NULL;

	// Identificador de la clave a utilizar
	CK_BYTE		keyID		= 0;
	CK_BYTE		Etiqueta	= 0;
	CK_ULONG	ulCount		= 0;

	// Variables a utilizar
	byteBuffer bbPath(( byte *)"\x3F\x11\x01\x00", 4 );
	unsigned short usFileLen			= 0x00;

	CPKCS11PrivateKeyObject * phObject	= NULL;

	CK_RV rvResult						= CKR_OK;

	CK_ATTRIBUTE_PTR pSearchTemplate	= NULL;
	CK_BBOOL bHayBusqueda				= false;
	CK_BYTE *bkeyID						= NULL;
	CK_BYTE *bkeyMODULUS				= NULL;
	CK_OBJECT_CLASS TipoObjetoPrKey		= CKO_PRIVATE_KEY;

	try
	{
		// Haremos una búsqueda de la clave privada asociada a ella, por si existiera.
		ulCount++;
		INCLUIR_TEMPLATE(pSearchTemplate,
						 CKA_CLASS, &TipoObjetoPrKey,
						 sizeof(CK_OBJECT_CLASS), ulCount);

		// Si existe en el template de búsqueda, añadimos el CKA_ID
		bbComponente = pObject->GetAttribute(CKA_ID);
		if( bbComponente!=NULL )
		{
			bHayBusqueda = true;

			ulCount++;
			INCLUIR_TEMPLATE(pSearchTemplate,
				CKA_ID, &bbComponente->at(0), bbComponente->size(), ulCount);
		}

		// Si existe en el template de búsqueda, añadimos el CKA_ID
		bbComponente = pObject->GetAttribute(CKA_MODULUS);
		if( bbComponente!=NULL )
		{
			bHayBusqueda = true;

			ulCount++;
			INCLUIR_TEMPLATE(pSearchTemplate,
							 CKA_MODULUS, &bbComponente->at(0), bbComponente->size(), ulCount);

			ReadAttributesForSearch( pSearchTemplate, ulCount );
		}

		// La clave a crear debe ser de firma o de intercambio
		if ( pObject->GetAttributeBool(CKA_VERIFY) )
			ulUsoClave =  ulUsoClave | SC_FL_SIGNATURE_KEY;

		if ( pObject->GetAttributeBool(CKA_ENCRYPT) )
			ulUsoClave = ulUsoClave | SC_FL_EXCHANGE_KEY;

		if(ulUsoClave == 0)
		{
			ulUsoClave = SC_FL_SIGNATURE_KEY | SC_FL_EXCHANGE_KEY;
			pObject->SetAttribute(CKA_VERIFY,(bool)CK_TRUE);
			pObject->SetAttribute(CKA_ENCRYPT,(bool)CK_TRUE);
		}

		if(bHayBusqueda)
		{
			m_ObjectList.FindMatchingObjets( pSearchTemplate, ulCount, !IsLogged(), m_FindResult );

			if(m_FindResult.size()!=0)
			{
				// Hemos encontrado clave privada, así que usaremos ese KeyReference
				phObject = (CPKCS11PrivateKeyObject *)m_FindResult[0];
				keyID = phObject->GetKeyReference();
			}
			else
			{
				// Buscamos un KeyID libre 
				if(keyID == 0)
					keyID = GetFreeKeyRef();

				if(keyID == 0)
					throw Pkcs11Exception( CKR_DEVICE_MEMORY );
			}

			// Asignamos el identificador del fichero RSA
			bbPath[3] = keyID;

			// Obtenemos el tamaño del fichero RSA
			if( phObject!= NULL )
			{
				usFileLen = *(unsigned short*)phObject->GetValueLength();
			}
			else
			{
				m_pComm->SelectFileByName( ICC_CRYPTO_NAME );
				//usFileLen = m_pComm->SelectFileGetSize( (bbPath[2]<<8) | (bbPath[3]) );
				usFileLen = m_pComm->SelectRSAFileGetSize( ulUsoClave, keyID , bbPath );
			}
		}

		// Si no hemos encontrado su clave privada asociada, crearemos una nueva
		if( phObject==NULL )
		{
			// Buscamos un KeyID libre 
			if(keyID == 0)
				keyID = GetFreeKeyRef();

			if(keyID == 0)
				throw Pkcs11Exception( CKR_DEVICE_MEMORY );
			
			//
			// Borramos el fichero de la clave y escribiremos la pública
			//
			m_pComm->DeleteKey( ulUsoClave, keyID);

			// Almacenamos el exponente público (debe tener longitud 3)
			Etiqueta = 0x12;
			bbComponente = pObject->GetAttribute(CKA_PUBLIC_EXPONENT);
			rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PUBLIC_KEY,
													 Etiqueta,
													 bbComponente[0],
													 keyID);

			if (rvResult != CKR_OK) 
				throw Pkcs11Exception( rvResult );


			// Almacenamos el módulo
			Etiqueta = 0x14;
			bbComponente = pObject->GetAttribute(CKA_MODULUS);
			rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PUBLIC_KEY,
													 Etiqueta,
													 bbComponente[0],
													 keyID);

			if (rvResult != CKR_OK) 
				throw Pkcs11Exception( rvResult );

			// Asignamos el identificador definitivo del fichero RSA
			bbPath[3] = keyID;
		}

		// Asignamos los valores PKCS#15 necesarios:
		// KeyReference
		((CPKCS11PublicKeyObject*)pObject)->SetKeyReference(keyID);

		// PublicRSAKeyAttributes->modulusLength
		bbComponente = pObject->GetAttribute(CKA_MODULUS_BITS);
		if(bbComponente == NULL)
		{
			// Si no teníamos CKA_MODULUS_BITS lo calculamos a partir del CKA_MODULUS
			bbComponente = pObject->GetAttribute(CKA_MODULUS);
			if(bbComponente!= NULL)
				((CPKCS11PublicKeyObject*)pObject)->SetKeyLen(bbComponente->size()*8);
			else
				((CPKCS11PublicKeyObject*)pObject)->SetKeyLen(0);

			// Asignamos valor al atributo PKCS#11 CKA_MODULUS_BITS
			pObject->SetAttribute(CKA_MODULUS_BITS,((CPKCS11PublicKeyObject*)pObject)->GetKeyLen());
		}
		else
		{
			// Si la plantilla tenía CKA_MODULUS_BITS, lo usamos como tamaño de clave
			((CPKCS11PublicKeyObject*)pObject)->SetKeyLen( pObject->GetAttributeUlong(CKA_MODULUS_BITS) );
		}

		// PublicRSAKeyAttributes->Path.index = 0;
		// PublicRSAKeyAttributes->Path.length
		usFileLen = m_pComm->SelectRSAFileGetSize( ulUsoClave|SC_FL_PUBLIC_KEY, keyID, bbPath );
		((CPKCS11PublicKeyObject*)pObject)->SetValuePath(&bbPath[0], bbPath.size(), 0, usFileLen);

		// Liberamos la memoria reservada
		if(bkeyID)
			delete(bkeyID);
		if(bkeyMODULUS)
			delete(bkeyMODULUS);
		if( pSearchTemplate )
			free(pSearchTemplate);
	}
	catch (Pkcs11Exception & e)
	{
		if(bkeyID)
			delete(bkeyID);
		if(bkeyMODULUS)
			delete(bkeyMODULUS);
		if( pSearchTemplate )
			free(pSearchTemplate);

		throw e;
	}
}


void CSession::CreatePrivateRSAKey( CPKCS11Object	* pObject)
{
	unsigned long ulUsoClave = 0;

	byteBuffer *bbComponente = NULL;

	// Identificador de la clave a utilizar
	CK_BYTE		keyID = 0;
	CK_BYTE		Etiqueta;

	CK_RV rvResult = CKR_OK;

	byteBuffer bbPath(( byte *)"\x3F\x11\x01\x00", 4 );
	unsigned short usFileLen			= 0x00;	

	CK_ATTRIBUTE_PTR pSearchTemplate	= NULL;

	try
	{
		// Haremos una búsqueda de la clave pública asociada a ella, por si existiera.

		CK_ULONG				ulCount = 0;
		CK_BBOOL				bHayBusqueda = false;
		CK_OBJECT_CLASS			TipoObjetoPubKey = CKO_PUBLIC_KEY;
		CPKCS11PublicKeyObject	* pPubKeyObject= NULL;

		ulCount++;
		INCLUIR_TEMPLATE(pSearchTemplate,
						 CKA_CLASS, &TipoObjetoPubKey,
						 sizeof(CK_OBJECT_CLASS), ulCount);

		// Si existe en el template de búsqueda, añadimos el CKA_ID
		bbComponente = pObject->GetAttribute(CKA_ID);
		if( bbComponente!=NULL )
		{
			bHayBusqueda = true;

			ulCount++;
			INCLUIR_TEMPLATE(pSearchTemplate,
				CKA_ID, &bbComponente->at(0), bbComponente->size(), ulCount);
		}

		// Si existe en el template de búsqueda, añadimos el CKA_ID

		bbComponente = pObject->GetAttribute(CKA_MODULUS);
		if( bbComponente!=NULL )
		{
			bHayBusqueda = true;

			ulCount++;
			INCLUIR_TEMPLATE(pSearchTemplate,
							 CKA_MODULUS, &bbComponente->at(0), bbComponente->size(), ulCount);

			ReadAttributesForSearch( pSearchTemplate, ulCount );
		}

		if(bHayBusqueda)
		{
			m_ObjectList.FindMatchingObjets( pSearchTemplate, ulCount, !IsLogged(), m_FindResult );

			if(m_FindResult.size()!=0)
			{
				pPubKeyObject = (CPKCS11PublicKeyObject *)m_FindResult[0];
				keyID = pPubKeyObject->GetKeyReference();
			}
		}

		// La clave a crear debe ser de firma o de intercambio
		if ( pObject->GetAttributeBool(CKA_SIGN) )
			ulUsoClave =  ulUsoClave | SC_FL_SIGNATURE_KEY;

		if ( pObject->GetAttributeBool(CKA_DECRYPT) )
			ulUsoClave = ulUsoClave | SC_FL_EXCHANGE_KEY;

		if(ulUsoClave == 0)
		{
			ulUsoClave = SC_FL_SIGNATURE_KEY | SC_FL_EXCHANGE_KEY;
			pObject->SetAttribute(CKA_SIGN,(bool)CK_TRUE);
			pObject->SetAttribute(CKA_DECRYPT,(bool)CK_TRUE);
		}

		//
		// Realizamos la generación con el DF ICC_CRYPTO seleccionado
		//
		m_pComm->SelectFileByName( ICC_CRYPTO_NAME );

		// Buscamos un KeyID libre 
		if(keyID == 0)
			keyID = GetFreeKeyRef();

		if(keyID == 0)
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		//
		// Borramos el fichero de la clave, para re-escribirlo con privada (incluye parte pública)
		//
		m_pComm->DeleteKey( ulUsoClave, keyID);

		// Almacenamos el primo 1
		Etiqueta = 0x02;
		bbComponente = pObject->GetAttribute(CKA_PRIME_1);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PRIVATE_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el primo 2
		Etiqueta = 0x04;
		bbComponente = pObject->GetAttribute(CKA_PRIME_2);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PRIVATE_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el coeficiente
		Etiqueta = 0x06;
		bbComponente = pObject->GetAttribute(CKA_COEFFICIENT);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PRIVATE_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el exponente 1
		Etiqueta = 0x08;
		bbComponente = pObject->GetAttribute(CKA_EXPONENT_1);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PRIVATE_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el exponente 2
		Etiqueta = 0x0A;
		bbComponente = pObject->GetAttribute(CKA_EXPONENT_2);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PRIVATE_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el exponente público (debe tener longitud 3)
		Etiqueta = 0x12;
		bbComponente = pObject->GetAttribute(CKA_PUBLIC_EXPONENT);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PUBLIC_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		// Almacenamos el módulo
		Etiqueta = 0x14;
		bbComponente = pObject->GetAttribute(CKA_MODULUS);
		rvResult = m_pComm->StoreRSAComponents( ulUsoClave|SC_FL_PUBLIC_KEY,
												 Etiqueta,
												 bbComponente[0],
												 keyID);

		if (rvResult != CKR_OK) 
			throw Pkcs11Exception( rvResult );

		unsigned short usFileLen = 0;
		byteBuffer bbPath(( byte *)"\x3F\x11\x01\x00", 4 );
		bbPath[3] = keyID;

		//usFileLen = m_pComm->SelectFileGetSize( 0x0100 | keyID );
		usFileLen = m_pComm->SelectRSAFileGetSize( ulUsoClave|SC_FL_PRIVATE_KEY, keyID, bbPath );

		((CPKCS11PrivateKeyObject*)pObject)->SetKeyReference(keyID);
		((CPKCS11PrivateKeyObject*)pObject)->SetKeyLen(bbComponente->size()*8);
		((CPKCS11PrivateKeyObject*)pObject)->SetValuePath(&bbPath[0], bbPath.size(), 0, usFileLen);
	}
	catch (Pkcs11Exception & e)
	{
		throw e;
	}
}

CK_RV CSession::DestroyObject( CK_OBJECT_HANDLE  hObject )
{
	CSlotTransaction	transaction(m_Slot);
	CK_RV				rv_result			= CKR_OK;

	CPKCS11Object *		pObject				= NULL;
	CP15File	  *		pFile				= NULL;

	CK_ULONG			ulUsoClave			= 0;

	if (hObject == NULL )
		throw Pkcs11Exception( CKR_OBJECT_HANDLE_INVALID );

	try
	{
		// Buscamos el objeto para ver si tenemos permisos necesarios para la operación
		pObject = m_ObjectList.GetObjectFromHandle(hObject);

		CK_BBOOL isToken	= pObject->GetAttributeBool( CKA_TOKEN );
		CK_BBOOL isPrivate	= pObject->GetAttributeBool( CKA_PRIVATE );

		if ((isToken) && (isPrivate))
		{
			// PRIVATE TOKEN OBJECT
			// Unicamente puede crear el objeto un usuario autenticado
			// o tras haber presentado la clave Administrativa.
			if (m_state == CKS_RO_USER_FUNCTIONS)
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
			else if ( (m_state != CKS_RW_USER_FUNCTIONS) &&
					  (m_state != CKS_RW_SO_FUNCTIONS) )
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		else if ((isToken) && (!isPrivate))
		{
			// PUBLIC TOKEN OBJECT
			// Este tipo de objeto puede ser R/W por un usuario autenticado
			if ((m_state == CKS_RO_PUBLIC_SESSION) ||
				(m_state == CKS_RO_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_SESSION_READ_ONLY );
		}
		else if ((!isToken) && (isPrivate))
		{
			// PRIVATE SESSION OBJECT
			// El usuario debe estar autenticado.
			if ((m_state != CKS_RO_USER_FUNCTIONS) &&
				(m_state != CKS_RW_USER_FUNCTIONS))
				throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );
		}
		// else
		//    PUBLIC SESSION OBJECT
		//    Es independiente del estado en que se encuentre.

		// Se obtiene la clase de objeto a destruir
		CK_ULONG ulObjClass = pObject->GetAttributeUlong( CKA_CLASS );

		switch(ulObjClass)
		{
			case CKO_DATA:
				pFile = &m_DODF;
				break;

			case CKO_CERTIFICATE:
				pFile = &m_CDF;
				break;

			case CKO_PRIVATE_KEY:
				pFile = &m_PrKDF;
				break;

			case CKO_PUBLIC_KEY:
				pFile = &m_PuKDF;
				break;

			default:
				throw Pkcs11Exception( CKR_ATTRIBUTE_TYPE_INVALID );
		}

		// Si el objeto a destruir es de Token tendremos que eliminar su fichero vinculado
		if(isToken)
		{
			// Anotamos en el EFUS su fichero elemental asociado, en su caso
			if( (ulObjClass == CKO_DATA) || (ulObjClass == CKO_CERTIFICATE) )
			{
				// El path en los FE está en formato binario
				byteBuffer *bbBinPath = pObject->GetValuePath();
				//byteBuffer bbASCIIPath = CUtil::PathBin2ASCII( *bbBinPath );
				int* piFileIndex = pObject->GetValueIndex();

				int iFileLen = m_pComm->GetEFSize(*bbBinPath);

				// Indicaremos el tipo de codificación que debemos utilizar para el PATH,
				// que será coherente para toda la sesión: ASCII o BIN.
				m_EFUS.SetPathEncodingType(m_sessionASN1PathEncType);

				m_EFUS.AddFileEFUSlist(m_pComm, bbBinPath, piFileIndex, &iFileLen);
			}

			// Borramos el contenido de la clave, en su caso
			if( (ulObjClass == CKO_PRIVATE_KEY) || (ulObjClass == CKO_PUBLIC_KEY) )
			{
				// La clave a crear debe ser de firma o de intercambio
				if ( pObject->GetAttributeBool((ulObjClass == CKO_PRIVATE_KEY)?CKA_SIGN:CKA_VERIFY) )
					ulUsoClave =  ulUsoClave | SC_FL_SIGNATURE_KEY;

				if ( pObject->GetAttributeBool((ulObjClass == CKO_PRIVATE_KEY)?CKA_DECRYPT:CKA_ENCRYPT) )
					ulUsoClave = ulUsoClave | SC_FL_EXCHANGE_KEY;

				// Por seguridad, si no hay uso asociado a la clave, borramos tanto firma como intercambio.
				if(ulUsoClave==0)
					ulUsoClave = SC_FL_SIGNATURE_KEY | SC_FL_EXCHANGE_KEY;

				m_pComm->SelectFileByName( ICC_CRYPTO_NAME );
				m_pComm->DeleteKey( ulUsoClave, ((CPKCS11KeyObject*)pObject)->GetKeyReference());
			}

			// Como el objeto es de Token, eliminamos  el objeto a la lista de objetos de su tipo
			pFile->DeleteObjectFromList( pObject );

			// Indicaremos el tipo de codificación que debemos utilizar para el PATH y el ASN1
			pFile->SetPathEncodingType(m_sessionASN1PathEncType);
			pFile->SetASN1Type(m_sessionASN1Type);

			// Realizamos la codificación ASN1 del DF tratado y procedemos a reescribirlo.
			pFile->WriteP15File(m_pComm);
		}

		// En cualquier caso, eliminamos el objeto de la lista global.
		m_ObjectList.Destroy( pObject );

	}
	catch (Pkcs11Exception & e)
	{
		rv_result = e.getErrId();
	}

	return rv_result;
}

void CSession::FindOrCreate( CPKCS11Object		* pObject,
							 CK_BBOOL			  isPrivate )
{
	byteBuffer bbPath;
	unsigned int uiSize = 0;

	// Comprobamos si hay en EFUS un fichero de características adecuadas
	byteBuffer * pbbValue = pObject->GetAttribute(CKA_VALUE);
	byteBuffer bbCompressedValue;

	// Si nos han pasado atributo CKA_VALUE buscaremos FE donde albergarlo.
	if(pbbValue != NULL)
	{
		// Comprimimos los datos para conocer el tamaño que necesita realmente en la tarjeta
		m_pComm->CompressData(*pbbValue, bbCompressedValue);

		uiSize = bbCompressedValue.size();

		// Buscamos un fichero válido que se ajuste exactamente a las necesidades requeridas
		// de tipo de fichero y tamaño exacto necesario.
		int index = m_EFUS.FindValidFile( m_pComm, isPrivate?true:false, uiSize, bbPath );

		// No se ha encontrado ningún fichero válido
		if( index == -1 )
		{
			// Si no hay un fichero de esas características tendremos que crearlo.
			index = m_EFUS.GetNewID(m_pComm);

			bbPath.resize(4);

			// Construímos la ruta completa al FE según si el objeto PKCS#11 es público o privado
			if(isPrivate)
				memcpy( &bbPath[0], (byte *)"\x60\x81", 2 );
			else
				memcpy( &bbPath[0], (byte *)"\x60\x61", 2 );

			bbPath[2] = index>>8;
			bbPath[3] = index;

			// Incrementamos un 10% el tamaño del fichero a crear para que no sea tan exacto
			// y nos permita hacer modificaciones sin tener que generar uno nuevo.
			uiSize+= (uiSize/10);

			m_pComm->CreateFileEF( isPrivate?true:false, bbPath, uiSize );

		}
		else
		{
			// Dejamos indicado que la escritura del EFUS deberá ser consecuente con el
			// formato utilizado en el resto de DFs.
			m_EFUS.SetPathEncodingType(m_sessionASN1PathEncType);

			// Si encontramos un fichero válido para reutilizar, debemos reescribir el EFUS
			// eliminando su entrada correspondiente
			m_EFUS.DeleteFileEFUSlist(m_pComm, index);
		}

		// Nos posicionamos en el EF adecuado
		m_pComm->SelectFileByName( MF_NAME );
		m_pComm->SelectFileById( (bbPath[0]<<8) | (bbPath[1]) );
		m_pComm->SelectFileById( (bbPath[2]<<8) | (bbPath[3]) );

		// Escribimos el contenido ya comprimido en el fichero elemental asignado
		m_pComm->WriteBinary(0, bbCompressedValue);

		// El índice de todos los objetos dentro de su EF es 0
		index = 0;

		// Anotamos en el objeto PKCS#11 los datos relativos al Path
		pObject->SetValuePath(bbPath, index, bbCompressedValue.size());

	}

}


// Función que devolverá si en la plantilla que pasamos, el atributo indicado
// está puesto a TRUE o a FALSE.
CK_BBOOL CSession::TemplateGetBoolean(CK_ATTRIBUTE_TYPE attributeType,
									  CK_ATTRIBUTE_PTR pTemplate,
									  CK_ULONG ulCount)
{
	unsigned int i;

	for (i=0; ((i<ulCount-1) && (pTemplate[i].type != attributeType)); i++);
	if ((pTemplate[i].type		 == attributeType) &&
		(pTemplate[i].ulValueLen != 0)				&&
		(pTemplate[i].pValue	 != NULL))
		return (CK_BBOOL)(*(CK_BBOOL *)pTemplate[i].pValue);
	else
		return FALSE;
}


// Devuelve el valor del atributo CKA_CLASS de la plantilla.
CK_OBJECT_CLASS CSession::TemplateGetClass(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
	unsigned int i;

	for (i=0; ((i<ulCount-1) && (pTemplate[i].type != CKA_CLASS)); i++);

	if ((pTemplate[i].type == CKA_CLASS) && (pTemplate[i].pValue != NULL))
		return (CK_OBJECT_CLASS)(*(CK_OBJECT_CLASS *)pTemplate[i].pValue);

	throw Pkcs11Exception( CKR_TEMPLATE_INCOMPLETE );
}

// Función que comprueba que ningún atributo de la plantilla
// tenga valor NULL, ni esté repetido dentro de la misma
CK_RV CSession::Verificar_Atributos( CK_ATTRIBUTE_PTR pTemplate,
									 CK_ULONG ulCount)
{
	// Variable internas de la función
	CK_ULONG i = 0;

	while(i<ulCount)
	{
		if(pTemplate[i].pValue == NULL)
			throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );

		if (i<ulCount-1)
		{
			CK_ULONG j = i;
			j++;

			// Comprobamos además que no demos valor dos veces al mismo atributo
			for(j; ((j<ulCount-1)&&(pTemplate[j].type != pTemplate[i].type)); j++);

			if(pTemplate[j].type == pTemplate[i].type)
				throw Pkcs11Exception( CKR_TEMPLATE_INCONSISTENT );
		}

		i++;

	} // Fin del while

	return (CK_RV)CKR_OK;
}

// Función que carga los datos necesarios en caso de tratar atributos especiales
CK_RV CSession::Cargar_AtributosEspeciales( CPKCS11Object* pObject,
											CK_ATTRIBUTE_PTR pTemplate,
										    CK_ULONG ulCount)
{
	// Variable internas de la función
	CK_ULONG idx			= 0;
	CK_RV	rv_return		= CKR_OK;
	byteBuffer *pAttribute  = NULL;

	CK_ULONG ulObjClass = pObject->GetAttributeUlong( CKA_CLASS );

	while( idx<ulCount )
	{
		pAttribute = pObject->GetAttribute( pTemplate[idx].type );
		if( !pAttribute )
		{
			//
			// Gestión de atributos no incluídos en el P15 (CKA_VALUE, CKA_MODULUS, CKA_PUBLIC_EXPONENT)
			//
			switch ( pTemplate[idx].type )
			{

			case CKA_VALUE:
				if ( ( ulObjClass == CKO_DATA ) || ( ulObjClass == CKO_CERTIFICATE ) )
				{
					ReadValue( pObject );
					pAttribute = pObject->GetAttribute( pTemplate[idx].type );

					// Si no hemos podido obtener el valor lanzaremos una excepción
					if(!pAttribute)
						throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );
				}
				break;

			case CKA_MODULUS:
			case CKA_PUBLIC_EXPONENT:
				if ( ( ulObjClass == CKO_PUBLIC_KEY ) || ( ulObjClass == CKO_PRIVATE_KEY ) )
				{
					ReadRSAPublicComponents( (CPKCS11KeyObject *)pObject );
					pAttribute = pObject->GetAttribute( pTemplate[idx].type );

					// Si no hemos podido obtener el valor lanzaremos una excepción
					if(!pAttribute)
						throw Pkcs11Exception( CKR_ATTRIBUTE_VALUE_INVALID );
				}
				break;
			}
		}

		idx++;

	}// Fin del while

	return rv_return;
}

void CSession::UpdateKeyRefsMap(void)
{
	// Borramos el contenido del mapa de KeyRefs
	m_mapKeyRefs.clear();
	
	//m_PrKDF Objects
	for(unsigned long idx=0; idx<m_PrKDF.m_P11Objects.size(); idx++)
	{
		m_mapKeyRefs[((CPKCS11KeyObject*)m_PrKDF.m_P11Objects[idx])->GetKeyReference()] = 1;
	}

	//m_PuKDF Objects
	for(unsigned long idx=0; idx<m_PuKDF.m_P11Objects.size(); idx++)
	{
		m_mapKeyRefs[((CPKCS11KeyObject*)m_PuKDF.m_P11Objects[idx])->GetKeyReference()] = 1;
	}
}

byte CSession::GetFreeKeyRef(void)
{
	map<byte, byte>::iterator it;

	// Actualizamos la lista de los KeyReferences existentes
	UpdateKeyRefsMap();

	for(byte idx=m_pComm->GetMinKeyId(); idx<=m_pComm->GetMaxKeyId(); idx++)
	{
		it = m_mapKeyRefs.find(idx);
		if( it == m_mapKeyRefs.end() )
			return idx;
	}

	return 0;
}

