#include <winscard.h>
#include "Communicator.h"
#include "Pkcs11Exceptions.h"
#include "Util.h"

#include "zlib.h"

extern SCARDCONTEXT g_hContext;

int numTransmission = 0;

/********************************************************************************************
	Constructor.
********************************************************************************************/
CCommunicator::CCommunicator( CSlot & slot ) : m_slot( slot )
{
	DBG_TRACE(( "Constructor CCommunicator(%s) [0x%08x]\n", slot.GetName().c_str(), this ));

#if defined PCSC_SHARE_EXCLUSIVE
	try
	{
		m_slot.Connect( SCARD_SHARE_EXCLUSIVE );
	}
	catch( Pkcs11Exception & )
	{
		m_slot.Connect( SCARD_SHARE_SHARED );
	}
#else
	m_slot.Connect( SCARD_SHARE_SHARED );
#endif
}


/********************************************************************************************
	Destructor
********************************************************************************************/
CCommunicator::~CCommunicator(void)
{
	DBG_TRACE(( "Destructor ~CCommunicator() [0x%08x]\n", this ));
	m_slot.Disconnect();
}


/********************************************************************************************
	Método que envía un APDU Select File indicando el fichero por ID
	y nos devuelve además el tamaño del fichero seleccionado.

	Parámetros:
		- LPBYTE fileId: ID del fichero.
********************************************************************************************/
unsigned short CCommunicator::SelectFileGetSize( ushort fileId )
{
	byteBuffer	cmd(7);
	byteBuffer	answer;

	unsigned short usFileLen = 0;

	m_lastFCI.clear();

	memcpy( &cmd[0], (byte *)"\x00\xa4\x00\x00\x02", 5 );
	cmd[5] = (fileId >> 8) & 0x00ff;
	cmd[6] = fileId & 0x00ff;

	sendAPDU( cmd, answer, 0 );

	//
	// Si no hay canal, no se ha leido el FCI. Lo hacemos ahora.
	//
	if( (m_SW & 0xff00) == 0x6100 )
		getResponse(answer);

	if( m_SW != 0x9000 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	m_lastFCI = answer;

	if(answer[0] == 0x84)
	{
		usFileLen = (answer[answer[1]+9])*0x100;
		usFileLen += (answer[answer[1]+10]);
	}
	else
	{
		usFileLen = (answer[7])*0x100;
		usFileLen += answer[8];
	}

	return usFileLen;
}


int CCommunicator::GetEFSize( byteBuffer & path )
{
	SelectFileByPath( path );

	if(m_lastFCI.size()<9)
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	return (m_lastFCI[7]<<8) | (m_lastFCI[8]);
}

/********************************************************************************************
	Método selecciona el path y el tamaño del fichero RSA que contiene determinada clave

	Parámetros:
		- byte keyID: identificador de la clave a buscar.
		- byteBuffer path: path en BINARIO del fichero elemental.
********************************************************************************************/
unsigned short CCommunicator::SelectRSAFileGetSize( unsigned long ulUsoClave, byte keyID, byteBuffer & path )
{
	unsigned short usFileLen = 0;

	path.assign(( byte *)"\x3F\x11\x01\x00", 4 );

	path[3] = keyID;

	SelectFileByName( ICC_CRYPTO_NAME );
	usFileLen = SelectFileGetSize( (path[2]<<8) | (path[3]) );

	if(m_lastFCI.size()<9)
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	return usFileLen;
}

/********************************************************************************************
	Método que envía un APDU Select File indicando la ruta al FE

	Parámetros:
		- byteBuffer path: path en BINARIO del fichero elemental.
********************************************************************************************/
void CCommunicator::SelectFileByPath( byteBuffer & path )
{
	// Seleccionamos el FE para conocer el tamaño exacto del fichero y anotarlo en el EFUS
	SelectFileByName( MF_NAME );

	for( ulong idx=0; idx<path.size(); idx+=2 )
	{
		SelectFileById( (path.at(idx)<<8) | path.at(idx+1) );
	}
}

/********************************************************************************************
	Método que envía un APDU Select File indicando el fichero por ID.

	Parámetros:
		- LPBYTE fileId: ID del fichero.
********************************************************************************************/
void CCommunicator::SelectFileById( ushort fileId )
{
	byteBuffer	cmd(7);
	byteBuffer	answer;

	m_lastFCI.clear();

	memcpy( &cmd[0], (byte *)"\x00\xa4\x00\x00\x02", 5 );
	cmd[5] = (fileId >> 8) & 0x00ff;
	cmd[6] = fileId & 0x00ff;

	sendAPDU( cmd, answer, 0 );

	//
	// Si no hay canal, no se ha leido el FCI. Lo hacemos ahora.
	//
	if( (m_SW & 0xff00) == 0x6100 )
		getResponse(answer);

	if( m_SW != 0x9000 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	m_lastFCI = answer;
}


/********************************************************************************************
	Método que envía un APDU Select File indicando el fichero por nombre.

	Parámetros:
		- string sName: Nombre del fichero.
********************************************************************************************/
void CCommunicator::SelectFileByName( string sName )
{
	byteBuffer	cmd( 5 + sName.length() );
	byteBuffer	answer;

	m_lastFCI.clear();

	memcpy( &cmd[0], (byte *)"\x00\xa4\x04\x00", 4);
	cmd[4] = (byte)sName.length();
	memcpy( &cmd[5], sName.c_str(), sName.length() );

	sendAPDU( cmd, answer, 0 );

	//
	// Si no hay canal, no se ha leido el FCI. Lo hacemos ahora.
	//
	if( (m_SW & 0xff00) == 0x6100 )
		getResponse(answer);

	if( m_SW != 0x9000 ){
        answer.clear();
        
		#ifdef _YOSEMITE_
		if(m_SW == 0x6a86) {
            DBG_TRACE(("Retransmisi—n LINE 203 6a86 num retrans 0x%08x", numTransmission));
			
            sendAPDU( cmd, answer, 0 );
            
            if( (m_SW & 0xff00) == 0x6100 )
                getResponse(answer);
            
            if( m_SW != 0x9000 )
                throw Pkcs11Exception( 0x80100000 | m_SW );
		}
        else {
            throw Pkcs11Exception( 0x80100000 | m_SW );
		}
		#else
			throw Pkcs11Exception( 0x80100000 | m_SW );
		#endif
	}
	
    m_lastFCI = answer;
}


/********************************************************************************************
	Método que envía un APDU Read Binary, permitiendo leer hasta 256 bytes de datos.

	Parámetros:
		- unsigned short ushOffset: Offset del primer byte a leer desde el principio del
		  fichero.
		- ulong * pulLength: Puntero a la longitud en bytes de los datos que se quieren
		  leer. En *pulLength se devolverán los datos efectivamente leídos.
		- LPBYTE pbyData: Buffer donde almacenar los datos leídos. Debe tener reservado un
		  espacio de memoria de *pulLength bytes.
********************************************************************************************/
void CCommunicator::ReadBinary( ushort ushOffset, ulong * pulLength, byte * pData )
{
	byteBuffer	cmd((byte *)"\x00\xb0\x00\x00\x00", 5);
	byteBuffer	answer;
	ulong		ulBytesRead = 0;

	while( (ulBytesRead < *pulLength) && (cmd[4] == answer.size()) )
	{
		cmd[2] = (ushOffset >> 8) & 0x00ff;
		cmd[3] = ushOffset & 0x00ff;
		cmd[4] = (byte) min( 250, *pulLength - ulBytesRead );

		sendAPDU( cmd, answer );

		if( !answer.size() )
			break;

		memcpy( pData, &answer[0], answer.size() );
		pData += answer.size();
		ulBytesRead += answer.size();
		ushOffset += (ushort)answer.size();
	}

	*pulLength = ulBytesRead;
}

void CCommunicator::ReadBinary( ushort ushOffset, ulong ulLength, byteBuffer & data )
{
	byteBuffer	cmd((byte *)"\x00\xb0\x00\x00\x00", 5);
	byteBuffer	answer;
	ushort		usCurrentOffset;

	data.clear();

	while( (data.size() < ulLength) && (cmd[4] == answer.size()) )
	{
		usCurrentOffset = ushOffset+(ushort)data.size();

		cmd[2] = (usCurrentOffset >> 8) & 0x00ff;
		cmd[3] = usCurrentOffset & 0x00ff;
		cmd[4] = (byte) min( 250, ulLength - data.size() );

		sendAPDU( cmd, answer );

		if( !answer.size() )
			break;

		data += answer;
	}
}


//
// Funciones para el envío directo de APDUs
//
void CCommunicator::sendAPDUExt( byteBuffer & cmdHeader, byteBuffer & cmdData, byteBuffer & answer,unsigned short expected_SW )
{
	if(cmdData.size() < 250)
	{
		byteBuffer cmd (cmdHeader);
		cmd.push_back((byte)cmdData.size());
		cmd += cmdData;

		sendPlainAPDU( &cmd[0], cmd.size(), answer, expected_SW );

		return;
	}

	// Construímos el comando extendido
	byteBuffer cmdExt (cmdHeader);
	cmdExt.push_back(0x00);
	cmdExt.push_back((byte)(cmdData.size()>>8));
	cmdExt.push_back(cmdData.size()&0xFF);
	cmdExt += cmdData;

	// Enviamos el comando extendido
	sendPlainAPDUExt(cmdExt, answer, expected_SW);

}


// Envía un comando extendido
// En el caso de tarjeta T=0 FNMT se utiliza el comando Envelope propietario
void CCommunicator::sendPlainAPDUExt( byteBuffer & cmdExtendido, byteBuffer & answer,unsigned short expected_SW /*= 0x9000*/ )
{
	// Construímos el primer Envelope
	byteBuffer cmdEnvelope(( byte *)"\x90\xC2\x00\x00\xFF", 5 );
	cmdEnvelope += cmdExtendido.substr(0,250);
	cmdEnvelope[4] = (byte)(cmdEnvelope.size()-5);

	sendPlainAPDU(cmdEnvelope, answer);

	// Construímos el segundo Envelope
	cmdEnvelope.assign( (byte *)"\x90\xC2\x00\x00\xFF", 5 );
	cmdEnvelope += cmdExtendido.substr(250,250);
	cmdEnvelope[4] = (byte)(cmdEnvelope.size()-5);

	sendPlainAPDU(cmdEnvelope, answer, expected_SW);

}

void CCommunicator::sendAPDU( byteBuffer & cmd, byteBuffer & answer, unsigned short expected_SW )
{
	sendAPDU( &cmd[0], cmd.size(), answer, expected_SW );
}

void CCommunicator::sendPlainAPDU( byteBuffer & cmd, byteBuffer & answer, unsigned short expected_SW )
{
	sendPlainAPDU( &cmd[0], cmd.size(), answer, expected_SW );
}

void CCommunicator::sendPlainAPDU( byte * pCmd, ulong cmdLen, byteBuffer & answer, unsigned short expected_SW /*= 0x9000*/ )
{
	// Jesús: dejamos log de comando en claro
	DBG_DUMP_DIRECT(( "FNMTP11 transmitted: ",  pCmd, cmdLen ));
	m_slot.TransmitData( pCmd, cmdLen, answer );
	if (answer.size()>0)
		DBG_DUMP_DIRECT(("FNMTP11 received: ",  &answer[0], answer.size()));

	if( answer.size() < 2 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );
	//
	// Leemos el SW de los últimos 2 bytes
	//
	m_SW = (answer[answer.size()-2]<<8) | answer[answer.size()-1];

	//
	// Eliminamos los 2 bytes
	//
	answer.pop_back();
	answer.pop_back();

	//
	// Comprobamos el SW en función de lo esperado
	//
	if( (expected_SW != 0) &&
		(expected_SW != m_SW) &&
		!( (expected_SW == 0x6100) && ((m_SW & 0xff00) == 0x6100) ) ){
		#ifdef _YOSEMITE_
			if(numTransmission < 2 && m_SW == 0x6a86) {
                DBG_TRACE(("Retransmisi—n LINE 365 6a86 num retrans 0x%08x", numTransmission));
				numTransmission++;
				sendPlainAPDU( pCmd, cmdLen, answer, expected_SW);
			} else {
                numTransmission = 0;
				throw Pkcs11Exception( 0x80100000 | m_SW );
			}
		#else
			throw Pkcs11Exception( 0x80100000 | m_SW );
		#endif
    }else numTransmission = 0;
}

void CCommunicator::readEF_sequence( unsigned short id, byteBuffer & ef, unsigned short startOffset /*= 0*/)
{
	byteBuffer	cmd;
	byteBuffer	answer;

	try
	{
		//
		// Si no tenemos selección por SFI
		//
		if( !(startOffset & 0x8000) )
		{
			//
			// Enviamos el comando de seleccionar EF, si es necesario.
			//
			if( id )
				SelectFileById(id);
		}


		//
		// Leemos los 4 primeros bytes para saber el tamaño de los datos
		//
		ReadBinary( startOffset, 4, answer );

		//
		// Eliminamos el SFI si lo hubiera
		//
		if( startOffset & 0x8000 )
			startOffset &= 0x00ff;


		//
		// Comprobamos que contiene un SEQUENCE de ASN.1
		// y el fichero no está vacío
		//
		if (answer[0] != 0x30)
			throw Pkcs11Exception( CKR_DATA_INVALID );

		//
		// Obtenemos el tamaño de la entrada
		//
		long	dataLen;
		long	tlLen;
		tlLen = CUtil::getTLV( &answer[0], NULL, &dataLen );

		//
		// El tamaño total es el de los datos mas el de el tag+len
		//
		dataLen += tlLen;
		ef = answer;

		for( long offset = startOffset+4; offset<startOffset+dataLen; )
		{
			DWORD	len = min(startOffset+dataLen-offset,250);

			ReadBinary( (ushort)offset, len, answer );

			ef.append( answer );
			offset += len;
		}
	}
	catch ( Pkcs11Exception & e )
	{
		if( (e.getErrId() == 0x80106b00) ||					// Offset fuera del EF
			((e.getErrId() & 0xffffff00 ) == 0x80106c00) )	// No hay datos suficientes en el EF
			throw Pkcs11Exception( CKR_DATA_INVALID );

		throw e;
	}
}


void CCommunicator::readEF_CompressedData( byteBuffer & path, byteBuffer & data )
{
	struct
	{
		int	uncompressed;
		int	compressed;
	}	length;

	if( path.size() & 1 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	SelectFileByName( MF_NAME );

	for( ulong idx=0; idx<path.size(); idx+=2 )
	{
		SelectFileById( (path[idx]<<8) | path[idx+1] );
	}

	ReadBinary( 0, sizeof(length), data );
	memcpy( (byte *)&length, &data[0], sizeof(length) );

	ReadBinary( sizeof(length), (ulong)length.compressed, data );

	// Si las longitudes coinciden es que no había compresión, así que hemos terminado
	if(length.compressed == length.uncompressed)
		return;

	try
	{
		ZlibDecompressor	decom;
		decom.Put(&data[0],data.size() );
		decom.Flush(true);

		data.resize(length.uncompressed);
		decom.Get(&data[0],data.size());
	}
	catch( exception & e )
	{
		DBG_TRACE(( "Zlib exception: %s\n", e.what() ));
		throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

void CCommunicator::CompressData( byteBuffer & data )
{
	CompressData(data, data);
}

void CCommunicator::CompressData( byteBuffer & dataIn, byteBuffer & dataOut )
{
	struct
	{
		int	uncompressed;
		int	compressed;
	}	length;

	try
	{
		ZlibCompressor	comp;

		comp.Put( &dataIn[0],dataIn.size() );
		//comp.Flush(true);
		// Modificación enero-2013 para modificar la cabecera de compresión ZLIB.
		comp.MessageEnd();

		length.uncompressed = dataIn.size();
		length.compressed	= (long)comp.MaxRetrievable();

		dataOut.resize(length.compressed + sizeof(length));
		comp.Get(&dataOut[sizeof(length)],length.compressed);

		memcpy(&dataOut[0], &length.uncompressed, sizeof(length));

	}
	catch( exception & e )
	{
		DBG_TRACE(( "Zlib exception: %s\n", e.what() ));
		throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}



void CCommunicator::getResponse( byteBuffer & response, unsigned short expected_SW /*= 0x9000*/ )
{
	byteBuffer	apdu( (byte *)"\x00\xC0\x00\x00\x00", 5);
	byteBuffer	recv;

	response.clear();

	while( (m_SW & 0xff00) == 0x6100 )
	{
		apdu[4] = m_SW & 0x00ff;
		sendPlainAPDU( apdu, recv, 0 );
		response += recv;
	}

	//
	// Si el último GET RESPONSE no devolvió lo esperado, generamos la excepción
	//
	if( (expected_SW != 0) &&
		(expected_SW != m_SW) &&
		!( (expected_SW == 0x6100) && ((m_SW & 0xff00) == 0x6100) ) )
		throw Pkcs11Exception( 0x80100000 | m_SW );
}


void CCommunicator::VerifyPIN( CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen )
{
	if( ((ulPinLen < 4)&&(ulPinLen!=0))|| ulPinLen > 16 )
		throw Pkcs11Exception( CKR_PIN_LEN_RANGE );

	byteBuffer	apdu( 5+ulPinLen );
	byteBuffer	recv;

	try
	{
		memcpy( &apdu[0], "\x00\x20\x00\x00", 4 );
		apdu[4] = (byte)ulPinLen;
		
		if(ulPinLen!=0)
			memcpy( &apdu[5], pPin, ulPinLen );

		sendAPDU( apdu, recv );

		apdu.zeroClear();
	}
	catch( Pkcs11Exception & e )
	{
		apdu.zeroClear();

		// Si sólo queríamos conocer el número de reintentos, lo propagamos
		if(ulPinLen==0)
			throw e;

		CK_RV	rvResult = e.getErrId();

		if( rvResult == 0x80106983 )
			throw Pkcs11Exception( CKR_PIN_LOCKED );

		if( (rvResult&0xfffffff0) == 0x801063c0 )
			throw Pkcs11Exception( CKR_PIN_INCORRECT );

		throw Pkcs11Exception( CKR_DEVICE_ERROR );
	}
}


void CCommunicator::UnblockPIN( CK_UTF8CHAR_PTR pUnblock, CK_ULONG ulUnbLen,
							    CK_UTF8CHAR_PTR pNewPIN, CK_ULONG ulPinLen)
{
	if( ulUnbLen != 16 )
		throw Pkcs11Exception( CKR_DATA_INVALID );

	// Si queremos cambiar el PIN además de desbloquear, comprobamos u longitud
	if(pNewPIN)
	{
		if( ulPinLen < 4 || ulPinLen > 16 )
			throw Pkcs11Exception( CKR_PIN_LEN_RANGE );
	}

	byteBuffer	apdu(5);
	byteBuffer	recv;
	byteBuffer	aux;

	try
	{
		// Seleccionamos el fichero de códigos CHV 0000
		SelectFileByName( MF_NAME );
		SelectFileById( 0x0000 );

		memcpy( &apdu[0], "\x90\x2C\x00\x00\x00", 5 );
		aux.resize(ulUnbLen);
		memcpy( &aux[0], pUnblock, ulUnbLen );
		apdu.append(aux);

		// Si además de desbloqueo queremos cambiar el PIN...
		if(pNewPIN)
		{
			apdu.append((byte)ulPinLen);
			aux.resize(ulPinLen);
			memcpy( &aux[0], pNewPIN, ulPinLen );
			apdu.append(aux);
		}

		apdu[4] = (byte)(apdu.size()-5);

		sendAPDU( apdu, recv );

		apdu.zeroClear();
	}
	catch( Pkcs11Exception & e )
	{
		apdu.zeroClear();

		// Al tratarse de un método interno, simplemente propagamos el error 
		// para que lo gestione la capa superior.
		throw e;

		/*CK_RV	rvResult = e.getErrId();
		if( (rvResult&0xfffffff0) == 0x801063c0 )
			throw Pkcs11Exception( CKR_PIN_INCORRECT );

		if( (rvResult&0x0000ffff) == 0x00006a80 )
			throw Pkcs11Exception( CKR_DATA_INVALID );

		if( rvResult == 0x80106983 )
			throw Pkcs11Exception( CKR_PIN_LOCKED );

		throw Pkcs11Exception( CKR_DEVICE_ERROR );*/
	}
}

void CCommunicator::ChangePIN( CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen,
							   CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen,
							   byte bCodeId)
{
	if( ulOldLen < 4 || ulOldLen > 16 || ulNewLen < 4 || ulNewLen > 16 )
		throw Pkcs11Exception( CKR_PIN_LEN_RANGE );

	// 90 24 00 00 LE ty L1 'PIN1' L2 'PIN2'
	byteBuffer	apdu(5);
	byteBuffer	recv;
	byteBuffer	aux;

	try
	{
		// Seleccionamos el fichero de códigos CHV 0000
		SelectFileByName( MF_NAME );
		SelectFileById( 0x0000 );

		memcpy( &apdu[0], "\x90\x24\x00\x00\x00", 5 );

		// Tipo de código a cambiar: PIN(0x01) ó PUK(0x00)
		apdu.append(bCodeId);

		// Añadimos tamaño y PIN viejos
		apdu.append((byte)ulOldLen);
		aux.resize(ulOldLen);
		memcpy(&aux[0], pOldPin, ulOldLen);
		apdu.append(aux);

		// Añadimos tamaño y PIN nuevos
		apdu.append((byte)ulNewLen);
		aux.resize(ulNewLen);
		memcpy(&aux[0], pNewPin, ulNewLen);
		apdu.append(aux);

		apdu[4] = (byte)(apdu.size()-5);

		sendAPDU( apdu, recv );

		apdu.zeroClear();
	}
	catch( Pkcs11Exception & e )
	{
		apdu.zeroClear();

		CK_RV	rvResult = e.getErrId();

		if( rvResult == 0x80106983 )
			throw Pkcs11Exception( CKR_PIN_LOCKED );

		if( (rvResult&0xfffffff0) == 0x801063c0 )
			throw Pkcs11Exception( CKR_PIN_INCORRECT );

		throw Pkcs11Exception( CKR_DEVICE_ERROR );
	}
}

ushort CCommunicator::GetPINCounter()
{
	ushort ushPINCounter = 0;
	byteBuffer apdu( 5 );
	byteBuffer recv;

	try
	{
		// Mandamos un Verify con el campo de datos vacío para consultar el estado del contador
		// de intentos del PIN
		memcpy( &apdu[0], "\x00\x20\x00\x00\x00", 5 );

		sendAPDU( apdu, recv );

		apdu.zeroClear();
	}
	catch( Pkcs11Exception & e )
	{
		apdu.zeroClear();

		CK_RV rvResult = e.getErrId();

		// PIN bloqueado
		if( rvResult == 0x80106983 )
			ushPINCounter = 0;

		// La respuesta 0x63CX indica que quedan 'X' intentos disponibles
		else if( ( rvResult & 0xfffffff0 ) == 0x801063c0 )
				ushPINCounter = (ushort) (rvResult & 0x0000000f);
		else if( ( rvResult & 0xffff0000 ) == 0x80100000)
				throw Pkcs11Exception( CKR_DEVICE_ERROR );
		else throw e;
	}

	return ushPINCounter;
}


void CCommunicator::ReadRSAPublicComponents( byte keyReference, byteBuffer & publicExponent, byteBuffer & modulus )
{
	byteBuffer	cmd((byte *)"\x90\x56\x80\x01\x01\x12",6);

	SelectFileByName( ICC_CRYPTO_NAME );

	cmd[3] = keyReference;
	// Solicitamos el exponente público y obtenemos la respuesta con GetResponse
	sendAPDU( cmd, publicExponent, 0 );
	if( (m_SW & 0xff00) == 0x6100 )
		getResponse(publicExponent);

	if( m_SW != 0x9000 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	// Solicitamos el módulo y obtenemos la respuesta con GetResponse
	cmd[5] = 0x14;
	sendAPDU( cmd, modulus, 0);
	if( (m_SW & 0xff00) == 0x6100 )
		getResponse(modulus);

	if( m_SW != 0x9000 )
		throw Pkcs11Exception( CKR_DEVICE_ERROR );
}

void CCommunicator::SignMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature )
{
	// Obtenemos el keyReference de la clave utilizada por el mecanismo
	byte keyReference = pMechanism->GetKeyReference();

	// Modificaremos los datos de entrada en caso de que el mecanismo lo requiera
	pMechanism->ConstructData(data);

	// Seleccionamos la clave RSA con MSE
	byteBuffer	cmd((byte *)"\x00\x22\x41\xB6\x04\x84\x02\x01\x01",9);
	byteBuffer answer;

	cmd[8] = keyReference;
	sendAPDU( cmd, answer );

	// Realizamos la firma con PSO
	cmd.assign( (byte *)"\x00\x2A\x9E\x9A",4 );		// Preparamos la cabecera del comando ...
	cmd.push_back((byte)data.size());				// Añadimos longitud de los datos ...
	cmd += data;									// Añadimos los datos ...

	// Enviamos el comando de firma
	sendAPDU( cmd, signature );						// Enviamos el código extendido ...
}

void CCommunicator::SignMechUpdate(CMech * pMechanism, byteBuffer & data)
{
	CK_RV	rvResult = CKR_OK;

	// Realizamos las operaciones de update en función del mecanismo
	rvResult = pMechanism->SignUpdate(&data[0], data.size());
	
	// Si la operación no se realizó correctamente, lanzamos la excepción
	if(rvResult != CKR_OK)
		throw Pkcs11Exception( rvResult );
}

void CCommunicator::SignMechFinal(CMech * pMechanism, byteBuffer & signature)
{
	// Obtenemos el keyReference de la clave utilizada por el mecanismo
	byte keyReference = pMechanism->GetKeyReference();

	// Modificaremos los datos de entrada en caso de que el mecanismo lo requiera
	byteBuffer data;
	pMechanism->ConstructDataFinal(data);

	// Seleccionamos la clave RSA con MSE
	byteBuffer	cmd((byte *)"\x00\x22\x41\xB6\x04\x84\x02\x01\x01",9);
	byteBuffer answer;

	cmd[8] = keyReference;
	sendAPDU( cmd, answer );

	// Realizamos la firma con PSO
	cmd.assign( (byte *)"\x00\x2A\x9E\x9A",4 );		// Preparamos la cabecera del comando ...
	cmd.push_back((byte)data.size());				// Añadimos longitud de los datos ...
	cmd += data;									// Añadimos los datos ...

	// Enviamos el comando de firma
	sendAPDU( cmd, signature );						// Enviamos el código extendido ...
}

void CCommunicator::VerifySignatureMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature )
{

	try
	{
		// Obtenemos el keyReference de la clave utilizada por el mecanismo
		byte keyReference = pMechanism->GetKeyReference();

		// Preparamos los datos en función del mecanismo
		pMechanism->ConstructData(data);

		//
		// Primer paso: cargar los datos con el comando LOAD DATA
		//
		byteBuffer	cmd((byte *)"\x90\x58\x00\x00",4);
		byteBuffer answer;

		pMechanism->AddPadding(data, signature.size());		// Hacemos el padding ...
		sendAPDUExt(cmd, data, answer);						// Enviamos el comando ...

		//
		// Segundo paso: verificar la firma con el comando VERIFY SIGNATURE
		//
		cmd.zeroClear();
		answer.zeroClear();
		cmd.assign( (byte *)"\x90\x5E\x80\x00",4 );			// Preparamos la cabecera del comando ...
		cmd[3] = keyReference;

		sendAPDUExt(cmd, signature, answer);
	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106300)
			throw Pkcs11Exception( CKR_SIGNATURE_INVALID );
		else
			throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

void CCommunicator::VerifySignatureMechUpdate ( CMech * pMechanism, byteBuffer & data )
{
	CK_RV	rvResult = CKR_OK;

	// Realizamos las operaciones de update en función del mecanismo
	rvResult = pMechanism->VerifyUpdate(&data[0], data.size());
	
	// Si la operación no se realizó correctamente, lanzamos la excepción
	if(rvResult != CKR_OK)
		throw Pkcs11Exception( rvResult );
}

void CCommunicator::VerifySignatureMechFinal ( CMech * pMechanism, byteBuffer & signature )
{
	try
	{
		// Obtenemos el keyReference de la clave utilizada por el mecanismo
		byte keyReference = pMechanism->GetKeyReference();
		byteBuffer data;

		// Preparamos los datos en función del mecanismo
		pMechanism->ConstructDataFinal(data);

		//
		// Primer paso: cargar los datos con el comando LOAD DATA
		//
		byteBuffer	cmd((byte *)"\x90\x58\x00\x00",4);
		byteBuffer answer;

		pMechanism->AddPadding(data, signature.size());		// Hacemos el padding ...
		sendAPDUExt(cmd, data, answer);						// Enviamos el comando ...

		//
		// Segundo paso: verificar la firma con el comando VERIFY SIGNATURE
		//
		cmd.zeroClear();
		answer.zeroClear();
		cmd.assign( (byte *)"\x90\x5E\x80\x00",4 );			// Preparamos la cabecera del comando ...
		cmd[3] = keyReference;

		sendAPDUExt(cmd, signature, answer);
	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106300)
			throw Pkcs11Exception( CKR_SIGNATURE_INVALID );
		else
			throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

void CCommunicator::DecryptMech ( CMech * pMechanism, byteBuffer & encryptedData, byteBuffer & decryptedData )
{
	// Obtenemos el keyReference de la clave utilizada por el mecanismo
	byte keyReference = pMechanism->GetKeyReference();

	// Realizamos la llamada para la firma contra esa clave
	Decrypt( keyReference, encryptedData, decryptedData );
}

void CCommunicator::Decrypt ( byte keyReference, byteBuffer & encryptedData, byteBuffer & decryptedData )
{
	try
	{
		// Construímos el comando de ImportKey
		byteBuffer	cmd((byte *)"\x90\x74\x40\x00",4);
		byteBuffer answer;

		cmd[3] = keyReference;								// Indicamos el identificador de la clave a usar

		sendAPDUExt(cmd, encryptedData, answer, 0);			// Enviamos el comando extendido...

		//
		// Si no hay canal, no se ha leido el FCI. Lo hacemos ahora.
		//
		if( (m_SW & 0xff00) == 0x6100 )
			getResponse(answer);

		if( m_SW != 0x9000 )
			throw Pkcs11Exception( 0x80100000 | m_SW );

		// Las tarjetas anteriores a versión 3.xx devuelven los datos sin quitar el padding,
		// lo hacemos en el driver:
		if( answer.size()== encryptedData.size() )
		{
			CUtil::pkcs1Unpadding( answer, decryptedData );
		}
		else
		{
			// La tarjeta ha quitado ya el relleno, así que lo copiamos
			decryptedData.resize(answer.size());
			memset(	decryptedData, 0, answer.size() );			// Inicializamos el buffer del descifrado...
			memcpy(	decryptedData, &answer[0], answer.size() );	// Copiamos la respuesta.
		}
	}
	catch(Pkcs11Exception & e)
	{
		if( (e.getErrId() == 0x80106A82) ||
			(e.getErrId() == 0x80106A88) )
			throw Pkcs11Exception( CKR_DEVICE_ERROR );
		
		// Controlamos el error de datos, tanto desde PC/SC como de PKCS#11
		if( (e.getErrId() == 0x80106A80)		||
			(e.getErrId() == CKR_DATA_INVALID ) )
			throw Pkcs11Exception( CKR_ENCRYPTED_DATA_INVALID );

		if ( (e.getErrId()& 0xffff0000) == 0x80100000)
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		// Si no hemos reconocido la excepción, la propagamos al siguiente nivel
		throw e;
	}
}

/********************************************************************************************
	Método que envía un APDU Write Binary, permitiendo escribir hasta 256 bytes de datos.

	Parámetros:
		- unsigned short ushOffset: Offset del primer byte a leer desde el principio del
		  fichero.
		- LPBYTE pbData: Buffer donde se incluyen los datos a escribir.
********************************************************************************************/
void CCommunicator::WriteBinary( ushort ushOffset, byte * pData, ulong ulDataLen )
{
	byteBuffer	cmd((byte *)"\x00\xd6\x00\x00", 4);
	byteBuffer	answer;

	cmd.resize(260);

	while( ulDataLen )
	{
		cmd[2] = (ushOffset >> 8) & 0x00ff;
		cmd[3] = ushOffset & 0x00ff;
		cmd[4] = (byte) min( 231, ulDataLen );

		memcpy( &cmd[5], pData, cmd[4] );

		// OJO: DESCOMENTAR ESTA LINEA PARA QUE SE ESCRIBA DE VERDAD EN EL EF!!
		sendAPDU( &cmd[0], cmd[4]+5, answer );
		/*DBG_DUMP((&cmd[0], cmd[4]+5));
		answer.assign((byte*)"\x90\x00",2);

		if( !answer.size() )
			break;*/

		//
		// Eliminamos el SFI si lo hubiera
		//
		if( ushOffset & 0x8000 )
			ushOffset &= 0x00ff;

		pData += cmd[4];
		ulDataLen -= cmd[4];
		ushOffset += cmd[4];
	}

}

void CCommunicator::WriteBinary( ushort ushOffset, byteBuffer & data )
{
	WriteBinary(ushOffset, &data[0], data.size() );
}

void CCommunicator::writeEF_sequence( unsigned short id, byteBuffer & ef, unsigned short startOffset /*= 0*/)
{
	byteBuffer	cmd;
	byteBuffer	answer;

	try
	{
		// Si no tenemos selección por SFI
		//
		if( !(startOffset & 0x8000) )
		{
			//
			// Enviamos el comando de seleccionar EF, si es necesario.
			//
			if( id )
				SelectFileById(id);
		}

		//
		// Comprobamos que contiene un SEQUENCE de ASN.1
		//
		if (ef[0] != 0x30)
			throw Pkcs11Exception( CKR_DATA_INVALID );

		//
		// Obtenemos el tamaño de la entrada
		//
		long	dataLen;
		long	tlLen;
		tlLen = CUtil::getTLV( &ef[0], NULL, &dataLen );

		//
		// El tamaño total es el de los datos mas el de el tag+len
		//
		if ( ef.size() != dataLen+tlLen )
			throw Pkcs11Exception( CKR_DATA_INVALID );

		//
		// Escribimos los datos
		//
		WriteBinary( (ushort)startOffset, ef );

	}
	catch ( Pkcs11Exception & e )
	{
		if( (e.getErrId() == 0x80106b00) ||					// Offset fuera del EF
			((e.getErrId() & 0xffffff00 ) == 0x80106c00) )	// No hay datos suficientes en el EF
			throw Pkcs11Exception( CKR_DATA_INVALID );

		throw e;
	}
}

void CCommunicator::CreateFileEF( bool privado, byteBuffer & path, unsigned int size )
{
	byteBuffer	cmd((byte *)"\x90\xe0\x01\x00\x0A", 5);
	//byteBuffer	data((byte *)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10);
	byteBuffer	answer;

	// Tipo de fichero
	cmd.append(0x01);

	// Identificador
	cmd.append(path.substr(2,2));

	// Tamaño
	cmd.append(size>>8);
	cmd.append(size%0x100);

	// Atributos de seguridad
	if(privado)
	{
		cmd.append(0x11);	// AC1
		cmd.append(0x11);	// AC2
		cmd.append(0x11);	// AC3
		cmd.append(0x11);	// AC4
		cmd.append(0xFF);	// AC5
	}
	else
	{
		cmd.append(0x00);	// AC1
		cmd.append(0x00);	// AC2
		cmd.append(0x11);	// AC3
		cmd.append(0x11);	// AC4
		cmd.append(0xFF);	// AC5
	}

	// Nos posicionamos en el DF adecuado
	SelectFileByName( MF_NAME );
	SelectFileById( (path[0]<<8) | (path[1]) );

	// Enviamos el comando a la tarjeta
	sendAPDU( &cmd[0], cmd.size(), answer );

}

void CComm_Plain::SignMechFinal ( CMech * pMechanism, byteBuffer & signature )
{
	CK_ULONG ulKeyLenBytes = 0;

	try
	{
		// Obtenemos el keyReference de la clave utilizada por el mecanismo
		byte keyReference = pMechanism->GetKeyReference();
		byteBuffer answer;
		byteBuffer data;

		// Preparamos los datos en función del mecanismo
		pMechanism->ConstructDataFinal(data);

		//
		// Primer paso: cargar los datos con el comando LOAD DATA
		//
		byteBuffer	cmd((byte *)"\x90\x58\x00\x00",4);

		ulKeyLenBytes = pMechanism->GetKeyLen();
		// Si la longitud de la clave en el PKCS#15 estaba en bits lo tenemos que convertir a bytes
		if(ulKeyLenBytes>0x100)
			ulKeyLenBytes = ulKeyLenBytes/8;

		pMechanism->AddPadding(data, ulKeyLenBytes);		// Hacemos el padding ...
		sendAPDUExt(cmd, data, answer);						// Enviamos el comando ...

		//
		// Segundo paso: realizar la firma con el comando SIGN DATA
		//
		cmd.zeroClear();
		answer.zeroClear();
		cmd.assign( (byte *)"\x90\x5A\x80\x00\x00",5 );		// Preparamos la cabecera ...
		cmd[3] = keyReference;
		cmd[4] = (byte)ulKeyLenBytes;
		//cmd[4] = (byte)pMechanism->GetKeyLen();

		// Enviamos el comando de firma
		sendPlainAPDU(cmd, signature);						// Enviamos el comando ..
	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106300)
			throw Pkcs11Exception( CKR_SIGNATURE_INVALID );
		else
			throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

void CComm_Plain::SignMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature )
{
	CK_ULONG ulKeyLenBytes = 0;

	try
	{
		// Obtenemos el keyReference de la clave utilizada por el mecanismo
		byte keyReference = pMechanism->GetKeyReference();
		byteBuffer answer;

		// Preparamos los datos en función del mecanismo
		pMechanism->ConstructData(data);

		//
		// Primer paso: cargar los datos con el comando LOAD DATA
		//
		byteBuffer	cmd((byte *)"\x90\x58\x00\x00",4);

		ulKeyLenBytes = pMechanism->GetKeyLen();
		// Si la longitud de la clave en el PKCS#15 estaba en bits lo tenemos que convertir a bytes
		if(ulKeyLenBytes>0x100)
			ulKeyLenBytes = ulKeyLenBytes/8;

		pMechanism->AddPadding(data, ulKeyLenBytes);		// Hacemos el padding ...
		sendAPDUExt(cmd, data, answer);						// Enviamos el comando ...

		//
		// Segundo paso: realizar la firma con el comando SIGN DATA
		//
		cmd.zeroClear();
		answer.zeroClear();
		cmd.assign( (byte *)"\x90\x5A\x80\x00\x00",5 );		// Preparamos la cabecera ...
		cmd[3] = keyReference;
		cmd[4] = (byte)ulKeyLenBytes;
		//cmd[4] = (byte)pMechanism->GetKeyLen();

		// Enviamos el comando de firma
		sendPlainAPDU(cmd, signature);						// Enviamos el comando ..
	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106300)
			throw Pkcs11Exception( CKR_SIGNATURE_INVALID );
		else
			throw Pkcs11Exception( CKR_DATA_INVALID );
	}
}

CK_RV CCommunicator::StoreRSAComponents( CK_ULONG	usoClave,
										CK_BYTE		etiqueta,
										byteBuffer	&componente,
										CK_BYTE		keyID)
{
	byteBuffer cmdStoreKey(( byte *)"\x90\x00\x00\x00", 4 );
	byteBuffer	answer;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		// El comando es distinto si se trata de claves públicas o privadas
		if( usoClave & SC_FL_PUBLIC_KEY)
			cmdStoreKey[1] = 0x50;

		if( usoClave & SC_FL_PRIVATE_KEY )
			cmdStoreKey[1] = 0x52;

		// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
		if( usoClave & SC_FL_EXCHANGE_KEY )
			cmdStoreKey[2] |= 0x40;

		if( usoClave & SC_FL_SIGNATURE_KEY )
			cmdStoreKey[2] |= 0x80;

		cmdStoreKey[3] = keyID;

		byteBuffer cmdData(componente);
		CUtil::buildTLV(etiqueta,cmdData);

		sendAPDUExt(cmdStoreKey,cmdData,answer);

	}
	catch(Pkcs11Exception & e)
	{
		switch( e.getErrId() )
		{
		case 0x80106A80:
			throw Pkcs11Exception( CKR_DATA_INVALID );

		// Si ya existía ese componente devolvemos el error, pero sin lanzar una excepción
		// Error recuperable pasando a otro fichero
		case 0x80106A88:
		case 0x80106985:
			return (CK_RV)CKR_DEVICE_MEMORY;

		// Ya no existen más ficheros de claves RSA disponibles
		case 0x80106A82:
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		// Estado de seguridad no satisfecho -> hace falta PIN?
		case 0x80106982:
			throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );

		default:
			throw e;
		}
	}

	return (CK_RV)CKR_OK;
}


CK_RV CCommunicator::GetChallenge( CK_BYTE_PTR	pRandomData,
								   CK_ULONG		ulRandomLen )
{
	CK_ULONG ulLen =0;

	// Ponemos GetChallenge siempre con longitud 20 por compatibilidad con tarjetas antiguas,
	// que no soportan GetChallenge corto (8 bytes).
	byteBuffer	cmdGetChallenge((byte *)"\x00\x84\x00\x00\x14",5);
	byteBuffer	answer;

	if(!pRandomData)
		throw Pkcs11Exception( CKR_ARGUMENTS_BAD );

	while( ulRandomLen )
	{
		sendAPDU(cmdGetChallenge, answer);

		ulLen = min(ulRandomLen, 20);

		memcpy(pRandomData, &answer[0], ulLen);

		pRandomData+=ulLen;
		ulRandomLen-=ulLen;
	}

	return CKR_OK;
}


CK_RV CCommunicator::GenerateKey (	CK_ULONG	usoClave,
									CK_ULONG	keyLen,
									byteBuffer	&publicExponent,
									CK_BYTE		keyID )
{
	byteBuffer	cmdGenKey((byte *)"\x90\x76\x00\x00\x00",5);

	byteBuffer	cmdRehab((byte *)"\x90\x44\x00\x00\x00",5);

	byteBuffer bbAux;
	byteBuffer answer;

	ushort usFileID = 0x0100;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		// Nos aseguramos de que el fichero RSA no está invalidado
		try
		{
			SelectFileById( usFileID + keyID );
			sendAPDU(cmdRehab, answer);
		}
		catch(Pkcs11Exception & e)
		{
			if(e.getErrId() != 0x80106283)
				throw e;
		}

		cmdGenKey[3] = keyID;

		// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
		if( usoClave & SC_FL_EXCHANGE_KEY )
			cmdGenKey[2] |= 0x40;

		if( usoClave & SC_FL_SIGNATURE_KEY )
			cmdGenKey[2] |= 0x80;

		// Insertamos la longitud de la clave
		cmdGenKey.append(0x80);
		cmdGenKey.append(0x02);
		bbAux.clear();
		bbAux.append((byte)(keyLen>>8));
		bbAux.append((byte)(keyLen%0x100));
		cmdGenKey.append(bbAux);

		/*
		// Insertamos el tipo de clave CRT o NO-CRT
		cmdGenKey.append(0x81);
		cmdGenKey.append(0x01);
		if(ulKeyLen<1088)
			cmdGenKey.append(0x28);	// Clave RSA NO-CRT
		else
			cmdGenKey.append(0x48);	// Clave RSA CRT
		*/

		// Insertamos el exponente público
		cmdGenKey.append(0x82);
		cmdGenKey.append((byte)publicExponent.size());
		cmdGenKey.append(publicExponent);

		// Escribimos en el comando el tamaño de los datos (no contabilizamos la cabecera)
		cmdGenKey[4] = (byte)(cmdGenKey.size() - 5);

		// Enviamos el comando de firma
		sendAPDU(cmdGenKey, answer);					// Enviamos el comando ..

		// La clave se ha generado correctamente, así que rehabilitamos el fichero RSA
		// (puede haber quedado invalidado según la preperso por motivos de certificación CC).
		sendAPDU(cmdRehab, answer, 0);
		if( m_SW != 0x9000 && m_SW != 0x6283 )
			throw Pkcs11Exception(m_SW | 0x80100000);

	}
	catch(Pkcs11Exception & e)
	{
		switch( e.getErrId() )
		{
		case 0x80106A84:		// El tamaño del fichero es pequeño para la clave a generar
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		case 0x80106A82:		// Fichero no encontrado
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		case 0x80106A88:		// Ya existe clave en el fichero, hay que probar con otro fichero
			return( CKR_DEVICE_MEMORY );

		default:				// No podemos propagar errores PC/SC así que lo consideramos CKR_DEVICE_ERROR
			throw Pkcs11Exception( CKR_DEVICE_ERROR );
		}
	}

	return CKR_OK;
}

CK_RV CCommunicator::DeleteKey (	CK_ULONG	usoClave,
									CK_BYTE		keyID )
{
	byteBuffer	cmdDelKey((byte *)"\x90\x54\x00\x00\x00",5);
	byteBuffer	cmdRehab((byte *)"\x90\x44\x00\x00\x00",5);

	byteBuffer answer;

	ushort usFileID = 0x0100;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		// Nos aseguramos de que el fichero RSA no está invalidado
		try
		{
			SelectFileById( usFileID + keyID );
			sendAPDU( cmdRehab, answer );
		}
		catch(Pkcs11Exception & e)
		{
			if(e.getErrId() != 0x80106283)
				throw e;
		}

		cmdDelKey[3] = keyID;

		// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
		if( usoClave & SC_FL_SIGNATURE_KEY )
			cmdDelKey[2] |= 0x80;

		if( usoClave & SC_FL_EXCHANGE_KEY )
			cmdDelKey[2] |= 0x40;

		// Enviamos el comando de borrado de clave
		sendAPDU(cmdDelKey, answer);

	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106982)
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		if(e.getErrId() == 0x80106A84)
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		if(e.getErrId() == 0x80106A82)
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		if(e.getErrId() == 0x80106A88)
			return( CKR_DEVICE_MEMORY );
		else
			throw Pkcs11Exception( e.getErrId() );

	}

	return CKR_OK;
}

CK_RV CComm_Ceres1_0::DeleteKey (	CK_ULONG	usoClave,
									CK_BYTE		keyID )
{
	byteBuffer	cmdDelKey((byte *)"\x90\x54\x00\x00\x00",5);
	byteBuffer answer;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		cmdDelKey[3] = keyID;

		try
		{
			// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
			if( usoClave & SC_FL_SIGNATURE_KEY )
			{
				cmdDelKey[2] = 0x80;

				// Enviamos el comando de borrado de clave
				CCommunicator::sendAPDU(cmdDelKey, answer);
			}
		}
		catch(Pkcs11Exception & e)
		{
			// Permitimos este error en tarjetas Ceres 1.0, que indica que no existe esa clave.
			// En Ceres 2.0, TC-FNMT etc. esa situación devuelve siempre 9000.
			if(e.getErrId() != 0x80106A88)
				throw Pkcs11Exception( e.getErrId() );
		}

		try
		{
			if( usoClave & SC_FL_EXCHANGE_KEY )
			{
				cmdDelKey[2] = 0x40;

				// Enviamos el comando de borrado de clave
				CCommunicator::sendAPDU(cmdDelKey, answer);
			}
		}
		catch(Pkcs11Exception & e)
		{
			// Permitimos este error en tarjetas Ceres 1.0, que indica que no existe esa clave.
			// En Ceres 2.0, TC-FNMT etc. esa situación devuelve siempre 9000.
			if(e.getErrId() != 0x80106A88)
				throw Pkcs11Exception( e.getErrId() );
		}

	}
	catch(Pkcs11Exception & e)
	{
		if(e.getErrId() == 0x80106A84)
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		if(e.getErrId() == 0x80106A82)
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		if(e.getErrId() == 0x80106A88)
			return( CKR_DEVICE_MEMORY );
		else
			throw Pkcs11Exception( e.getErrId() );

	}

	return CKR_OK;
}

