[Pcsclite-cvs-commit] CVS Drivers/ccid/src

CVS User rousseau ludovic.rousseau@free.fr
Tue, 19 Oct 2004 01:22:06 -0600


Update of /cvsroot/pcsclite/Drivers/ccid/src
In directory haydn:/tmp/cvs-serv9364

Modified Files:
	commands.c commands.h 
Log Message:
Add support of character level communication (CCID_CLASS_CHARACTER).

Thanks to Jeffrey Dai


--- /cvsroot/pcsclite/Drivers/ccid/src/commands.c	2004/09/30 14:00:13	1.32
+++ /cvsroot/pcsclite/Drivers/ccid/src/commands.c	2004/10/19 07:22:06	1.33
@@ -1,6 +1,6 @@
 /*
     commands.c: Commands sent to the card
-    Copyright (C) 2003   Ludovic Rousseau
+    Copyright (C) 2003-2004   Ludovic Rousseau
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 */
 
 /*
- * $Id: commands.c,v 1.32 2004/09/30 14:00:13 rousseau Exp $
+ * $Id: commands.c,v 1.33 2004/10/19 07:22:06 rousseau Exp $
  */
 
 #include <string.h>
@@ -52,6 +52,10 @@
 	unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
 	unsigned char rx_buffer[]);
 
+static RESPONSECODE CmdXfrBlockCHAR_T0(unsigned int reader_index, unsigned int
+	tx_length, unsigned char tx_buffer[], unsigned int *rx_length, unsigned
+	char rx_buffer[]);
+
 static RESPONSECODE CmdXfrBlockTPDU_T1(unsigned int reader_index,
 	unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
 	unsigned char rx_buffer[]);
@@ -418,6 +422,18 @@
 				tx_length, tx_buffer, rx_length, rx_buffer);
 			break;
 
+		case CCID_CLASS_CHARACTER:
+			if (protocol == T_0)
+				return_value = CmdXfrBlockCHAR_T0(reader_index, tx_length,
+					tx_buffer, rx_length, rx_buffer);
+			else
+				if (protocol == T_1)
+					return_value = CmdXfrBlockTPDU_T1(reader_index, tx_length,
+						tx_buffer, rx_length, rx_buffer);
+ 				else
+					return_value = IFD_PROTOCOL_NOT_SUPPORTED;
+			break;
+
 		default:
 			*rx_length = 0;
 			return_value = IFD_COMMUNICATION_ERROR;
