/*
protocol_t1.c
Handling of ISO 7816 T=1 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_t1.h"
#include "t1_block.h"
/*
* Not exported constants definition
*/
#define PROTOCOL_T1_DEFAULT_IFSC 32
#define PROTOCOL_T1_DEFAULT_IFSD 32
#define PROTOCOL_T1_MAX_IFSC 251 /* Cannot send > 255 buffer */
#define PROTOCOL_T1_DEFAULT_CWI 13
#define PROTOCOL_T1_DEFAULT_BWI 4
#define PROTOCOL_T1_EDC_LRC 0
#define PROTOCOL_T1_EDC_CRC 1
/*
* Not exported functions declaration
*/
static void
Protocol_T1_Clear (Protocol_T1 * t1);
static int
Protocol_T1_SendBlock (Protocol_T1 * t1, T1_Block * block);
static int
Protocol_T1_ReceiveBlock (Protocol_T1 * t1, T1_Block ** block);
static int
Protocol_T1_UpdateBWT (Protocol_T1 * t1, unsigned short bwt);
/*
* Exproted funtions definition
*/
Protocol_T1 *
Protocol_T1_New (void)
{
Protocol_T1 *t1;
t1 = (Protocol_T1 *) malloc (sizeof (Protocol_T1));
if (t1 != NULL)
Protocol_T1_Clear (t1);
return t1;
}
int
Protocol_T1_Init (Protocol_T1 * t1, ICC_Async * icc, PPS_ProtocolParameters * params)
{
ICC_Async_Timings timings;
BYTE ta, tb, tc, cwi, bwi;
unsigned long baudrate;
double work_etu;
ATR *atr;
int i;
/* Set ICC */
t1->icc = icc;
/* Get ATR of the card */
atr = ICC_Async_GetAtr (t1->icc);
/* Set IFSC */
if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TA, &ta) == ATR_NOT_FOUND)
t1->ifsc = PROTOCOL_T1_DEFAULT_IFSC;
else if ((ta != 0x00) && (ta != 0xFF))
t1->ifsc = ta;
else
t1->ifsc = PROTOCOL_T1_DEFAULT_IFSC;
/* Towitoko does not allow IFSC > 251 */
t1->ifsc = MIN (t1->ifsc, PROTOCOL_T1_MAX_IFSC);
/* Set IFSD */
t1->ifsd = PROTOCOL_T1_DEFAULT_IFSD;
#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS
/* Calculate CWI and BWI */
if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TB, &tb) == ATR_NOT_FOUND)
{
#endif
cwi = PROTOCOL_T1_DEFAULT_CWI;
bwi = PROTOCOL_T1_DEFAULT_BWI;
#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS
}
else
{
cwi = tb & 0x0F;
bwi = (tb & 0xF0) >> 4;
}
#endif
/* Work etu = (1000 / baudrate) milliseconds */
ICC_Async_GetBaudrate (t1->icc, &baudrate);
work_etu = 1000 / (double)baudrate;
/* Set CWT = (2^CWI + 11) work etu */
t1->cwt = 1;
for (i = 0; i < cwi ; i++)
t1->cwt *= 2;
t1->cwt = (unsigned short) ((t1->cwt + 11) * work_etu);
/* Set BWT = (2^BWI * 960 + 11) work etu */
t1->bwt = 1;
for (i = 0; i < bwi; i++)
t1->bwt *= 2;
t1->bwt = (unsigned short) ((t1->bwt * 960 + 11) * work_etu);
/* Set BGT = 22 * work etu */
t1->bgt = (unsigned short) (22 * work_etu);
/* Set the error detection code type */
if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TC, &tc) == ATR_NOT_FOUND)
t1->edc = PROTOCOL_T1_EDC_LRC;
else
t1->edc = tc & 0x01;
/* Set initial send sequence (NS) */
t1->ns = 1;
/* Set timings */
ICC_Async_GetTimings (t1->icc, &timings);
timings.block_timeout = t1->bwt;
timings.char_timeout = t1->cwt;
timings.block_delay = t1->bgt;
ICC_Async_SetTimings (t1->icc, &timings);
#ifdef DEBUG_PROTOCOL
printf ("Protocol: T=1: IFSC=%d, IFSD=%d, CWT=%d, BWT=%d, BGT=%d, EDC=%s\n",
t1->ifsc, t1->ifsd, t1->cwt, t1->bwt, t1->bgt,
(t1->edc == PROTOCOL_T1_EDC_LRC) ? "LRC" : "CRC");
#endif
return PROTOCOL_T1_OK;
}
int
Protocol_T1_Command (Protocol_T1 * t1, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
T1_Block *block;
BYTE *buffer, rsp_type, bytes, nr, wtx;
unsigned short counter;
int ret;
bool more;
/* Calculate the number of bytes to send */
counter = 0;
bytes = MIN (APDU_Cmd_RawLen (cmd), t1->ifsc);
/* See if chaining is needed */
more = (APDU_Cmd_RawLen (cmd) > t1->ifsc);
/* Increment ns */
t1->ns = (t1->ns + 1) %2;
/* Create an I-Block */
block = T1_Block_NewIBlock (bytes, APDU_Cmd_Raw (cmd), t1->ns, more);
#ifdef DEBUG_PROTOCOL
printf ("Sending block I(%d,%d)\n", t1->ns, more);
#endif
/* Send a block */
ret = Protocol_T1_SendBlock (t1, block);
/* Delete I-block */
T1_Block_Delete (block);
while ((ret == PROTOCOL_T1_OK) && more)
{
/* Receive a block */
ret = Protocol_T1_ReceiveBlock (t1, &block);
if (ret == PROTOCOL_T1_OK)
{
rsp_type = T1_Block_GetType (block);
/* Positive ACK R-Block received */
if (rsp_type == T1_BLOCK_R_OK)
{
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Received block R(%d)\n", T1_Block_GetNR (block));
#endif
/* Delete block */
T1_Block_Delete (block);
/* Increment ns */
t1->ns = (t1->ns + 1) % 2;
/* Calculate the number of bytes to send */
counter += bytes;
bytes = MIN (APDU_Cmd_RawLen (cmd) - counter, t1->ifsc);
/* See if chaining is needed */
more = (APDU_Cmd_RawLen (cmd) - counter > t1->ifsc);
/* Create an I-Block */
block =
T1_Block_NewIBlock (bytes, APDU_Cmd_Raw (cmd) + counter,
t1->ns, more);
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Sending block I(%d,%d)\n", t1->ns, more);
#endif
/* Send a block */
ret = Protocol_T1_SendBlock (t1, block);
/* Delete I-block */
T1_Block_Delete (block);
}
else
{
/* Delete block */
T1_Block_Delete (block);
ret = PROTOCOL_T1_NOT_IMPLEMENTED;
}
}
else
{
ret = PROTOCOL_T1_NOT_IMPLEMENTED;
}
}
/* Reset counter */
buffer = NULL;
counter = 0;
more = TRUE;
wtx = 0;
while ((ret == PROTOCOL_T1_OK) && more)
{
if (wtx > 1)
Protocol_T1_UpdateBWT (t1, wtx * (t1->bwt));
/* Receive a block */
ret = Protocol_T1_ReceiveBlock (t1, &block);
if (wtx > 1)
{
Protocol_T1_UpdateBWT (t1, t1->bwt);
wtx = 0;
}
if (ret == PROTOCOL_T1_OK)
{
rsp_type = T1_Block_GetType (block);
if (rsp_type == T1_BLOCK_I)
{
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Received block I(%d,%d)\n",
T1_Block_GetNS(block), T1_Block_GetMore (block));
#endif
/* Calculate nr */
nr = (T1_Block_GetNS (block) + 1) % 2;
/* Save inf field */
bytes = T1_Block_GetLen (block);
buffer = (BYTE *) realloc(buffer, counter + bytes);
memcpy (buffer + counter, T1_Block_GetInf (block), bytes);
counter += bytes;
/* See if chaining is requested */
more = T1_Block_GetMore (block);
/* Delete block */
T1_Block_Delete (block);
if (more)
{
/* Create an R-Block */
block = T1_Block_NewRBlock (T1_BLOCK_R_OK, nr);
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Sending block R(%d)\n", nr);
#endif
/* Send R-Block */
ret = Protocol_T1_SendBlock (t1, block);
/* Delete I-block */
T1_Block_Delete (block);
}
}
/* WTX Request S-Block received */
else if (rsp_type == T1_BLOCK_S_WTX_REQ)
{
/* Get wtx multiplier */
wtx = (*T1_Block_GetInf (block));
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Received block S(WTX request, %d)\n", wtx);
#endif
/* Delete block */
T1_Block_Delete (block);
/* Create an WTX response S-Block */
block = T1_Block_NewSBlock (T1_BLOCK_S_WTX_RES, 1, &wtx);
#ifdef DEBUG_PROTOCOL
printf ("Protocol: Sending block S(WTX response, %d)\n", wtx);
#endif
/* Send WTX response */
ret = Protocol_T1_SendBlock (t1, block);
/* Delete block */
T1_Block_Delete (block);
}
else
{
ret = PROTOCOL_T1_NOT_IMPLEMENTED;
}
}
}
if (ret == PROTOCOL_T1_OK)
(*rsp) = APDU_Rsp_New (buffer, counter);
if (buffer != NULL)
free (buffer);
return ret;
}
int
Protocol_T1_Close (Protocol_T1 * t1)
{
Protocol_T1_Clear (t1);
return PROTOCOL_T1_OK;
}
void
Protocol_T1_Delete (Protocol_T1 * t1)
{
free (t1);
}
/*
* Not exported functions definition
*/
static int
Protocol_T1_SendBlock (Protocol_T1 * t1, T1_Block * block)
{
BYTE *buffer;
int length, ret;
/* Setup transmission */
if (ICC_Async_BeginTransmission (t1->icc) != ICC_ASYNC_OK)
ret = PROTOCOL_T1_ICC_ERROR;
else
{
/* Send T=1 block */
buffer = T1_Block_Raw (block);
length = T1_Block_RawLen (block);
if (ICC_Async_Transmit (t1->icc, length, buffer) != ICC_ASYNC_OK)
{
ICC_Async_EndTransmission (t1->icc);
ret = PROTOCOL_T1_ICC_ERROR;
}
else
ret = PROTOCOL_T1_OK;
}
return ret;
}
static int
Protocol_T1_ReceiveBlock (Protocol_T1 * t1, T1_Block ** block)
{
BYTE buffer[T1_BLOCK_MAX_SIZE];
int ret;
/* Receive four mandatory bytes */
if (ICC_Async_Receive (t1->icc, 4, buffer) != ICC_ASYNC_OK)
{
ret = PROTOCOL_T1_ICC_ERROR;
(*block) = NULL;
}
else
{
if (buffer[2] != 0x00)
{
/* Set timings to read the remaining block */
Protocol_T1_UpdateBWT (t1, t1->cwt);
/* Receive remaining bytes */
if (ICC_Async_Receive (t1->icc, buffer[2], buffer + 4) !=
ICC_ASYNC_OK)
{
(*block) = NULL;
ret = PROTOCOL_T1_ICC_ERROR;
}
else
{
(*block) = T1_Block_New (buffer, buffer[2] + 4);
ret = PROTOCOL_T1_OK;
}
/* Restore timings */
Protocol_T1_UpdateBWT (t1, t1->bwt);
}
else
{
ret = PROTOCOL_T1_OK;
(*block) = T1_Block_New (buffer, 4);
}
}
if (ICC_Async_Switch (t1->icc) != ICC_ASYNC_OK)
ret = PROTOCOL_T1_ICC_ERROR;
/* End of transmission */
if (ICC_Async_EndTransmission (t1->icc) != ICC_ASYNC_OK)
ret = PROTOCOL_T1_ICC_ERROR;
return ret;
}
static void
Protocol_T1_Clear (Protocol_T1 * t1)
{
t1->icc = NULL;
t1->ifsc = 0;
t1->ifsd = 0;
t1->bgt = 0;
t1->bwt = 0;
t1->cwt = 0;
t1->edc = 0;
t1->ns = 0;
}
static int
Protocol_T1_UpdateBWT (Protocol_T1 * t1, unsigned short bwt)
{
ICC_Async_Timings timings;
ICC_Async_GetTimings (t1->icc, &timings);
timings.block_timeout = bwt;
ICC_Async_SetTimings (t1->icc, &timings);
return PROTOCOL_T1_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1