CK_RV CComm_Ceres1_0::StoreRSAComponents(	CK_ULONG	usoClave,
											CK_BYTE		etiqueta,
											byteBuffer	&componente,
											CK_BYTE		keyID)
{
	byteBuffer cmdStoreKey(( byte *)"\x90\x00\x00\x00", 4 );
	byteBuffer	answer;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		// El comando es distinto si se trata de claves públicas o privadas
		if( usoClave & SC_FL_PUBLIC_KEY)
			cmdStoreKey[1] = 0x50;

		if( usoClave & SC_FL_PRIVATE_KEY )
			cmdStoreKey[1] = 0x52;

		// Preparamos los datos para el envío, sabiendo que en Ceres 1.xx 
		// los componentes no pueden ser mayores de 0x80.
		byteBuffer cmdData;
		cmdData.append(etiqueta);
		cmdData.append((byte)componente.size());
		cmdData.append(componente);

		// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
		if( usoClave & SC_FL_EXCHANGE_KEY )
		{
			cmdStoreKey[2] = 0x40;
			cmdStoreKey[3] = keyID;

			sendAPDUExt(cmdStoreKey,cmdData,answer);
		}

		if( usoClave & SC_FL_SIGNATURE_KEY )
		{
			cmdStoreKey[2] = 0x80;
			cmdStoreKey[3] = keyID;

			sendAPDUExt(cmdStoreKey,cmdData,answer);
		}

	}
	catch(Pkcs11Exception & e)
	{
		switch( e.getErrId() )
		{
		case 0x80106A80:
			throw Pkcs11Exception( CKR_DATA_INVALID );

		// Si ya existía ese componente devolvemos el error, pero sin lanzar una excepción
		// Error recuperable pasando a otro fichero
		case 0x80106A88:
		case 0x80106985:
			return (CK_RV)CKR_DEVICE_MEMORY;

		// Ya no existen más ficheros de claves RSA disponibles
		case 0x80106A82:
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		// Estado de seguridad no satisfecho -> hace falta PIN?
		case 0x80106982:
			throw Pkcs11Exception( CKR_USER_NOT_LOGGED_IN );

		default:
			throw e;
		}
	}

	return (CK_RV)CKR_OK;
}

