/*
protocol_t0.c
Handling of ISO 7816 T=0 protocol
This file is part of the Unix driver for Towitoko smartcard readers
Copyright (C) 2000 Carlos Prados <cprados@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "defines.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "protocol_t0.h"
#include "atr.h"
/*
* Not exported constants definition
*/
#define PROTOCOL_T0_MAX_NULLS 200
#define PROTOCOL_T0_DEFAULT_WI 10
#define PROTOCOL_T0_MAX_SHORT_COMMAND 260
#define PROTOCOL_T0_MAX_SHORT_RESPONSE 258
/*
* Not exported functions declaration
*/
static void Protocol_T0_Clear (Protocol_T0 * t0);
static int
Protocol_T0_Case1 (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case2S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case3S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case4S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case2E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case3E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_Case4E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
static int
Protocol_T0_ExchangeTPDU (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp);
/*
* Exproted funtions definition
*/
Protocol_T0 *
Protocol_T0_New (void)
{
Protocol_T0 *t0;
t0 = (Protocol_T0 *) malloc (sizeof (Protocol_T0));
if (t0 != NULL)
Protocol_T0_Clear (t0);
return t0;
}
int
Protocol_T0_Init (Protocol_T0 * t0, ICC_Async * icc, PPS_ProtocolParameters * params)
{
ICC_Async_Timings timings;
BYTE wi;
#ifndef PROTOCOL_T0_USE_DEFAULT_TIMINGS
ATR *atr = ICC_Async_GetAtr (icc);
#endif
/* Set ICC */
t0->icc = icc;
/* Integer value WI = TC2, by default 10 */
#ifndef PROTOCOL_T0_USE_DEFAULT_TIMINGS
if (ATR_GetInterfaceByte (atr, 2, ATR_INTERFACE_BYTE_TC, &(wi)) != ATR_OK)
#endif
wi = PROTOCOL_T0_DEFAULT_WI;
/* WWT = 960 * WI * (Fi / f) * 1000 milliseconds */
t0->wwt = (long unsigned int) (960 * wi * (params->f / ICC_Async_GetClockRate (t0->icc)) * 1000);
/* Set timings */
ICC_Async_GetTimings (t0->icc, &timings);
timings.block_timeout = t0->wwt;
timings.char_timeout = t0->wwt;
ICC_Async_SetTimings (t0->icc, &timings);
#ifdef DEBUG_PROTOCOL
printf ("Protocol: T=0: WWT=%d\n", t0->wwt);
#endif
return PROTOCOL_T0_OK;
}
int
Protocol_T0_Command (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int cmd_case, ret;
cmd_case = APDU_Cmd_Case (cmd);
#ifdef DEBUG_PROTOCOL
if (cmd_case != APDU_MALFORMED)
printf ("Protocol: T=0 Case %d %s\n", (cmd_case & 0x0F),
APDU_CASE_IS_EXTENDED (cmd_case)? "extended": "short");
#endif
if (cmd_case == APDU_CASE_1)
ret = Protocol_T0_Case1 (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_2S)
ret = Protocol_T0_Case2S (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_3S)
ret = Protocol_T0_Case3S (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_4S)
ret = Protocol_T0_Case4S (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_2E)
ret = Protocol_T0_Case2E (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_3E)
ret = Protocol_T0_Case3E (t0, cmd, rsp);
else if (cmd_case == APDU_CASE_4E)
ret = Protocol_T0_Case4E (t0, cmd, rsp);
else
{
#ifdef DEBUG_PROTOCOL
printf ("Protocol: T=0: Invalid APDU\n");
#endif
ret = PROTOCOL_T0_ERROR;
}
return ret;
}
int
Protocol_T0_Close (Protocol_T0 * t0)
{
Protocol_T0_Clear (t0);
return PROTOCOL_T0_OK;
}
void
Protocol_T0_Delete (Protocol_T0 * t0)
{
free (t0);
}
/*
* Not exported functions definition
*/
static int
Protocol_T0_Case1 (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
BYTE buffer[5];
APDU_Cmd *tpdu_cmd;
/* Map command APDU onto TPDU */
memcpy (buffer, APDU_Cmd_Raw (cmd), 4);
buffer[4] = 0x00;
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
return ret;
}
static int
Protocol_T0_Case2S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, cmd, rsp);
return ret;
}
static int
Protocol_T0_Case3S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
APDU_Rsp *tpdu_rsp;
#ifdef PROTOCOL_T0_ISO
BYTE buffer[5];
APDU_Cmd *tpdu_cmd;
#endif
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, cmd, (&tpdu_rsp));
if (ret == PROTOCOL_T0_OK)
{
#ifdef PROTOCOL_T0_ISO
/* Le definitely not accepted */
if (APDU_Rsp_SW1 (tpdu_rsp) == 0x67)
{
/* Map response APDU onto TPDU without change */
(*rsp) = tpdu_rsp;
}
/* Le not accepted, La indicated */
else if (APDU_Rsp_SW1 (tpdu_rsp) == 0x6C)
{
/* Map command APDU onto TPDU */
memcpy (buffer, APDU_Cmd_Raw (cmd), 4);
buffer[4] = APDU_Rsp_SW2 (tpdu_rsp);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Re-issue command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
if (APDU_Rsp_DataLen ((*rsp)) > APDU_Cmd_Le (cmd))
{
/* Map response APDU onto TPDU */
APDU_Rsp_TruncateData ((*rsp), APDU_Cmd_Le (cmd));
}
}
}
/* Command processed, Lx indicated */
else if (APDU_Rsp_SW1 (tpdu_rsp) == 0x61)
{
/* MAP response TPDU onto APDU */
(*rsp) = tpdu_rsp;
/* Prepare Get Response TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
do
{
/* Issue Get Response command TPDU */
buffer[4] = APDU_Rsp_SW2 (tpdu_rsp);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
/* Append response TPDU to APDU */
if (APDU_Rsp_AppendData ((*rsp), tpdu_rsp) != APDU_OK)
ret = PROTOCOL_T0_ERROR;
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
}
}
while ((ret == PROTOCOL_T0_OK) && (APDU_Rsp_SW1 (*rsp) == 0x61));
if (ret == PROTOCOL_T0_OK)
{
if (APDU_Rsp_DataLen ((*rsp)) > APDU_Cmd_Le (cmd))
{
/* Map response APDU onto TPDU */
APDU_Rsp_TruncateData ((*rsp), APDU_Cmd_Le (cmd));
}
}
}
/* Le accepted */
else
{
/* Map response TPDU onto APDU without change */
(*rsp) = tpdu_rsp;
}
#else
(*rsp) = tpdu_rsp;
#endif
}
return ret;
}
static int
Protocol_T0_Case4S (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
BYTE buffer[PROTOCOL_T0_MAX_SHORT_COMMAND];
APDU_Cmd *tpdu_cmd;
APDU_Rsp *tpdu_rsp;
/* Map command APDU onto TPDU */
memcpy (buffer, APDU_Cmd_Raw (cmd), APDU_Cmd_RawLen (cmd) - 1);
tpdu_cmd = APDU_Cmd_New (buffer, APDU_Cmd_RawLen (cmd) - 1);
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
#ifdef PROTOCOL_T0_ISO
/* Command accepted with information added */
if (APDU_Rsp_SW1 (tpdu_rsp) == 0x61)
{
/* Prepare Get Reponse command TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
if (APDU_Rsp_SW2 (tpdu_rsp) == 0x00)
buffer[4] = (BYTE) APDU_Cmd_Le (cmd);
else
buffer[4] = MIN (APDU_Cmd_Le (cmd), APDU_Rsp_SW2 (tpdu_rsp));
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Issue Get Reponse command */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
/* Command not accepted */
else if ((APDU_Rsp_SW1 (tpdu_rsp) & 0xF0) == 0x60)
{
/* Map response TPDU onto APDU without change */
(*rsp) = tpdu_rsp;
}
/* Command accepted */
else
{
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Prepare Get Reponse TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
buffer[4] = (BYTE) APDU_Cmd_Le (cmd);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Issue Get Reponse command TPDU */
ret = Protocol_T0_Case3S (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
#else
(*rsp) = tpdu_rsp;
#endif
}
return ret;
}
static int
Protocol_T0_Case2E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret = PROTOCOL_T0_OK, i;
BYTE buffer[PROTOCOL_T0_MAX_SHORT_COMMAND];
APDU_Cmd *tpdu_cmd;
APDU_Rsp *tpdu_rsp;
if (APDU_Cmd_Lc (cmd) < 256)
{
/* MAP APDU onto command TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = APDU_Cmd_Ins (cmd);
buffer[2] = APDU_Cmd_P1 (cmd);
buffer[3] = APDU_Cmd_P2 (cmd);
buffer[4] = (BYTE) APDU_Cmd_Lc (cmd);
memcpy (buffer + 5, APDU_Cmd_Data (cmd), buffer[4]);
tpdu_cmd = APDU_Cmd_New (buffer, buffer[4] + 5);
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
else
{
/* Prepare envelope TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC2;
buffer[2] = 0x00;
buffer[3] = 0x00;
for (i = 0; i < APDU_Cmd_RawLen (cmd); i += buffer[4])
{
/* Create envelope command TPDU */
buffer[4] = MIN (255, APDU_Cmd_RawLen (cmd) - i);
memcpy (buffer + 5, APDU_Cmd_Raw (cmd) + i, buffer[4]);
tpdu_cmd = APDU_Cmd_New (buffer, buffer[4] + 5);
/* Send envelope command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
/* Card does support envelope command */
if (APDU_Rsp_SW1 (tpdu_rsp) == 0x90)
{
/* This is not the last segment */
if (buffer[4] + i < APDU_Cmd_RawLen (cmd))
{
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
}
else
{
/* Map response TPDU onto APDU */
(*rsp) = tpdu_rsp;
}
}
/* Card does not support envelope command or error */
else
{
/* Map response tpdu onto APDU without change */
(*rsp) = tpdu_rsp;
break;
}
}
else
break;
}
}
return ret;
}
static int
Protocol_T0_Case3E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
BYTE buffer[5];
APDU_Cmd *tpdu_cmd;
APDU_Rsp *tpdu_rsp;
long Lm, Lx;
if (APDU_Cmd_Le (cmd) <= 256)
{
/* Map APDU onto command TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = APDU_Cmd_Ins (cmd);
buffer[2] = APDU_Cmd_P1 (cmd);
buffer[3] = APDU_Cmd_P2 (cmd);
buffer[4] = (BYTE) APDU_Cmd_Le (cmd);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Send command TPDU */
ret = Protocol_T0_Case3S (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
else
{
/* Map APDU onto command TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = APDU_Cmd_Ins (cmd);
buffer[2] = APDU_Cmd_P1 (cmd);
buffer[3] = APDU_Cmd_P2 (cmd);
buffer[4] = 0x00;
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
/* Le definitely not accepted */
if (APDU_Rsp_SW1 (tpdu_rsp) == 0x67)
{
/* Map response APDU onto TPDU without change */
(*rsp) = tpdu_rsp;
}
/* Le not accepted, La indicated */
else if (APDU_Rsp_SW1 (tpdu_rsp) == 0x6C)
{
/* Map command APDU onto TPDU */
memcpy (buffer, APDU_Cmd_Raw (cmd), 4);
buffer[4] = APDU_Rsp_SW2 (tpdu_rsp);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Re-issue command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, rsp);
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
/* Command processed, Lx indicated */
else if (APDU_Rsp_SW1 (tpdu_rsp) == 0x61)
{
/* Map response TPDU onto APDU */
(*rsp) = tpdu_rsp;
Lx =
(APDU_Rsp_SW2 (tpdu_rsp) ==
0x00) ? 256 : APDU_Rsp_SW2 (tpdu_rsp);
Lm = APDU_Cmd_Le (cmd) - APDU_Rsp_DataLen (*rsp);
/* Prepare Get Response TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
while (Lm > 0)
{
buffer[4] = (BYTE) MIN (Lm, Lx);
tpdu_cmd = APDU_Cmd_New (buffer, 5);
/* Issue Get Response command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
if (ret == PROTOCOL_T0_OK)
{
/* Append response TPDU to APDU */
if (APDU_Rsp_AppendData ((*rsp), tpdu_rsp) != APDU_OK)
{
ret = PROTOCOL_T0_ERROR;
APDU_Rsp_Delete (tpdu_rsp);
break;
}
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
}
else
break;
Lm = APDU_Cmd_Le (cmd) - APDU_Rsp_DataLen (*rsp);
}
/* Lm == 0 */
}
/* Le accepted: card has no more than 265 bytes or does not
support Get Response */
else
{
/* Map response TPDU onto APDU without change */
(*rsp) = tpdu_rsp;
}
}
}
return ret;
}
static int
Protocol_T0_Case4E (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
int ret;
BYTE buffer[PROTOCOL_T0_MAX_SHORT_COMMAND];
APDU_Cmd *tpdu_cmd, *gr_cmd;
APDU_Rsp *tpdu_rsp;
long Le;
/* 4E1 */
if (APDU_Cmd_Lc (cmd) < 256)
{
/* Map APDU onto command TPDU */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = APDU_Cmd_Ins (cmd);
buffer[2] = APDU_Cmd_P1 (cmd);
buffer[3] = APDU_Cmd_P2 (cmd);
buffer[4] = (BYTE) APDU_Cmd_Lc (cmd);
memcpy (buffer + 5, APDU_Cmd_Data (cmd), buffer[4]);
tpdu_cmd = APDU_Cmd_New (buffer, buffer[4] + 5);
/* Send command TPDU */
ret = Protocol_T0_ExchangeTPDU (t0, tpdu_cmd, (&tpdu_rsp));
/* Delete command TPDU */
APDU_Cmd_Delete (tpdu_cmd);
}
/* 4E2 */
else
{
ret = Protocol_T0_Case2E (t0, cmd, (&tpdu_rsp));
}
/* 4E1 a) b) and c) */
if (ret == PROTOCOL_T0_OK)
{
if (APDU_Rsp_SW1 (tpdu_rsp) == 0x61)
{
/* Lm == (Le - APDU_Rsp_RawLen (tpdu_rsp)) == 0 */
if (APDU_Rsp_SW2 (tpdu_rsp) != 0x00)
Le = MIN(APDU_Rsp_SW2 (tpdu_rsp), APDU_Cmd_Le(cmd));
else
Le = APDU_Cmd_Le (cmd);
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Prepare extended Get Response APDU command */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
buffer[4] = 0x00; /* B1 = 0x00 */
buffer[5] = (BYTE) (Le >> 8); /* B2 = BL-1 */
buffer[6] = (BYTE) (Le & 0x00FF); /* B3 = BL */
gr_cmd = APDU_Cmd_New (buffer, 7);
/* Issue Case 3E get response command */
ret = Protocol_T0_Case3E (t0, gr_cmd, rsp);
/* Delete Get Response command APDU */
APDU_Cmd_Delete (gr_cmd);
}
else if ((APDU_Rsp_SW1 (tpdu_rsp) & 0xF0) == 0x60)
{
/* Map response TPDU onto APDU without change */
(*rsp) = tpdu_rsp;
}
else
{
/* Delete response TPDU */
APDU_Rsp_Delete (tpdu_rsp);
/* Prepare extended Get Response APDU command */
buffer[0] = APDU_Cmd_Cla (cmd);
buffer[1] = 0xC0;
buffer[2] = 0x00;
buffer[3] = 0x00;
buffer[4] = 0x00; /* B1 = 0x00 */
buffer[5] = (BYTE) (APDU_Cmd_Le (cmd) >> 8); /* B2 = BL-1 */
buffer[6] = (BYTE) (APDU_Cmd_Le (cmd) & 0x00FF); /* B3 = BL */
gr_cmd = APDU_Cmd_New (buffer, 7);
/* Issue Case 3E get response command */
ret = Protocol_T0_Case3E (t0, gr_cmd, rsp);
/* Delete Get Response command APDU */
APDU_Cmd_Delete (gr_cmd);
}
}
return ret;
}
static int
Protocol_T0_ExchangeTPDU (Protocol_T0 * t0, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
BYTE buffer[PROTOCOL_T0_MAX_SHORT_RESPONSE];
BYTE *data;
long Lc, Le, sent, recv;
int ret = PROTOCOL_T0_OK, nulls, cmd_case;
/* Parse APDU */
Lc = APDU_Cmd_Lc (cmd);
Le = APDU_Cmd_Le (cmd);
data = APDU_Cmd_Data (cmd);
cmd_case = APDU_Cmd_Case (cmd);
/* Check case of command */
if ((cmd_case != APDU_CASE_2S) && (cmd_case != APDU_CASE_3S))
return PROTOCOL_T0_ERROR;
/* Initialise transmission */
if (ICC_Async_BeginTransmission (t0->icc) != ICC_ASYNC_OK)
{
(*rsp) = NULL;
return PROTOCOL_T0_ICC_ERROR;
}
/* Send header bytes */
if (ICC_Async_Transmit (t0->icc, 5, APDU_Cmd_Header (cmd)) != ICC_ASYNC_OK)
{
ICC_Async_EndTransmission (t0->icc);
(*rsp) = NULL;
return PROTOCOL_T0_ICC_ERROR;
}
/* Initialise counters */
nulls = 0;
sent = 0;
recv = 0;
/*
* Let's be a bit paranoid with buffer sizes within this loop
* so it doesn't overflow reception and transmission buffers
* if card does not strictly respect the protocol
*/
while (recv < PROTOCOL_T0_MAX_SHORT_RESPONSE)
{
/* Read one procedure byte */
if (ICC_Async_Receive (t0->icc, 1, buffer + recv) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
/* NULL byte received */
if (buffer[recv] == 0x60)
{
nulls++;
/* Maximum number of nulls reached */
if (nulls >= PROTOCOL_T0_MAX_NULLS)
{
ret = PROTOCOL_T0_NULL_ERROR;
break;
}
continue;
}
/* SW1 byte received */
else if ((buffer[recv] & 0xF0) == 0x60 || (buffer[recv] & 0xF0) == 0x90)
{
recv++;
if (recv >= PROTOCOL_T0_MAX_SHORT_RESPONSE)
return PROTOCOL_T0_ERROR;
/* Read SW2 byte */
if (ICC_Async_Receive (t0->icc, 1, buffer + recv) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
recv++;
ret = PROTOCOL_T0_OK;
break;
}
/* ACK byte received */
else if ((buffer[recv] & 0x0E) == (APDU_Cmd_Ins (cmd) & 0x0E))
{
/* Reset null's counter */
nulls = 0;
/* Case 2 command: send data */
if (cmd_case == APDU_CASE_2S)
{
if (sent >= Lc)
return PROTOCOL_T0_ERROR;
if (ICC_Async_Switch (t0->icc) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
/* Send remaining data bytes */
if (ICC_Async_Transmit
(t0->icc, MAX (Lc - sent, 0), data + sent) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
sent = Lc;
continue;
}
/* Case 3 command: receive data */
else
{
if (recv >= PROTOCOL_T0_MAX_SHORT_RESPONSE)
return PROTOCOL_T0_ERROR;
/*
* Le <= PROTOCOL_T0_MAX_SHORT_RESPONSE - 2 for short commands
*/
/* Read remaining data bytes */
if (ICC_Async_Receive
(t0->icc, MAX (Le - recv, 0),
buffer + recv) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
recv = Le;
continue;
}
}
/* ~ACK byte received */
else if ((buffer[recv] & 0x0E) == ((~APDU_Cmd_Ins (cmd)) & 0x0E))
{
/* Reset null's counter */
nulls = 0;
/* Case 2 command: send data */
if (cmd_case == APDU_CASE_2S)
{
if (sent >= Lc)
return PROTOCOL_T0_ERROR;
if (ICC_Async_Switch (t0->icc) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
/* Send next data byte */
if (ICC_Async_Transmit (t0->icc, 1, data + sent) !=
ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
sent++;
continue;
}
/* Case 3 command: receive data */
else
{
if (recv >= PROTOCOL_T0_MAX_SHORT_RESPONSE)
return PROTOCOL_T0_ERROR;
/* Read next data byte */
if (ICC_Async_Receive (t0->icc, 1, buffer + recv) !=
ICC_ASYNC_OK)
{
ret = PROTOCOL_T0_ICC_ERROR;
break;
}
recv++;
continue;
}
}
/* Anything else received */
else
{
ret = PROTOCOL_T0_ERROR;
break;
}
}
if (ICC_Async_Switch (t0->icc) != ICC_ASYNC_OK)
ret = PROTOCOL_T0_ICC_ERROR;
if (ret == PROTOCOL_T0_OK)
(*rsp) = APDU_Rsp_New (buffer, recv);
else
(*rsp) = NULL;
/* End of transmission */
if (ICC_Async_EndTransmission (t0->icc) != ICC_ASYNC_OK)
return PROTOCOL_T0_ICC_ERROR;
return (ret);
}
static void
Protocol_T0_Clear (Protocol_T0 * t0)
{
t0->icc = NULL;
t0->wwt = 0;
}
syntax highlighted by Code2HTML, v. 0.9.1