@@ -434,7 +450,7 @@
  *
  ****************************************************************************/
 RESPONSECODE CCID_Transmit(unsigned int reader_index, unsigned int tx_length,
-	const unsigned char tx_buffer[], unsigned char bBWI)
+	const unsigned char tx_buffer[], unsigned short rx_length, unsigned char bBWI)
 {
 	unsigned char cmd[10+CMD_BUF_SIZE];	/* CCID + APDU buffer */
 	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
@@ -444,7 +460,8 @@
 	cmd[5] = ccid_descriptor->bCurrentSlotIndex;	/* slot number */
 	cmd[6] = (*ccid_descriptor->pbSeq)++;
 	cmd[7] = bBWI;	/* extend block waiting timeout */
-	cmd[8] = cmd[9] = 0; /* RFU */
+	cmd[8] = rx_length & 0xFF;	/* Expected length */
+	cmd[9] = (rx_length >> 8) & 0xFF;
 	memcpy(cmd+10, tx_buffer, tx_length);
 
 	if (WritePort(reader_index, 10+tx_length, cmd) != STATUS_SUCCESS)
@@ -513,16 +530,336 @@
 
 	DEBUG_COMM2("T=0: %d bytes", tx_length);
 
-	return_value = CCID_Transmit(reader_index, tx_length, tx_buffer, 0);
+	return_value = CCID_Transmit(reader_index, tx_length, tx_buffer, 0, 0);
 	if (return_value != IFD_SUCCESS)
 		return return_value;
-	
+
 	return CCID_Receive(reader_index, rx_length, rx_buffer);
 } /* CmdXfrBlockTPDU_T0 */
 
 
 /*****************************************************************************
  *
+ *					T0CmdParsing
+ *
+ ****************************************************************************/
+static RESPONSECODE T0CmdParsing(unsigned char *cmd, unsigned int cmd_len,
+	unsigned int *exp_len)
+{
+	*exp_len = 0;
+
+	/* Ref: 7816-4 Annex A */
+	switch (cmd_len)
+	{
+		case 4:	/* Case 1 */
+			*exp_len = 2; /* SW1 and SW2 only */
+			break;
+
+		case 5: /* Case 2 */
+			if (cmd[4] != 0)
+				*exp_len = cmd[4] + 2;
+			else
+				*exp_len = 256 + 2;
+			break;
+
+		default: /* Case 3 */
+			if (cmd_len > 5 && cmd_len == (unsigned int)(cmd[4] + 5))
+				*exp_len = 2; /* SW1 and SW2 only */
+			else
+				return IFD_COMMUNICATION_ERROR;	/* situation not supported */
+			break;
+	}
+
+	return IFD_SUCCESS;
+} /* T0CmdParsing */
+
+
+/*****************************************************************************
+ *
+ *					T0ProcACK
+ *
+ ****************************************************************************/
+static RESPONSECODE T0ProcACK(unsigned int reader_index,
+	unsigned char **snd_buf, unsigned int *snd_len,
+	unsigned char **rcv_buf, unsigned int *rcv_len,
+	unsigned char **in_buf, unsigned int *in_len,
+	unsigned int proc_len, int is_rcv)
+{
+	RESPONSECODE return_value;
+	unsigned int remain_len;
+	unsigned char tmp_buf[512];
+	unsigned int ret_len;
+
+	DEBUG_COMM2("Enter, is_rcv = %d", is_rcv);
+
+	if (is_rcv == 1)
+	{	/* Receiving mode */
+		if (*in_len > 0)
+		{	/* There are still available data in our buffer */
+			if (*in_len >= proc_len)
+			{
+				/* We only need to get the data from our buffer */
+				memcpy(*rcv_buf, *in_buf, proc_len);
+				*rcv_buf += proc_len;
+				*in_buf += proc_len;
+				*rcv_len += proc_len;
+				*in_len -= proc_len;
+
+				return IFD_SUCCESS;
+			}
+			else
+			{
+				/* Move all data in the input buffer to the reply buffer */
+				remain_len = proc_len - *in_len;
+				memcpy(*rcv_buf, *in_buf, *in_len);
+				*rcv_buf += *in_len;
+				*in_buf += *in_len;
+				*rcv_len += *in_len;
+				*in_len = 0;
+			}
+		}
+		else
+			/* There is no data in our tmp_buf,
+			 * we have to read all data we needed */
+			remain_len = proc_len;
+
+		/* Read the expected data from the smartcard */
+		if (*in_len != 0)
+		{
+			DEBUG_CRITICAL("*in_len != 0");
+			return IFD_COMMUNICATION_ERROR;
+		}
+
+		memset(tmp_buf, 0, sizeof(tmp_buf));
+
+		ret_len = remain_len;
+		return_value = CCID_Transmit(reader_index, 0, *snd_buf, ret_len, 0);
+		if (return_value != IFD_SUCCESS)
+			return return_value;
+
+		return_value = CCID_Receive(reader_index, &ret_len, tmp_buf);
+		if (return_value != IFD_SUCCESS)
+			return return_value;
+
+		memcpy(*rcv_buf, tmp_buf, remain_len);
+		*rcv_buf += remain_len, *rcv_len += remain_len;
+
+		/* If ret_len != remain_len, our logic is erroneous */
+		if (ret_len != remain_len)
+		{
+			DEBUG_CRITICAL("ret_len != remain_len");
+			return IFD_COMMUNICATION_ERROR;
+		}
+	}
+	else
+	{	/* Sending mode */
+
+		return_value = CCID_Transmit(reader_index, proc_len, *snd_buf, 1, 0);
+		if (return_value != IFD_SUCCESS)
+			return return_value;
+
+		*snd_len -= proc_len;
+		*snd_buf += proc_len;
+	}
+
+	DEBUG_COMM("Exit");
+
+	return IFD_SUCCESS;
+} /* T0ProcACK */
+
+
+/*****************************************************************************
+ *
+ *					T0ProcSW1
+ *
+ ****************************************************************************/
+static RESPONSECODE T0ProcSW1(unsigned int reader_index,
+	unsigned char *rcv_buf, unsigned int *rcv_len,
+	unsigned char *in_buf, unsigned int in_len)
+{
+	RESPONSECODE return_value = IFD_SUCCESS;
+	UCHAR tmp_buf[512];
+	unsigned char *rcv_buf_tmp = rcv_buf;
+	const unsigned int rcv_len_tmp = *rcv_len;
+	unsigned char sw1, sw2;
+
+	/* store the SW1 */
+	sw1 = *rcv_buf = *in_buf;
+	rcv_buf++;
+	in_buf++;
+	in_len--;
+	(*rcv_len)++;
+
+	/* store the SW2 */
+	if (0 == in_len)
+	{
+		return_value = CCID_Transmit(reader_index, 0, rcv_buf, 1, 0);
+		if (return_value != IFD_SUCCESS)
+			return return_value;
+
+		in_len = 1;
+
+		return_value = CCID_Receive(reader_index, &in_len, tmp_buf);
+		if (return_value != IFD_SUCCESS)
+			return return_value;
+
+		in_buf = tmp_buf;
+	}
+	sw2 = *rcv_buf = *in_buf;
+	rcv_buf++;
+	in_buf++;
+	in_len--;
+	(*rcv_len)++;
+
+	if (return_value != IFD_SUCCESS)
+	{
+		rcv_buf_tmp[0] = rcv_buf_tmp[1] = 0;
+		*rcv_len = rcv_len_tmp;
+	}
+
+	DEBUG_COMM3("Exit: SW=%02X %02X", sw1, sw2);
+
+	return return_value;
+} /* T0ProcSW1 */
+
+
+/*****************************************************************************
+ *
+ *					CmdXfrBlockCHAR_T0
+ *
+ ****************************************************************************/
+static RESPONSECODE CmdXfrBlockCHAR_T0(unsigned int reader_index,
+	unsigned int snd_len, unsigned char snd_buf[], unsigned int *rcv_len,
+	unsigned char rcv_buf[])
+{
+	int is_rcv;
+	unsigned char tmp_buf[512];
+	unsigned int exp_len, in_len;
+	unsigned char ins, *in_buf;
+	RESPONSECODE return_value = IFD_SUCCESS;
+
+	DEBUG_COMM2("T=0: %d bytes", snd_len);
+
+	in_buf = tmp_buf;
+	in_len = 0;
+	*rcv_len = 0;
+
+	return_value = T0CmdParsing(snd_buf, snd_len, &exp_len);
+	if (return_value != IFD_SUCCESS)
+	{
+		DEBUG_CRITICAL("T0CmdParsing failed");
+		return IFD_COMMUNICATION_ERROR;
+	}
+
+	if (snd_len == 5 || snd_len == 4)
+		is_rcv = 1;
+	else
+		is_rcv = 0;
+
+	/* Command to send to the smart card (must be 5 bytes, from 7816 p.15) */
+	unsigned char cmd[5];
+	memset(cmd, 0, sizeof(cmd));
+	if (snd_len == 4)
+	{
+		memcpy(cmd, snd_buf, 4);
+		snd_buf += 4;
+		snd_len -= 4;
+	}
+	else
+	{
+		memcpy(cmd, snd_buf, 5);
+		snd_buf += 5;
+		snd_len -= 5;
+	}
+
+	/* Make sure this is a valid command by checking the INS field */
+	ins = cmd[1];
+	if ((ins & 0xF0) == 0x60 ||	/* 7816-3 8.3.2 */
+		(ins & 0xF0) == 0x90)
+	{
+		DEBUG_CRITICAL2("fatal: INS (0x%02X) = 0x6X or 0x9X", ins);
+		return IFD_COMMUNICATION_ERROR;
+	}
+
+	return_value = CCID_Transmit(reader_index, 5, cmd, 1, 0);
+	if (return_value != IFD_SUCCESS)
+		return return_value;
+
+	while (1)
+	{
+		if (in_len == 0)
+		{
+			in_len = 1;
+			return_value = CCID_Receive(reader_index, &in_len, tmp_buf);
+			if (return_value != IFD_SUCCESS)
+			{
+				DEBUG_CRITICAL("CCID_Receive failed");
+				return return_value;
+			}
+			in_buf = tmp_buf;
+		}
+		if (in_len == 0)
+		{
+			/* Suppose we should be able to get data.
+			 * If not, error. Set the time-out error */
+			DEBUG_CRITICAL("error: in_len = 0");
+			return IFD_RESPONSE_TIMEOUT;
+		}
+
+		/* Start to process the procedure bytes */
+		if (*in_buf == 0x60)
+		{
+			in_len = 0;
+			return_value = CCID_Transmit(reader_index, 0, cmd, 1, 0);
+
+			if (return_value != IFD_SUCCESS)
+				return return_value;
+
+			continue;
+		}
+		else if (*in_buf == ins || *in_buf == (ins ^ 0x01))
+		{
+			/* ACK => To transfer all remaining data bytes */
+			in_buf++, in_len--;
+			if (is_rcv)
+				return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
+					&rcv_buf, rcv_len, &in_buf, &in_len, exp_len - *rcv_len, 1);
+			else
+				return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
+					&rcv_buf, rcv_len, &in_buf, &in_len, snd_len, 0);
+
+			if (*rcv_len == exp_len)
+				return return_value;
+
+			continue;
+		}
+		else if (*in_buf == (ins ^ 0xFF) || *in_buf == (ins ^ 0xFE))
+		{
+			/* ACK => To transfer 1 remaining bytes */
+			in_buf++, in_len--;
+			return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
+				&rcv_buf, rcv_len, &in_buf, &in_len, 1, is_rcv);
+
+			if (return_value != IFD_SUCCESS)
+				return return_value;
+
+			continue;
+		}
+		else if ((*in_buf & 0xF0) == 0x60 || (*in_buf & 0xF0) == 0x90)
+			/* SW1 */
+			return T0ProcSW1(reader_index, rcv_buf, rcv_len, in_buf, in_len);
+
+		/* Error, unrecognized situation found */
+		DEBUG_CRITICAL2("Unrecognized Procedure byte (0x%02X) found!", *in_buf);
+		return return_value;
+	}
+
+	return return_value;
+} /* CmdXfrBlockCHAR_T0 */