CK_RV CComm_Ceres1_0::GenerateKey (	CK_ULONG	usoClave,
									CK_ULONG	keyLen,
									byteBuffer	&publicExponent,
									CK_BYTE		keyID )
{
	byteBuffer	cmdGenKey((byte *)"\x90\x76\x00\x00\x00",5);

	byteBuffer bbAux;
	byteBuffer answer;

	ushort usFileID = 0x0100;

	try
	{
		SelectFileByName( ICC_CRYPTO_NAME );

		// Indicaremos en el comando el tipo de clave de Firma y/o Intercambio
		if( usoClave & SC_FL_EXCHANGE_KEY )
		{
			cmdGenKey[2] |= 0x40;
		}

		if( usoClave & SC_FL_SIGNATURE_KEY )
		{
			cmdGenKey[2] |= 0x80;
		}

		cmdGenKey[3] = keyID;

		// Insertamos el exponente público
		cmdGenKey.append(publicExponent);

		// Escribimos en el comando el tamaño de los datos (no contabilizamos la cabecera)
		cmdGenKey[4] = (byte)(cmdGenKey.size() - 5);

		// Enviamos el comando de firma
		CCommunicator::sendAPDU(cmdGenKey, answer);					// Enviamos el comando ..
	}
	catch(Pkcs11Exception & e)
	{
		switch( e.getErrId() )
		{
		case 0x80106A84:		// El tamaño del fichero es pequeño para la clave a generar
			throw Pkcs11Exception( CKR_DEVICE_MEMORY );

		case 0x80106A82:		// Fichero no encontrado
			throw Pkcs11Exception( CKR_DEVICE_ERROR );

		case 0x80106A88:		// Ya existe clave en el fichero, hay que probar con otro fichero
			return( CKR_DEVICE_MEMORY );

		default:				// No podemos propagar errores PC/SC así que lo consideramos CKR_DEVICE_ERROR
			throw Pkcs11Exception( CKR_DEVICE_ERROR );
		}
	}

	return CKR_OK;
}

unsigned short CComm_Ceres1_0::SelectRSAFileGetSize( unsigned long ulUsoClave, byte keyID, byteBuffer & path )
{
	unsigned short usFileLen = 0;

	path.assign(( byte *)"\x3F\x11\x3F\x00", 4 );

	// Si es clave RSA PRIVADA le asignaremos a la ruta correspondiente, según uso de clave
	if(ulUsoClave & SC_FL_PRIVATE_KEY)
		path[3] = (ulUsoClave & SC_FL_SIGNATURE_KEY)?0x73:0x77;

	// Si es clave RSA PUBLICA le asignaremos a la ruta correspondiente, según uso de clave
	if(ulUsoClave & SC_FL_PUBLIC_KEY)
		path[3] = (ulUsoClave & SC_FL_SIGNATURE_KEY)?0x74:0x78;

	SelectFileByName( ICC_CRYPTO_NAME );
	usFileLen = SelectFileGetSize( (path[2]<<8) | (path[3]) );

	if(m_lastFCI.size()<9)
		throw Pkcs11Exception( CKR_DEVICE_ERROR );

	return usFileLen;
}
