|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Implementation of T=1
|
|
Packit |
9f0df5 |
*
|
|
Packit |
9f0df5 |
* Copyright (C) 2003, Olaf Kirch <okir@suse.de>
|
|
Packit |
9f0df5 |
*
|
|
Packit |
9f0df5 |
* improvements by:
|
|
Packit |
9f0df5 |
* Copyright (C) 2004 Ludovic Rousseau <ludovic.rousseau@free.fr>
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#include <config.h>
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#include <pcsclite.h>
|
|
Packit |
9f0df5 |
#include <ifdhandler.h>
|
|
Packit |
9f0df5 |
#include "commands.h"
|
|
Packit |
9f0df5 |
#include "buffer.h"
|
|
Packit |
9f0df5 |
#include "debug.h"
|
|
Packit |
9f0df5 |
#include "proto-t1.h"
|
|
Packit |
9f0df5 |
#include "checksum.h"
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#include "ccid.h"
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#ifdef HAVE_STRING_H
|
|
Packit |
9f0df5 |
#include <string.h>
|
|
Packit |
9f0df5 |
#endif
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* I block */
|
|
Packit |
9f0df5 |
#define T1_I_SEQ_SHIFT 6
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* R block */
|
|
Packit |
9f0df5 |
#define T1_IS_ERROR(pcb) ((pcb) & 0x0F)
|
|
Packit |
9f0df5 |
#define T1_EDC_ERROR 0x01
|
|
Packit |
9f0df5 |
#define T1_OTHER_ERROR 0x02
|
|
Packit |
9f0df5 |
#define T1_R_SEQ_SHIFT 4
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* S block stuff */
|
|
Packit |
9f0df5 |
#define T1_S_IS_RESPONSE(pcb) ((pcb) & T1_S_RESPONSE)
|
|
Packit |
9f0df5 |
#define T1_S_TYPE(pcb) ((pcb) & 0x0F)
|
|
Packit |
9f0df5 |
#define T1_S_RESPONSE 0x20
|
|
Packit |
9f0df5 |
#define T1_S_RESYNC 0x00
|
|
Packit |
9f0df5 |
#define T1_S_IFS 0x01
|
|
Packit |
9f0df5 |
#define T1_S_ABORT 0x02
|
|
Packit |
9f0df5 |
#define T1_S_WTX 0x03
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#define swap_nibbles(x) ( (x >> 4) | ((x & 0xF) << 4) )
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#ifndef TRUE
|
|
Packit |
9f0df5 |
#define TRUE 1
|
|
Packit |
9f0df5 |
#define FALSE 0
|
|
Packit |
9f0df5 |
#endif
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
#define NAD 0
|
|
Packit |
9f0df5 |
#define PCB 1
|
|
Packit |
9f0df5 |
#define LEN 2
|
|
Packit |
9f0df5 |
#define DATA 3
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* internal state, do not mess with it. */
|
|
Packit |
9f0df5 |
/* should be != DEAD after reset/init */
|
|
Packit |
9f0df5 |
enum {
|
|
Packit |
9f0df5 |
SENDING, RECEIVING, RESYNCH, DEAD
|
|
Packit |
9f0df5 |
};
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static void t1_set_checksum(t1_state_t *, int);
|
|
Packit |
9f0df5 |
static unsigned int t1_block_type(unsigned char);
|
|
Packit |
9f0df5 |
static unsigned int t1_seq(unsigned char);
|
|
Packit |
9f0df5 |
static unsigned int t1_rebuild(t1_state_t *t1, unsigned char *block);
|
|
Packit |
9f0df5 |
static unsigned int t1_compute_checksum(t1_state_t *, unsigned char *, size_t);
|
|
Packit |
9f0df5 |
static int t1_verify_checksum(t1_state_t *, unsigned char *, size_t);
|
|
Packit |
9f0df5 |
static int t1_xcv(t1_state_t *, unsigned char *, size_t, size_t);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Set default T=1 protocol parameters
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
static void t1_set_defaults(t1_state_t * t1)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
t1->retries = 3;
|
|
Packit |
9f0df5 |
/* This timeout is rather insane, but we need this right now
|
|
Packit |
9f0df5 |
* to support cryptoflex keygen */
|
|
Packit |
9f0df5 |
t1->ifsc = 32;
|
|
Packit |
9f0df5 |
t1->ifsd = 32;
|
|
Packit |
9f0df5 |
t1->nr = 0;
|
|
Packit |
9f0df5 |
t1->ns = 0;
|
|
Packit |
9f0df5 |
t1->wtx = 0;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static void t1_set_checksum(t1_state_t * t1, int csum)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
switch (csum) {
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_CHECKSUM_LRC:
|
|
Packit |
9f0df5 |
t1->rc_bytes = 1;
|
|
Packit |
9f0df5 |
t1->checksum = csum_lrc_compute;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_CHECKSUM_CRC:
|
|
Packit |
9f0df5 |
t1->rc_bytes = 2;
|
|
Packit |
9f0df5 |
t1->checksum = csum_crc_compute;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Attach t1 protocol
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
int t1_init(t1_state_t * t1, int lun)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
t1_set_defaults(t1);
|
|
Packit |
9f0df5 |
t1_set_param(t1, IFD_PROTOCOL_T1_CHECKSUM_LRC, 0);
|
|
Packit |
9f0df5 |
t1_set_param(t1, IFD_PROTOCOL_T1_STATE, SENDING);
|
|
Packit |
9f0df5 |
t1_set_param(t1, IFD_PROTOCOL_T1_MORE, FALSE);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
t1->lun = lun;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Detach t1 protocol
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
void t1_release(/*@unused@*/ t1_state_t * t1)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
(void)t1;
|
|
Packit |
9f0df5 |
/* NOP */
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Get/set parmaters for T1 protocol
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
int t1_set_param(t1_state_t * t1, int type, long value)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
switch (type) {
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_CHECKSUM_LRC:
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_CHECKSUM_CRC:
|
|
Packit |
9f0df5 |
t1_set_checksum(t1, type);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_IFSC:
|
|
Packit |
9f0df5 |
t1->ifsc = value;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_IFSD:
|
|
Packit |
9f0df5 |
t1->ifsd = value;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_STATE:
|
|
Packit |
9f0df5 |
t1->state = value;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case IFD_PROTOCOL_T1_MORE:
|
|
Packit |
9f0df5 |
t1->more = value;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
default:
|
|
Packit |
9f0df5 |
DEBUG_INFO2("Unsupported parameter %d", type);
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Send an APDU through T=1
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
int t1_transceive(t1_state_t * t1, unsigned int dad,
|
|
Packit |
9f0df5 |
const void *snd_buf, size_t snd_len,
|
|
Packit |
9f0df5 |
void *rcv_buf, size_t rcv_len)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
ct_buf_t sbuf, rbuf, tbuf;
|
|
Packit |
9f0df5 |
unsigned char sdata[T1_BUFFER_SIZE], sblk[5];
|
|
Packit |
9f0df5 |
unsigned int slen, retries, resyncs;
|
|
Packit |
9f0df5 |
size_t last_send = 0;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (snd_len == 0)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* we can't talk to a dead card / reader. Reset it! */
|
|
Packit |
9f0df5 |
if (t1->state == DEAD)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL("T=1 state machine is DEAD. Reset the card first.");
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
t1->state = SENDING;
|
|
Packit |
9f0df5 |
retries = t1->retries;
|
|
Packit |
9f0df5 |
resyncs = 3;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Initialize send/recv buffer */
|
|
Packit |
9f0df5 |
ct_buf_set(&sbuf, (void *)snd_buf, snd_len);
|
|
Packit |
9f0df5 |
ct_buf_init(&rbuf, rcv_buf, rcv_len);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Send the first block */
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
while (1) {
|
|
Packit |
9f0df5 |
unsigned char pcb;
|
|
Packit |
9f0df5 |
int n;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
retries--;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
n = t1_xcv(t1, sdata, slen, sizeof(sdata));
|
|
Packit |
9f0df5 |
if (-2 == n)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Parity error");
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_EDC_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (n < 0) {
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL("fatal: transmit/receive failed");
|
|
Packit |
9f0df5 |
t1->state = DEAD;
|
|
Packit |
9f0df5 |
goto error;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if ((sdata[NAD] != swap_nibbles(dad)) /* wrong NAD */
|
|
Packit |
9f0df5 |
|| (sdata[LEN] == 0xFF)) /* length == 0xFF (illegal) */
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("R-BLOCK required");
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (!t1_verify_checksum(t1, sdata, n)) {
|
|
Packit |
9f0df5 |
DEBUG_COMM("checksum failed");
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_EDC_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
pcb = sdata[PCB];
|
|
Packit |
9f0df5 |
switch (t1_block_type(pcb)) {
|
|
Packit |
9f0df5 |
case T1_R_BLOCK:
|
|
Packit |
9f0df5 |
if ((sdata[LEN] != 0x00) /* length != 0x00 (illegal) */
|
|
Packit |
9f0df5 |
|| (pcb & 0x20) /* b6 of pcb is set */
|
|
Packit |
9f0df5 |
)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("R-Block required");
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (((t1_seq(pcb) != t1->ns) /* wrong sequence number & no bit more */
|
|
Packit |
9f0df5 |
&& ! t1->more)
|
|
Packit |
9f0df5 |
)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM4("received: %d, expected: %d, more: %d",
|
|
Packit |
9f0df5 |
t1_seq(pcb), t1->ns, t1->more);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM("R-Block required");
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (t1->state == RECEIVING) {
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM("");
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* If the card terminal requests the next
|
|
Packit |
9f0df5 |
* sequence number, it received the previous
|
|
Packit |
9f0df5 |
* block successfully */
|
|
Packit |
9f0df5 |
if (t1_seq(pcb) != t1->ns) {
|
|
Packit |
9f0df5 |
ct_buf_get(&sbuf, NULL, last_send);
|
|
Packit |
9f0df5 |
last_send = 0;
|
|
Packit |
9f0df5 |
t1->ns ^= 1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* If there's no data available, the ICC
|
|
Packit |
9f0df5 |
* shouldn't be asking for more */
|
|
Packit |
9f0df5 |
if (ct_buf_avail(&sbuf) == 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
|
|
Packit |
9f0df5 |
&sbuf, &last_send);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
case T1_I_BLOCK:
|
|
Packit |
9f0df5 |
/* The first I-block sent by the ICC indicates
|
|
Packit |
9f0df5 |
* the last block we sent was received successfully. */
|
|
Packit |
9f0df5 |
if (t1->state == SENDING) {
|
|
Packit |
9f0df5 |
DEBUG_COMM("");
|
|
Packit |
9f0df5 |
ct_buf_get(&sbuf, NULL, last_send);
|
|
Packit |
9f0df5 |
last_send = 0;
|
|
Packit |
9f0df5 |
t1->ns ^= 1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
t1->state = RECEIVING;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* If the block sent by the card doesn't match
|
|
Packit |
9f0df5 |
* what we expected it to send, reply with
|
|
Packit |
9f0df5 |
* an R block */
|
|
Packit |
9f0df5 |
if (t1_seq(pcb) != t1->nr) {
|
|
Packit |
9f0df5 |
DEBUG_COMM("wrong nr");
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
t1->nr ^= 1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (ct_buf_put(&rbuf, sdata + 3, sdata[LEN]) < 0)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL2("buffer overrun by %d bytes", sdata[LEN] - (rbuf.size - rbuf.tail));
|
|
Packit |
9f0df5 |
goto error;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if ((pcb & T1_MORE_BLOCKS) == 0)
|
|
Packit |
9f0df5 |
goto done;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad, T1_R_BLOCK, NULL, NULL);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
case T1_S_BLOCK:
|
|
Packit |
9f0df5 |
if (T1_S_IS_RESPONSE(pcb) && t1->state == RESYNCH) {
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 6.2 */
|
|
Packit |
9f0df5 |
DEBUG_COMM("S-Block answer received");
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 6.3 */
|
|
Packit |
9f0df5 |
t1->state = SENDING;
|
|
Packit |
9f0df5 |
last_send = 0;
|
|
Packit |
9f0df5 |
resyncs = 3;
|
|
Packit |
9f0df5 |
retries = t1->retries;
|
|
Packit |
9f0df5 |
ct_buf_init(&rbuf, rcv_buf, rcv_len);
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
|
|
Packit |
9f0df5 |
&sbuf, &last_send);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (T1_S_IS_RESPONSE(pcb))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.2 */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM("Rule 7.2");
|
|
Packit |
9f0df5 |
slen = t1_rebuild(t1, sdata);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL("wrong response S-BLOCK received");
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata,
|
|
Packit |
9f0df5 |
dad, T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
ct_buf_init(&tbuf, sblk, sizeof(sblk));
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM("S-Block request received");
|
|
Packit |
9f0df5 |
switch (T1_S_TYPE(pcb)) {
|
|
Packit |
9f0df5 |
case T1_S_RESYNC:
|
|
Packit |
9f0df5 |
if (sdata[LEN] != 0)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM("Resync requested");
|
|
Packit |
9f0df5 |
/* the card is not allowed to send a resync. */
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
case T1_S_ABORT:
|
|
Packit |
9f0df5 |
if (sdata[LEN] != 0)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 9 */
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL("abort requested");
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
case T1_S_IFS:
|
|
Packit |
9f0df5 |
if (sdata[LEN] != 1)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL2("CT sent S-block with ifs=%u", sdata[DATA]);
|
|
Packit |
9f0df5 |
if (sdata[DATA] == 0)
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
t1->ifsc = sdata[DATA];
|
|
Packit |
9f0df5 |
ct_buf_putc(&tbuf, sdata[DATA]);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
case T1_S_WTX:
|
|
Packit |
9f0df5 |
if (sdata[LEN] != 1)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_R_BLOCK | T1_OTHER_ERROR,
|
|
Packit |
9f0df5 |
NULL, NULL);
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_COMM2("CT sent S-block with wtx=%u", sdata[DATA]);
|
|
Packit |
9f0df5 |
t1->wtx = sdata[DATA];
|
|
Packit |
9f0df5 |
ct_buf_putc(&tbuf, sdata[DATA]);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
default:
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL2("T=1: Unknown S block type 0x%02x", T1_S_TYPE(pcb));
|
|
Packit |
9f0df5 |
goto resync;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad,
|
|
Packit |
9f0df5 |
T1_S_BLOCK | T1_S_RESPONSE | T1_S_TYPE(pcb),
|
|
Packit |
9f0df5 |
&tbuf, NULL);
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Everything went just splendid */
|
|
Packit |
9f0df5 |
retries = t1->retries;
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
resync:
|
|
Packit |
9f0df5 |
/* the number or resyncs is limited, too */
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 6.4 */
|
|
Packit |
9f0df5 |
if (resyncs == 0)
|
|
Packit |
9f0df5 |
goto error;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 6 */
|
|
Packit |
9f0df5 |
resyncs--;
|
|
Packit |
9f0df5 |
t1->ns = 0;
|
|
Packit |
9f0df5 |
t1->nr = 0;
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, dad, T1_S_BLOCK | T1_S_RESYNC, NULL,
|
|
Packit |
9f0df5 |
NULL);
|
|
Packit |
9f0df5 |
t1->state = RESYNCH;
|
|
Packit |
9f0df5 |
t1->more = FALSE;
|
|
Packit |
9f0df5 |
retries = 1;
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
done:
|
|
Packit |
9f0df5 |
return ct_buf_avail(&rbuf);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
error:
|
|
Packit |
9f0df5 |
t1->state = DEAD;
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static unsigned t1_block_type(unsigned char pcb)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
switch (pcb & 0xC0) {
|
|
Packit |
9f0df5 |
case T1_R_BLOCK:
|
|
Packit |
9f0df5 |
return T1_R_BLOCK;
|
|
Packit |
9f0df5 |
case T1_S_BLOCK:
|
|
Packit |
9f0df5 |
return T1_S_BLOCK;
|
|
Packit |
9f0df5 |
default:
|
|
Packit |
9f0df5 |
return T1_I_BLOCK;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static unsigned int t1_seq(unsigned char pcb)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
switch (pcb & 0xC0) {
|
|
Packit |
9f0df5 |
case T1_R_BLOCK:
|
|
Packit |
9f0df5 |
return (pcb >> T1_R_SEQ_SHIFT) & 1;
|
|
Packit |
9f0df5 |
case T1_S_BLOCK:
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
default:
|
|
Packit |
9f0df5 |
return (pcb >> T1_I_SEQ_SHIFT) & 1;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
unsigned int t1_build(t1_state_t * t1, unsigned char *block,
|
|
Packit |
9f0df5 |
unsigned char dad, unsigned char pcb,
|
|
Packit |
9f0df5 |
ct_buf_t *bp, size_t *lenp)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
unsigned int len;
|
|
Packit |
9f0df5 |
char more = FALSE;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
len = bp ? ct_buf_avail(bp) : 0;
|
|
Packit |
9f0df5 |
if (len > t1->ifsc) {
|
|
Packit |
9f0df5 |
pcb |= T1_MORE_BLOCKS;
|
|
Packit |
9f0df5 |
len = t1->ifsc;
|
|
Packit |
9f0df5 |
more = TRUE;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Add the sequence number */
|
|
Packit |
9f0df5 |
switch (t1_block_type(pcb)) {
|
|
Packit |
9f0df5 |
case T1_R_BLOCK:
|
|
Packit |
9f0df5 |
pcb |= t1->nr << T1_R_SEQ_SHIFT;
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
case T1_I_BLOCK:
|
|
Packit |
9f0df5 |
pcb |= t1->ns << T1_I_SEQ_SHIFT;
|
|
Packit |
9f0df5 |
t1->more = more;
|
|
Packit |
9f0df5 |
DEBUG_COMM2("more bit: %d", more);
|
|
Packit |
9f0df5 |
break;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
block[0] = dad;
|
|
Packit |
9f0df5 |
block[1] = pcb;
|
|
Packit |
9f0df5 |
block[2] = len;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (len)
|
|
Packit |
9f0df5 |
memcpy(block + 3, ct_buf_head(bp), len);
|
|
Packit |
9f0df5 |
if (lenp)
|
|
Packit |
9f0df5 |
*lenp = len;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
len = t1_compute_checksum(t1, block, len + 3);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* memorize the last sent block */
|
|
Packit |
9f0df5 |
/* only 4 bytes since we are only interesed in R-blocks */
|
|
Packit |
9f0df5 |
memcpy(t1->previous_block, block, 4);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return len;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static unsigned int
|
|
Packit |
9f0df5 |
t1_rebuild(t1_state_t *t1, unsigned char *block)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
unsigned char pcb = t1 -> previous_block[1];
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* copy the last sent block */
|
|
Packit |
9f0df5 |
if (T1_R_BLOCK == t1_block_type(pcb))
|
|
Packit |
9f0df5 |
memcpy(block, t1 -> previous_block, 4);
|
|
Packit |
9f0df5 |
else
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL2("previous block was not R-Block: %02X", pcb);
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return 4;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Build/verify checksum
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
static unsigned int t1_compute_checksum(t1_state_t * t1,
|
|
Packit |
9f0df5 |
unsigned char *data, size_t len)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
return len + t1->checksum(data, len, data + len);
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
static int t1_verify_checksum(t1_state_t * t1, unsigned char *rbuf,
|
|
Packit |
9f0df5 |
size_t len)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
unsigned char csum[2];
|
|
Packit |
9f0df5 |
int m, n;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
m = len - t1->rc_bytes;
|
|
Packit |
9f0df5 |
n = t1->rc_bytes;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (m < 0)
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
t1->checksum(rbuf, m, csum);
|
|
Packit |
9f0df5 |
if (!memcmp(rbuf + m, csum, n))
|
|
Packit |
9f0df5 |
return 1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return 0;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/*
|
|
Packit |
9f0df5 |
* Send/receive block
|
|
Packit |
9f0df5 |
*/
|
|
Packit |
9f0df5 |
static int t1_xcv(t1_state_t * t1, unsigned char *block, size_t slen,
|
|
Packit |
9f0df5 |
size_t rmax)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
int n;
|
|
Packit |
9f0df5 |
_ccid_descriptor *ccid_desc ;
|
|
Packit |
9f0df5 |
int oldReadTimeout;
|
|
Packit |
9f0df5 |
unsigned int rmax_int;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
DEBUG_XXD("sending: ", block, slen);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
ccid_desc = get_ccid_descriptor(t1->lun);
|
|
Packit |
9f0df5 |
oldReadTimeout = ccid_desc->readTimeout;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (t1->wtx > 1)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
/* set the new temporary timeout at WTX card request */
|
|
Packit |
9f0df5 |
ccid_desc->readTimeout *= t1->wtx;
|
|
Packit |
9f0df5 |
DEBUG_INFO2("New timeout at WTX request: %d sec",
|
|
Packit |
9f0df5 |
ccid_desc->readTimeout);
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (isCharLevel(t1->lun))
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
rmax = 3;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
n = CCID_Transmit(t1 -> lun, slen, block, rmax, t1->wtx);
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* the second argument of CCID_Receive() is (unsigned int *)
|
|
Packit |
9f0df5 |
* so we can't use &rmax since &rmax is a (size_t *) and may not
|
|
Packit |
9f0df5 |
* be the same on 64-bits architectures for example (iMac G5) */
|
|
Packit |
9f0df5 |
rmax_int = rmax;
|
|
Packit |
9f0df5 |
n = CCID_Receive(t1 -> lun, &rmax_int, block, NULL);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (n == IFD_PARITY_ERROR)
|
|
Packit |
9f0df5 |
return -2;
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
rmax = block[2] + 1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
n = CCID_Transmit(t1 -> lun, 0, block, rmax, t1->wtx);
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
rmax_int = rmax;
|
|
Packit |
9f0df5 |
n = CCID_Receive(t1 -> lun, &rmax_int, &block[3], NULL);
|
|
Packit |
9f0df5 |
rmax = rmax_int;
|
|
Packit |
9f0df5 |
if (n == IFD_PARITY_ERROR)
|
|
Packit |
9f0df5 |
return -2;
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
n = rmax + 3;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
else
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
n = CCID_Transmit(t1 -> lun, slen, block, 0, t1->wtx);
|
|
Packit |
9f0df5 |
t1->wtx = 0; /* reset to default value */
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Get the response en bloc */
|
|
Packit |
9f0df5 |
rmax_int = rmax;
|
|
Packit |
9f0df5 |
n = CCID_Receive(t1 -> lun, &rmax_int, block, NULL);
|
|
Packit |
9f0df5 |
rmax = rmax_int;
|
|
Packit |
9f0df5 |
if (n == IFD_PARITY_ERROR)
|
|
Packit |
9f0df5 |
return -2;
|
|
Packit |
9f0df5 |
if (n != IFD_SUCCESS)
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
n = rmax;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (n >= 0)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
int m;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
m = block[2] + 3 + t1->rc_bytes;
|
|
Packit |
9f0df5 |
if (m < n)
|
|
Packit |
9f0df5 |
n = m;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (n >= 0)
|
|
Packit |
9f0df5 |
DEBUG_XXD("received: ", block, n);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Restore initial timeout */
|
|
Packit |
9f0df5 |
ccid_desc->readTimeout = oldReadTimeout;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
return n;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
int t1_negotiate_ifsd(t1_state_t * t1, unsigned int dad, int ifsd)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
ct_buf_t sbuf;
|
|
Packit |
9f0df5 |
unsigned char sdata[T1_BUFFER_SIZE];
|
|
Packit |
9f0df5 |
unsigned int slen;
|
|
Packit |
9f0df5 |
unsigned int retries;
|
|
Packit |
9f0df5 |
size_t snd_len;
|
|
Packit |
9f0df5 |
int n;
|
|
Packit |
9f0df5 |
unsigned char snd_buf[1];
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
retries = t1->retries;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* S-block IFSD request */
|
|
Packit |
9f0df5 |
snd_buf[0] = ifsd;
|
|
Packit |
9f0df5 |
snd_len = 1;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Initialize send/recv buffer */
|
|
Packit |
9f0df5 |
ct_buf_set(&sbuf, (void *)snd_buf, snd_len);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
while (TRUE)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
/* Build the block */
|
|
Packit |
9f0df5 |
slen = t1_build(t1, sdata, 0, T1_S_BLOCK | T1_S_IFS, &sbuf, NULL);
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* Send the block */
|
|
Packit |
9f0df5 |
n = t1_xcv(t1, sdata, slen, sizeof(sdata));
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
retries--;
|
|
Packit |
9f0df5 |
/* ISO 7816-3 Rule 7.4.2 */
|
|
Packit |
9f0df5 |
if (retries <= 0)
|
|
Packit |
9f0df5 |
goto error;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if (-1 == n)
|
|
Packit |
9f0df5 |
{
|
|
Packit |
9f0df5 |
DEBUG_CRITICAL("fatal: transmit/receive failed");
|
|
Packit |
9f0df5 |
goto error;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
if ((-2 == n) /* Parity error */
|
|
Packit |
9f0df5 |
|| (sdata[DATA] != ifsd) /* Wrong ifsd received */
|
|
Packit |
9f0df5 |
|| (sdata[NAD] != swap_nibbles(dad)) /* wrong NAD */
|
|
Packit |
9f0df5 |
|| (!t1_verify_checksum(t1, sdata, n)) /* checksum failed */
|
|
Packit |
9f0df5 |
|| (n != 4 + (int)t1->rc_bytes) /* wrong frame length */
|
|
Packit |
9f0df5 |
|| (sdata[LEN] != 1) /* wrong data length */
|
|
Packit |
9f0df5 |
|| (sdata[PCB] != (T1_S_BLOCK | T1_S_RESPONSE | T1_S_IFS))) /* wrong PCB */
|
|
Packit |
9f0df5 |
continue;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
/* no more error */
|
|
Packit |
9f0df5 |
goto done;
|
|
Packit |
9f0df5 |
}
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
done:
|
|
Packit |
9f0df5 |
return n;
|
|
Packit |
9f0df5 |
|
|
Packit |
9f0df5 |
error:
|
|
Packit |
9f0df5 |
t1->state = DEAD;
|
|
Packit |
9f0df5 |
return -1;
|
|
Packit |
9f0df5 |
}
|