556 lines
18 KiB
C
Executable File
556 lines
18 KiB
C
Executable File
/*
|
|
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 version is modified by doz21 to work in a special manner ;)
|
|
|
|
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 "../globals.h"
|
|
|
|
#ifdef WITH_CARDREADER
|
|
#include "../oscam-time.h"
|
|
#include "icc_async.h"
|
|
#include "protocol_t0.h"
|
|
/*
|
|
* Not exported constants definition
|
|
*/
|
|
|
|
#define OK 0
|
|
#define ERROR 1
|
|
|
|
#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
|
|
|
|
#define PROTOCOL_T14_MAX_NULLS 200
|
|
#define PROTOCOL_T14_DEFAULT_WI 10
|
|
#define PROTOCOL_T14_MAX_SHORT_COMMAND 260
|
|
#define PROTOCOL_T14_MAX_SHORT_RESPONSE 258
|
|
|
|
/* Types of APDU's */
|
|
#define APDU_CASE_1 0x0001 /* Nor send neither receive data */
|
|
#define APDU_CASE_2S 0x0002 /* Receive data (1..256) */
|
|
#define APDU_CASE_3S 0x0003 /* Send data (1..255) */
|
|
#define APDU_CASE_4S 0x0004 /* Send data (1..255) and receive data (1..256) */
|
|
#define APDU_CASE_2E 0x0102 /* Receive data (1..65536) */
|
|
#define APDU_CASE_3E 0x0103 /* Send data (1..65535) */
|
|
#define APDU_CASE_4E 0x0104 /* Send data (1..65535) and receive data (1..65536) */
|
|
#define APDU_MALFORMED 5 /* Malformed APDU */
|
|
|
|
/* Timings in ATR are not used in T=0 cards */
|
|
/* #undef PROTOCOL_T0_USE_DEFAULT_TIMINGS */
|
|
|
|
/*
|
|
* Not exported functions declaration
|
|
*/
|
|
|
|
static int32_t Protocol_T0_Case2E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr);
|
|
static int32_t Protocol_T0_Case3E(struct s_reader *reader, unsigned char *command, unsigned char *rsp, uint16_t *lr);
|
|
static int32_t Protocol_T0_Case4E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr);
|
|
static int32_t Protocol_T0_ExchangeTPDU(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr);
|
|
|
|
static int32_t APDU_Cmd_Case(unsigned char *command, uint16_t command_len){
|
|
unsigned char B1;
|
|
uint16_t B2B3;
|
|
uint32_t L;
|
|
int32_t res;
|
|
|
|
/* Calculate length of body */
|
|
L = MAX(command_len - 4, 0);
|
|
|
|
/* Case 1 */
|
|
if(L == 0)
|
|
{ res = APDU_CASE_1; }
|
|
else
|
|
{
|
|
/* Get first byte of body */
|
|
B1 = command[4];
|
|
|
|
if((B1 != 0) && (L == (uint32_t)B1 + 1))
|
|
{ res = APDU_CASE_2S; }
|
|
else if(L == 1)
|
|
{ res = APDU_CASE_3S; }
|
|
else if((B1 != 0) && (L == (uint32_t)B1 + 2))
|
|
{ res = APDU_CASE_4S; }
|
|
else if((B1 == 0) && (L > 2))
|
|
{
|
|
/* Get second and third byte of body */
|
|
B2B3 = (((uint16_t)(command[5]) << 8) | command[6]);
|
|
|
|
if((B2B3 != 0) && (L == (uint32_t)B2B3 + 3))
|
|
{ res = APDU_CASE_2E; }
|
|
else if(L == 3)
|
|
{ res = APDU_CASE_3E; }
|
|
else if((B2B3 != 0) && (L == (uint32_t)B2B3 + 5))
|
|
{ res = APDU_CASE_4E; }
|
|
else
|
|
{ res = APDU_MALFORMED; }
|
|
}
|
|
else
|
|
{ res = APDU_MALFORMED; }
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Exported funtions definition
|
|
*/
|
|
|
|
int32_t Protocol_T0_Command(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
*lr = 0; //will be returned in case of error
|
|
if(command_len < 5) //APDU_CASE_1 or malformed
|
|
{ return ERROR; }
|
|
int32_t cmd_case = APDU_Cmd_Case(command, command_len);
|
|
switch(cmd_case)
|
|
{
|
|
case APDU_CASE_2E:
|
|
return Protocol_T0_Case2E(reader, command, command_len, rsp, lr); /* fallthrough */
|
|
case APDU_CASE_3E:
|
|
return Protocol_T0_Case3E(reader, command, rsp, lr); /* fallthrough */
|
|
case APDU_CASE_4E:
|
|
return Protocol_T0_Case4E(reader, command, command_len, rsp, lr); /* fallthrough */
|
|
case APDU_CASE_4S:
|
|
command_len--; /* fallthrough */ //FIXME this should change 4S to 2S/3S command
|
|
case APDU_CASE_2S: /* fallthrough */
|
|
case APDU_CASE_3S:
|
|
return Protocol_T0_ExchangeTPDU(reader, command, command_len, rsp, lr); /* fallthrough */
|
|
default:
|
|
rdr_log_dbg(reader, D_IFD, "Protocol: T=0: Invalid APDU");
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Not exported functions definition
|
|
*/
|
|
|
|
|
|
static int32_t Protocol_T0_Case2E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
unsigned char buffer[PROTOCOL_T0_MAX_SHORT_COMMAND];
|
|
unsigned char tpdu_rsp[CTA_RES_LEN];
|
|
uint16_t tpdu_lr = 0;
|
|
uint32_t i;
|
|
|
|
uint32_t Lc = (((uint32_t)(command[5]) << 8) | command[6]);
|
|
if(Lc < 256)
|
|
{
|
|
/* MAP APDU onto command TPDU */
|
|
memcpy(buffer, command, 4);
|
|
buffer[4] = (unsigned char) Lc;
|
|
memcpy(buffer + 5, command + 7, buffer[4]);
|
|
return Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, rsp, lr);
|
|
}
|
|
|
|
/* Prepare envelope TPDU */
|
|
buffer[0] = command[0];
|
|
buffer[1] = 0xC2;
|
|
buffer[2] = 0x00;
|
|
buffer[3] = 0x00;
|
|
|
|
for(i = 0; i < command_len; i += buffer[4])
|
|
{
|
|
/* Create envelope command TPDU */
|
|
buffer[4] = MIN(255, command_len - i);
|
|
memcpy(buffer + 5, command + i, buffer[4]);
|
|
call(Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, tpdu_rsp, &tpdu_lr));
|
|
/* Card does support envelope command */
|
|
if(tpdu_rsp[tpdu_lr - 2] == 0x90)
|
|
{
|
|
/* This is not the last segment */
|
|
if(buffer[4] + i < command_len)
|
|
{ tpdu_lr = 0; }
|
|
else
|
|
{
|
|
memcpy(rsp, tpdu_rsp, tpdu_lr); // Map response TPDU onto APDU
|
|
*lr = tpdu_lr;
|
|
}
|
|
}
|
|
else /* Card does not support envelope command or error */
|
|
{
|
|
memcpy(rsp, tpdu_rsp, tpdu_lr); // Map response TPDU onto APDU
|
|
*lr = tpdu_lr;
|
|
break;
|
|
}
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
|
|
static int32_t Protocol_T0_Case3E(struct s_reader *reader, unsigned char *command, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
int32_t ret;
|
|
unsigned char buffer[5];
|
|
unsigned char tpdu_rsp[CTA_RES_LEN];
|
|
uint16_t tpdu_lr = 0;
|
|
int32_t Lm, Lx;
|
|
|
|
uint32_t Le = ((((uint32_t)(command[5]) << 8) | command[6]) == 0 ? 65536 : (((uint32_t)(command[5]) << 8) | command[6]));
|
|
memcpy(buffer, command, 4);//Map APDU command onto TPDU
|
|
|
|
if(Le <= 256)
|
|
{
|
|
buffer[4] = (unsigned char)Le;
|
|
return Protocol_T0_ExchangeTPDU(reader, buffer, 5, rsp, lr); //this was Case3S !!!
|
|
}
|
|
|
|
/* Map APDU onto command TPDU */
|
|
buffer[4] = 0x00;
|
|
call(Protocol_T0_ExchangeTPDU(reader, buffer, 5 , tpdu_rsp, &tpdu_lr));
|
|
|
|
if(tpdu_rsp[tpdu_lr - 2] == 0x6C) /* Le not accepted, La indicated */
|
|
{
|
|
/* Map command APDU onto TPDU */
|
|
memcpy(buffer, command, 4);
|
|
buffer[4] = tpdu_rsp[tpdu_lr - 1];
|
|
|
|
/* Delete response TPDU */
|
|
tpdu_lr = 0;
|
|
|
|
return Protocol_T0_ExchangeTPDU(reader, buffer, 5, rsp, lr); //Reissue command
|
|
}
|
|
|
|
memcpy(rsp, tpdu_rsp, tpdu_lr);//Map response TPDU onto APDU without change , also for SW1 = 0x67
|
|
*lr = tpdu_lr;
|
|
ret = OK;
|
|
if(tpdu_rsp[tpdu_lr - 2] == 0x61) /* Command processed, Lx indicated */
|
|
{
|
|
Lx = (tpdu_rsp[tpdu_lr - 1] == 0x00) ? 256 : tpdu_rsp[tpdu_lr - 1];
|
|
Lm = Le - (*lr - 2);
|
|
|
|
/* Prepare Get Response TPDU */
|
|
buffer[0] = command[0];
|
|
buffer[1] = 0xC0;
|
|
buffer[2] = 0x00;
|
|
buffer[3] = 0x00;
|
|
|
|
while(Lm > 0)
|
|
{
|
|
buffer[4] = (unsigned char) MIN(Lm, Lx);
|
|
call(Protocol_T0_ExchangeTPDU(reader, buffer, 5, tpdu_rsp, &tpdu_lr));
|
|
|
|
/* Append response TPDU to APDU */
|
|
if((*lr + tpdu_lr) > CTA_RES_LEN)
|
|
{
|
|
rdr_log(reader, "TPDU Append error, new length %i exceeds max length %i", *lr + tpdu_lr, CTA_RES_LEN);
|
|
return ERROR;
|
|
}
|
|
memcpy(rsp + (*lr - 2), tpdu_rsp, tpdu_lr);
|
|
*lr += tpdu_lr;
|
|
|
|
/* Delete response TPDU */
|
|
tpdu_lr = 0;
|
|
|
|
Lm = Le - (*lr - 2);
|
|
}/* Lm == 0 */
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int32_t Protocol_T0_Case4E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
int32_t ret;
|
|
unsigned char buffer[PROTOCOL_T0_MAX_SHORT_COMMAND];
|
|
unsigned char tpdu_rsp[CTA_RES_LEN];
|
|
memset(tpdu_rsp, 0, sizeof(tpdu_rsp));
|
|
uint16_t tpdu_lr = 0;
|
|
int32_t Le;
|
|
|
|
uint32_t Lc = (((uint32_t)(command[5]) << 8) | command[6]);
|
|
/* 4E1 */
|
|
if(Lc < 256)
|
|
{
|
|
/* Map APDU onto command TPDU */
|
|
memcpy(buffer, command, 4);
|
|
buffer[4] = (unsigned char) Lc;
|
|
memcpy(buffer + 5, command, buffer[4]);
|
|
ret = Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, tpdu_rsp, &tpdu_lr);
|
|
}
|
|
else /* 4E2 */
|
|
{ ret = Protocol_T0_Case2E(reader, command, command_len, tpdu_rsp, &tpdu_lr); }
|
|
|
|
/* 4E1 a) b) and c) */
|
|
if(ret == OK)
|
|
{
|
|
Le = ((((uint32_t)(command[command_len - 2]) << 8) | command[command_len - 1]) == 0 ? 65536 : (((uint32_t)(command[command_len - 2]) << 8) | command[command_len - 1]));
|
|
if(tpdu_lr > 1 && tpdu_rsp[tpdu_lr - 2] == 0x61)
|
|
{
|
|
/* Lm == (Le - APDU_Rsp_RawLen (tpdu_rsp)) == 0 */
|
|
if(tpdu_rsp[tpdu_lr - 1] != 0x00)
|
|
{ Le = MIN(tpdu_rsp[tpdu_lr - 1], Le); }
|
|
|
|
/* Delete response TPDU */
|
|
tpdu_lr = 0;
|
|
|
|
/* Prepare extended Get Response APDU command */
|
|
buffer[0] = command[0];
|
|
buffer[1] = 0xC0;
|
|
buffer[2] = 0x00;
|
|
buffer[3] = 0x00;
|
|
buffer[4] = 0x00; /* B1 = 0x00 */
|
|
buffer[5] = (unsigned char)(Le >> 8); /* B2 = BL-1 */
|
|
buffer[6] = (unsigned char)(Le & 0x00FF); /* B3 = BL */
|
|
ret = Protocol_T0_Case3E(reader, buffer, rsp, lr);
|
|
}
|
|
else if(tpdu_lr > 1 && (tpdu_rsp[tpdu_lr - 2] & 0xF0) == 0x60)
|
|
{
|
|
/* Map response TPDU onto APDU without change */
|
|
memcpy(rsp, tpdu_rsp, tpdu_lr);
|
|
*lr = tpdu_lr;
|
|
}
|
|
else
|
|
{
|
|
/* Delete response TPDU */
|
|
tpdu_lr = 0;
|
|
|
|
/* Prepare extended Get Response APDU command */
|
|
buffer[0] = command[0];
|
|
buffer[1] = 0xC0;
|
|
buffer[2] = 0x00;
|
|
buffer[3] = 0x00;
|
|
buffer[4] = 0x00; /* B1 = 0x00 */
|
|
buffer[5] = (unsigned char) Le >> 8; /* B2 = BL-1 */
|
|
buffer[6] = (unsigned char) Le & 0x00FF; /* B3 = BL */
|
|
ret = Protocol_T0_Case3E(reader, buffer, rsp, lr);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int32_t Protocol_T0_ExchangeTPDU(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
unsigned char buffer[PROTOCOL_T0_MAX_SHORT_RESPONSE];
|
|
unsigned char *data;
|
|
int32_t Lc, Le, sent, recved, expectedlen;
|
|
int32_t nulls, cmd_case;
|
|
int32_t timeout;
|
|
*lr = 0; //in case of error this will be returned
|
|
|
|
cmd_case = APDU_Cmd_Case(command, command_len);
|
|
switch(cmd_case)
|
|
{
|
|
case APDU_CASE_2S:
|
|
Lc = command[4];
|
|
Le = 0;
|
|
expectedlen = 1;
|
|
data = command + 5;
|
|
break;
|
|
case APDU_CASE_3S:
|
|
Lc = 0;
|
|
Le = command[4];
|
|
if(!Le)
|
|
{ expectedlen = 2; }
|
|
else
|
|
{ expectedlen = 1 + Le + 2; }
|
|
data = NULL;
|
|
break;
|
|
default:
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: invalid cmd_case = %i in Protocol_T0_ExchangeTPDU", cmd_case);
|
|
return ERROR;
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout
|
|
if(ICC_Async_Transmit(reader, 5, expectedlen, command, 0, timeout) != OK) { return ERROR; } //Send header bytes
|
|
|
|
/* Initialise counters */
|
|
nulls = 0;
|
|
sent = 0;
|
|
recved = 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(recved < PROTOCOL_T0_MAX_SHORT_RESPONSE)
|
|
{
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read one procedure byte
|
|
|
|
/* NULL byte received */
|
|
if(buffer[recved] == 0x60)
|
|
{
|
|
nulls++;
|
|
if(nulls >= PROTOCOL_T0_MAX_NULLS) //Maximum number of nulls reached
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Maximum number of nulls reached: %d", __func__, nulls);
|
|
return ERROR;
|
|
}
|
|
}
|
|
else if((buffer[recved] & 0xF0) == 0x60 || (buffer[recved] & 0xF0) == 0x90) /* SW1 byte received */
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "SW1: %02X", buffer[recved] & 0xf0);
|
|
recved++;
|
|
if(recved >= PROTOCOL_T0_MAX_SHORT_RESPONSE)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Maximum short response exceeded: %d", __func__, recved);
|
|
return ERROR;
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read SW2 byte
|
|
rdr_log_dbg(reader, D_TRACE, "SW2: %02X", buffer[recved] & 0xf0);
|
|
recved++;
|
|
break;
|
|
}
|
|
else if((buffer[recved] & 0x0E) == (command[1] & 0x0E)) /* ACK byte received */
|
|
{
|
|
//printf("ack\n");
|
|
/* Reset null's counter */
|
|
nulls = 0;
|
|
|
|
/* Case 2 command: send data */
|
|
if(cmd_case == APDU_CASE_2S)
|
|
{
|
|
if(sent >= Lc)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: ACK byte: sent=%d exceeds Lc=%d", __func__, sent, Lc);
|
|
return ERROR;
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout
|
|
if(ICC_Async_Transmit(reader, MAX(Lc - sent, 0), 2, data + sent, 0, timeout) != OK) { return ERROR; } /* Send remaining data bytes */
|
|
sent = Lc;
|
|
continue;
|
|
}
|
|
else /* Case 3 command: receive data */
|
|
{
|
|
if(recved > PROTOCOL_T0_MAX_SHORT_RESPONSE)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Case 3 ACK - maximum short response exceeded: %d", __func__, recved);
|
|
return ERROR;
|
|
}
|
|
|
|
/*
|
|
* Le <= PROTOCOL_T0_MAX_SHORT_RESPONSE - 2 for short commands
|
|
*/
|
|
|
|
/* Read remaining data bytes */
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
if(ICC_Async_Receive(reader, MAX(Le - recved, 0), buffer + recved, 0, timeout) != OK) { return ERROR; }
|
|
recved = Le;
|
|
continue;
|
|
}
|
|
}
|
|
else if((buffer[recved] & 0x0E) == ((~command[1]) & 0x0E)) /* ~ACK byte received */
|
|
{
|
|
//printf("~ack\n");
|
|
nulls = 0; //Reset null's counter
|
|
|
|
/* Case 2 command: send data */
|
|
if(cmd_case == APDU_CASE_2S)
|
|
{
|
|
if(sent >= Lc)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: ~ACK byte: sent=%d exceeds Lc=%d", __func__, sent, Lc);
|
|
return ERROR;
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout
|
|
if(ICC_Async_Transmit(reader, 1, 1, data + sent, 0, timeout) != OK) { return ERROR; } //Send next data byte
|
|
sent++;
|
|
continue;
|
|
}
|
|
else /* Case 3 command: receive data */
|
|
{
|
|
if(recved > PROTOCOL_T0_MAX_SHORT_RESPONSE)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Case 3 ~ACK - maximum short response exceeded: %d", __func__, recved);
|
|
return ERROR;
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read next data byte
|
|
recved++;
|
|
continue;
|
|
}
|
|
}
|
|
else /* Anything else received */
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Received unexpected character: %02X", __func__, buffer[recved]);
|
|
return ERROR;
|
|
}
|
|
}//while
|
|
|
|
memcpy(rsp, buffer, recved);
|
|
*lr = recved;
|
|
return OK;
|
|
}
|
|
|
|
int32_t Protocol_T14_ExchangeTPDU(struct s_reader *reader, unsigned char *cmd_raw, uint16_t command_len, unsigned char *rsp, uint16_t *lr)
|
|
{
|
|
unsigned char buffer[PROTOCOL_T14_MAX_SHORT_RESPONSE];
|
|
int32_t recved;
|
|
int32_t cmd_case;
|
|
int32_t timeout;
|
|
unsigned char ixor = 0x3E;
|
|
unsigned char ixor1 = 0x3F;
|
|
int32_t i;
|
|
int32_t cmd_len = (int32_t) command_len;
|
|
*lr = 0; //in case of error this is returned
|
|
|
|
/* Parse APDU */
|
|
cmd_case = APDU_Cmd_Case(cmd_raw, cmd_len);
|
|
for(i = 0; i < cmd_len; i++)
|
|
{ ixor ^= cmd_raw[i]; }
|
|
|
|
/* Check case of command */
|
|
if((cmd_case != APDU_CASE_2S) && (cmd_case != APDU_CASE_3S))
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: invalid cmd_case = %i in Protocol_T14_ExchangeTPDU", cmd_case);
|
|
return ERROR;
|
|
}
|
|
|
|
buffer[0] = 0x01; //send 0x01 byte
|
|
memcpy(buffer + 1, cmd_raw, cmd_len); // apdu
|
|
buffer[cmd_len + 1] = ixor; // xor byte
|
|
|
|
/* Send apdu */
|
|
timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout
|
|
if(ICC_Async_Transmit(reader, cmd_len + 2, 0, buffer, 0, timeout) != OK) { return ERROR; } //send apdu
|
|
if(cmd_raw[0] == 0x02 && cmd_raw[1] == 0x09) { cs_sleepms(2500); } //FIXME why wait? -> needed for init on overclocked T14 cards
|
|
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1;
|
|
if(ICC_Async_Receive(reader, 8, buffer, 0, timeout) != OK) { return ERROR; } //Read one procedure byte
|
|
recved = (int32_t)buffer[7];
|
|
if(recved)
|
|
{
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1;
|
|
if(ICC_Async_Receive(reader, recved, buffer + 8, 0 , timeout) != OK) {return ERROR; }
|
|
}
|
|
timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout
|
|
// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1;
|
|
if(ICC_Async_Receive(reader, 1, &ixor, 0, timeout) != OK) {return ERROR; }
|
|
for(i = 0; i < 8 + recved; i++)
|
|
{ ixor1 ^= buffer[i]; }
|
|
if(ixor1 != ixor)
|
|
{
|
|
rdr_log_dbg(reader, D_TRACE, "ERROR: invalid checksum = %02X expected %02X", ixor1, ixor);
|
|
return ERROR;
|
|
}
|
|
memcpy(buffer + 8 + recved, buffer + 2, 2);
|
|
*lr = recved + 2;
|
|
memcpy(rsp, buffer + 8, *lr);
|
|
return OK;
|
|
}
|
|
#endif
|