[25 lines skipped]
--- /cvsroot/pcsclite/Drivers/ccid/src/commands.h	2004/07/28 08:33:18	1.12
+++ /cvsroot/pcsclite/Drivers/ccid/src/commands.h	2004/10/19 07:22:06	1.13
@@ -18,7 +18,7 @@
 */
 
 /*
- * $Id: commands.h,v 1.12 2004/07/28 08:33:18 rousseau Exp $
+ * $Id: commands.h,v 1.13 2004/10/19 07:22:06 rousseau Exp $
  */
 
 #define SIZE_GET_SLOT_STATUS 10
@@ -46,7 +46,7 @@
 	unsigned char rx_buffer[], int protoccol);
 
 RESPONSECODE CCID_Transmit(unsigned int reader_index, unsigned int tx_length,
-	const unsigned char tx_buffer[], unsigned char bBWI);
+	const unsigned char tx_buffer[], unsigned short rx_length, unsigned char bBWI);
 
 RESPONSECODE CCID_Receive(unsigned int reader_index, unsigned int *rx_length,
 	unsigned char rx_buffer[]);
@@ -54,3 +54,5 @@
 RESPONSECODE SetParameters(unsigned int reader_index, char protocol,
 	unsigned int length, unsigned char buffer[]);
 
+int isCharLevel(int reader_index);
+