[Pcsclite-cvs-commit] Drivers/ccid/src/openct LICENSE,NONE,1.1 buffer.c,NONE,1.1 buffer.h,NONE,1.1 checksum.c,NONE,1.1 checksum.h,NONE,1.1 proto-t1.c,NONE,1.1 proto-t1.h,NONE,1.1

rousseau@haydn.debian.org rousseau@haydn.debian.org


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

Added Files:
	LICENSE buffer.c buffer.h checksum.c checksum.h proto-t1.c 
	proto-t1.h 
Log Message:
use T=1 TPDU code from the OpenCT project <http://www.opensc.org/>

The state automata was greatly improved to manage "all" the possible
errors cases.


--- NEW FILE: LICENSE ---
OpenCT, a middleware framework for smart card terminals.

Copyright (c) 2003, Olaf Kirch <okir@suse.de>
Copyright (c) 2003, Andreas Jellinghaus <aj@dungeon.inka.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  * Neither the name of the authors nor the names of its contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

--- NEW FILE: buffer.c ---
/*
 * Buffer handling functions
 *
 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openct/buffer.h>

void
ct_buf_init(ct_buf_t *bp, void *mem, size_t len)
{
	memset(bp, 0, sizeof(*bp));
	bp->base = (unsigned char *) mem;
	bp->size = len;
}

void
ct_buf_set(ct_buf_t *bp, void *mem, size_t len)
{
	ct_buf_init(bp, mem, len);
	bp->tail = len;
}

void
ct_buf_clear(ct_buf_t *bp)
{
	bp->head = bp->tail = 0;
}

int
ct_buf_get(ct_buf_t *bp, void *mem, size_t len)
{
	if (len > bp->tail - bp->head)
		return -1;
	if (mem)
		memcpy(mem, bp->base + bp->head, len);
	bp->head += len;
	return len;
}

int
ct_buf_gets(ct_buf_t *bp, char *buffer, size_t size)
{
	unsigned int	n, avail;
	unsigned char	*s;

	size -= 1; /* room for NUL byte */

	/* Limit string to what we have */
	avail = bp->tail - bp->head;
	if (size > avail)
		size = avail;

	/* Look for newline */
	s = bp->base + bp->head;
	for (n = 0; n < size && s[n] != '\n'; n++)
		;

	/* Copy string (excluding newline) */
	memcpy(buffer, s, n);
	buffer[n] = '\0';

	/* And eat any characters that weren't copied
	 * (including the newline)
	 */
	while (n < avail && s[n++] != '\n')
		;
	
	bp->head += n;
	return 0;
}

int
ct_buf_put(ct_buf_t *bp, const void *mem, size_t len)
{
	if (len > bp->size - bp->tail) {
		bp->overrun = 1;
		return -1;
	}
	if (mem)
		memcpy(bp->base + bp->tail, mem, len);
	bp->tail += len;
	return len;
}

int
ct_buf_putc(ct_buf_t *bp, int byte)
{
	unsigned char	c = byte;

	return ct_buf_put(bp, &c, 1);
}

int
ct_buf_puts(ct_buf_t *bp, const char *string)
{
	return ct_buf_put(bp, string, strlen(string));
}

unsigned int
ct_buf_avail(ct_buf_t *bp)
{
	return bp->tail - bp->head;
}

unsigned int
ct_buf_tailroom(ct_buf_t *bp)
{
	return bp->size - bp->tail;
}

unsigned int
ct_buf_size(ct_buf_t *bp)
{
	return bp->size;
}

void *
ct_buf_head(ct_buf_t *bp)
{
	return bp->base + bp->head;
}

void *
ct_buf_tail(ct_buf_t *bp)
{
	return bp->base + bp->tail;
}

int
ct_buf_read(ct_buf_t *bp, int fd)
{
	unsigned int	count;
	int		n;

	ct_buf_compact(bp);

	count = bp->size - bp->tail;
	if ((n = read(fd, bp->base + bp->tail, count)) < 0)
		return -1;
	bp->tail += n;
	return 0;
}

void
ct_buf_compact(ct_buf_t *bp)
{
	unsigned int	count;

	if (bp->head == 0)
		return;

	count = bp->tail - bp->head;
	memmove(bp->base, bp->base + bp->head, count);
	bp->tail -= bp->head;
	bp->head  = 0;
}

int
ct_buf_overrun(ct_buf_t *bp)
{
	return bp->overrun;
}

--- NEW FILE: buffer.h ---
/*
 * Buffer handling functions of the IFD handler library
 *
 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
 */

#ifndef OPENCT_BUFFER_H
#define OPENCT_BUFFER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/types.h>

typedef struct ct_buf {
	unsigned char *		base;
	unsigned int		head, tail, size;
	unsigned int		overrun;
} ct_buf_t;

extern void		ct_buf_init(ct_buf_t *, void *, size_t);
extern void		ct_buf_set(ct_buf_t *, void *, size_t);
extern void		ct_buf_clear(ct_buf_t *);
extern int		ct_buf_get(ct_buf_t *, void *, size_t);
extern int		ct_buf_gets(ct_buf_t *, char *, size_t);
extern int		ct_buf_put(ct_buf_t *, const void *, size_t);
extern int		ct_buf_putc(ct_buf_t *, int);
extern int		ct_buf_puts(ct_buf_t *, const char *);
extern unsigned int	ct_buf_avail(ct_buf_t *);
extern unsigned int	ct_buf_tailroom(ct_buf_t *);
extern unsigned int	ct_buf_size(ct_buf_t *);
extern void *		ct_buf_head(ct_buf_t *);
extern void *		ct_buf_tail(ct_buf_t *);
extern int		ct_buf_read(ct_buf_t *, int);
extern void		ct_buf_compact(ct_buf_t *);
extern int		ct_buf_overrun(ct_buf_t *);

#ifdef __cplusplus
}
#endif

#endif /* OPENCT_BUFFER_H */

--- NEW FILE: checksum.c ---
/*
 * Checksum handling
 *
 * Copyright Matthias Bruestle 1999-2002
 * For licensing, see the file LICENCE
 */

#include <stdint.h>
#include <unistd.h>

#define min( a, b )   ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )

/* ISO STD 3309 */
/* From: medin@catbyte.b30.ingr.com (Dave Medin)
 * Subject: CCITT checksums
 * Newsgroups: sci.electronics
 * Date: Mon, 7 Dec 1992 17:33:39 GMT
 */

/* Correct Table? */

static unsigned short crctab[256] = {
	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};

/*
 * Returns LRC of data.
 */
unsigned int
csum_lrc_compute(const uint8_t *in, size_t len, unsigned char *rc)
{
	unsigned char	lrc = 0;

	while (len--)
		lrc ^= *in++;

	if (rc)
		*rc = lrc;
	return 1;
}

/*
 * Compute CRC of data.
 */
unsigned int
csum_crc_compute(const uint8_t * data, size_t len, unsigned char *rc)
{
	unsigned short v = 0xFFFF;

	while (len--) {
		v = ((v >> 8) & 0xFF) ^ crctab[(v ^ *data++) & 0xFF];
	}

	if (rc) {
		rc[0] = (v >> 8) & 0xFF;
		rc[1] = v & 0xFF;
	}

	return 2;
}


--- NEW FILE: checksum.h ---
/*
    proto-t1.h: header file for proto-t1.c
    Copyright (C) 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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* $Id: checksum.h,v 1.1 2004/06/30 09:37:08 rousseau Exp $ */

#ifndef __CHECKSUM_H__
#define __CHECKSUM_H__

#include <unistd.h>

extern unsigned int	csum_lrc_compute(const unsigned char *, size_t, unsigned char *);
extern unsigned int	csum_crc_compute(const unsigned char *, size_t, unsigned char *);

#endif


--- NEW FILE: proto-t1.c ---
/*
 * Implementation of T=1
 *
 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
 */

#include <PCSC/pcsclite.h>
#include <PCSC/ifdhandler.h>
#include "commands.h"
#include "buffer.h"
#include "debug.h"
#include "proto-t1.h"
#include "checksum.h"

#include <sys/poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

/* T=1 protocol constants */
#define T1_I_BLOCK		0x00
#define T1_R_BLOCK		0x80
#define T1_S_BLOCK		0xC0
#define T1_MORE_BLOCKS		0x20

/* I block */
#define T1_I_SEQ_SHIFT		6

/* R block */
#define T1_IS_ERROR(pcb)	((pcb) & 0x0F)
#define T1_EDC_ERROR		0x01
#define T1_OTHER_ERROR		0x02
#define T1_R_SEQ_SHIFT		4

/* S block stuff */
#define T1_S_IS_RESPONSE(pcb)	((pcb) & T1_S_RESPONSE)
#define T1_S_TYPE(pcb)		((pcb) & 0x0F)
#define T1_S_RESPONSE		0x20
#define T1_S_RESYNC		0x00
#define T1_S_IFS		0x01
#define T1_S_ABORT		0x02
#define T1_S_WTX		0x03

#define swap_nibbles(x) ( (x >> 4) | ((x & 0xF) << 4) )

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define NAD 0
#define PCB 1
#define LEN 2
#define DATA 3

/* internal state, do not mess with it. */
/* should be != DEAD after reset/init */
enum {
	SENDING, RECEIVING, RESYNCH, DEAD
};

static void		t1_set_checksum(t1_state_t *, int);
static unsigned	int	t1_block_type(unsigned char);
static unsigned int	t1_seq(unsigned char);
static unsigned	int	t1_build(t1_state_t *, unsigned char *,
				unsigned char, unsigned char,
				ct_buf_t *, size_t *);
unsigned int	t1_rebuild(t1_state_t *t1, unsigned char *block);
static unsigned int	t1_compute_checksum(t1_state_t *,
				unsigned char *, size_t);
static int		t1_verify_checksum(t1_state_t *, unsigned char *,
				size_t);
static int		t1_xcv(t1_state_t *, unsigned char *, size_t, size_t);

/*
 * Set default T=1 protocol parameters
 */
static void
t1_set_defaults(t1_state_t *t1)
{
	t1->retries  = 3;
	/* This timeout is rather insane, but we need this right now
	 * to support cryptoflex keygen */
	t1->ifsc     = 32;
	t1->ifsd     = 32;
	t1->nr	     = 0;
	t1->ns	     = 0;
	t1->wtx	     = 0;
}

void
t1_set_checksum(t1_state_t *t1, int csum)
{
	switch (csum) {
	case IFD_PROTOCOL_T1_CHECKSUM_LRC:
		t1->rc_bytes = 1;
		t1->checksum = csum_lrc_compute;
		break;
	case IFD_PROTOCOL_T1_CHECKSUM_CRC:
		t1->rc_bytes = 2;
		t1->checksum = csum_crc_compute;
		break;
	}
}

/*
 * Attach t1 protocol
 */
int
t1_init(t1_state_t *t1)
{
	t1_set_defaults(t1);
	t1_set_param(t1, IFD_PROTOCOL_T1_CHECKSUM_LRC, 0);
	t1_set_param(t1, IFD_PROTOCOL_STATE, SENDING);
	t1_set_param(t1, IFD_PROTOCOL_MORE, FALSE);

	return 0;
}

/*
 * Detach t1 protocol
 */
void
t1_release(t1_state_t *t1)
{
	/* NOP */
}

/*
 * Get/set parmaters for T1 protocol
 */
int
t1_set_param(t1_state_t *t1, int type, long value)
{
	switch (type) {
	case IFD_PROTOCOL_T1_CHECKSUM_LRC:
	case IFD_PROTOCOL_T1_CHECKSUM_CRC:
		t1_set_checksum(t1, type);
		break;
	case IFD_PROTOCOL_T1_IFSC:
		t1->ifsc = value;
		break;
	case IFD_PROTOCOL_T1_IFSD:
		t1->ifsd = value;
		break;
	case IFD_PROTOCOL_STATE:
		t1->state = value;
		break;
	case IFD_PROTOCOL_MORE:
		t1->more = value;
		break;
	default:
		DEBUG_INFO2("Unsupported parameter %d", type);
		return -1;
	}

	return 0;
}

/*
 * Send an APDU through T=1
 */
int
t1_transceive(t1_state_t *t1, int dad,
		const void *snd_buf, size_t snd_len,
		void *rcv_buf, size_t rcv_len)
{
	ct_buf_t	sbuf, rbuf, tbuf;
	unsigned char	sdata[T1_BUFFER_SIZE], sblk[5];
	unsigned int	slen, retries, resyncs, sent_length = 0;
	size_t		last_send = 0;

	if (snd_len == 0)
		return -1;

	/* we can't talk to a dead card / reader. Reset it! */
	if (t1->state == DEAD)
		return -1;

	t1->state = SENDING;
	retries = t1->retries;
	resyncs = 3;

	/* Initialize send/recv buffer */
	ct_buf_set(&sbuf, (void *) snd_buf, snd_len);
	ct_buf_init(&rbuf, rcv_buf, rcv_len);

	/* Send the first block */
	slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send);

	while (1) {
		unsigned char	pcb;
		int		n;

		retries--;

		n = t1_xcv(t1, sdata, slen, sizeof(sdata));
		if (-2 == n)
		{
			DEBUG_COMM("Parity error");
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
					dad, T1_R_BLOCK | T1_EDC_ERROR,
					NULL, NULL);
			continue;
		}

		if (n < 0) {
			DEBUG_CRITICAL("fatal: transmit/receive failed");
			t1->state = DEAD;
			goto error;
		}

		if ((sdata[NAD] != swap_nibbles(dad)) /* wrong NAD */
			|| (sdata[LEN] == 0xFF))	/* length == 0xFF (illegal) */
		{
			DEBUG_COMM("R-BLOCK required");
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
				dad, T1_R_BLOCK | T1_OTHER_ERROR,
				NULL, NULL);
			continue;
		}

		if (!t1_verify_checksum(t1, sdata, n)) {
			DEBUG_COMM("checksum failed");
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
				dad, T1_R_BLOCK | T1_EDC_ERROR,
				NULL, NULL);
			continue;
		}

		pcb = sdata[PCB];
		switch (t1_block_type(pcb)) {
		case T1_R_BLOCK:
#if 0
			if (T1_IS_ERROR(pcb)) {
				DEBUG_COMM2("received error block, err=%d",
					     T1_IS_ERROR(pcb));
				goto resync;
			}
#endif
			if ((sdata[LEN] != 0x00)	/* length != 0x00 (illegal) */
				|| ((t1_seq(pcb) != t1->nr)	/* wrong sequence number & no bit more */
					&& ! t1->more)
				|| (pcb & 0x20) /* b6 of pcb is set */
			   )
			{
				DEBUG_COMM4("received: %d, expected: %d, more: %d",
					t1_seq(pcb), t1->ns, t1->more);

				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				if (T1_I_BLOCK == t1_block_type(t1->previous_block[1]))
				{
					DEBUG_COMM("repeat I-Block");
					slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
						&sbuf, &last_send);
					continue;
				}
				else
				{
					DEBUG_COMM("R-Block required");
					if (retries == 0 || sent_length)
						goto resync;
					slen = t1_build(t1, sdata,
							dad, T1_R_BLOCK | T1_OTHER_ERROR,
							NULL, NULL);
					continue;
				}
			}

			if (t1->state == RECEIVING) {
				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK,
						NULL, NULL);
				break;
			}

			/* If the card terminal requests the next
			 * sequence number, it received the previous
			 * block successfully */
			if (t1_seq(pcb) != t1->ns) {
				ct_buf_get(&sbuf, NULL, last_send);
				sent_length += last_send;
				last_send = 0;
				t1->ns ^= 1;
			}

			/* If there's no data available, the ICC
			 * shouldn't be asking for more */
			if (ct_buf_avail(&sbuf) == 0)
				goto resync;

			slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
					&sbuf, &last_send);
			break;

		case T1_I_BLOCK:
			/* The first I-block sent by the ICC indicates
			 * the last block we sent was received successfully. */
			if (t1->state == SENDING) {
				DEBUG_COMM("");
				ct_buf_get(&sbuf, NULL, last_send);
				last_send = 0;
				t1->ns ^= 1;
			}

			t1->state = RECEIVING;

			/* If the block sent by the card doesn't match
			 * what we expected it to send, reply with
			 * an R block */
			if (t1_seq(pcb) != t1->nr) {
				DEBUG_COMM("wrong nr");
				slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			t1->nr ^= 1;

			if (ct_buf_put(&rbuf, sdata + 3, sdata[LEN]) < 0)
				goto error;

			if ((pcb & T1_MORE_BLOCKS) == 0)
				goto done;

			slen = t1_build(t1, sdata, dad, T1_R_BLOCK, NULL, NULL);
			break;

		case T1_S_BLOCK:
			if (T1_S_IS_RESPONSE(pcb) && t1->state == RESYNCH) {
				DEBUG_COMM("S-Block answer received");
				t1->state = SENDING;
				sent_length =0;
				last_send = 0;
				resyncs = 3;
				retries = t1->retries;
				ct_buf_init(&rbuf, rcv_buf, rcv_len);
				slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
						&sbuf, &last_send);
				continue;
			}

			if (T1_S_IS_RESPONSE(pcb))
			{
				if (retries == 0)
					goto resync;

				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				DEBUG_CRITICAL("wrong response S-BLOCK received");
				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			ct_buf_init(&tbuf, sblk, sizeof(sblk));

			DEBUG_COMM("S-Block request received");
			switch (T1_S_TYPE(pcb)) {
			case T1_S_RESYNC:
				if (sdata[LEN] != 0)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_COMM("Resync requested");
				/* the card is not allowed to send a resync. */
				goto resync;
				break;
			case T1_S_ABORT:
				if (sdata[LEN] != 0)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_CRITICAL("abort requested");
				goto resync;
			case T1_S_IFS:
				if (sdata[LEN] != 1)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_CRITICAL2("CT sent S-block with ifs=%u", sdata[DATA]);
				if (sdata[DATA] == 0) 
					goto resync;
				t1->ifsc = sdata[DATA];
				ct_buf_putc(&tbuf, sdata[DATA]);
				break;
			case T1_S_WTX:
				if (sdata[LEN] != 1)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_CRITICAL2("CT sent S-block with wtx=%u", sdata[DATA]);
				t1->wtx = sdata[DATA];
				ct_buf_putc(&tbuf, sdata[DATA]);
				break;
			default:
				DEBUG_CRITICAL2("T=1: Unknown S block type 0x%02x", T1_S_TYPE(pcb));
				goto resync;
			}

			slen = t1_build(t1, sdata, dad,
				T1_S_BLOCK | T1_S_RESPONSE | T1_S_TYPE(pcb),
				&tbuf, NULL);
		}

		/* Everything went just splendid */
		retries = t1->retries;
		continue;

resync:
		/* the number or resyncs is limited, too */
		if (resyncs == 0)
			goto error;
		resyncs--;
		t1->ns = 0;
		t1->nr = 0;
		slen = t1_build(t1, sdata, dad, T1_S_BLOCK|T1_S_RESYNC, NULL,
				NULL);
		t1->state = RESYNCH;
		t1->more = FALSE;
		continue;
	}

done:
	return ct_buf_avail(&rbuf);

error:
	t1->state = DEAD;
	return -1;
}

static unsigned
t1_block_type(unsigned char pcb)
{
	switch (pcb & 0xC0) {
	case T1_R_BLOCK:
		return T1_R_BLOCK;
	case T1_S_BLOCK:
		return T1_S_BLOCK;
	default:
		return T1_I_BLOCK;
	}
}

static unsigned int
t1_seq(unsigned char pcb)
{
	switch (pcb & 0xC0) {
	case T1_R_BLOCK:
		return (pcb >> T1_R_SEQ_SHIFT) & 1;
	case T1_S_BLOCK:
		return 0;
	default:
		return (pcb >> T1_I_SEQ_SHIFT) & 1;
	}
}

unsigned int
t1_build(t1_state_t *t1, unsigned char *block,
		unsigned char dad, unsigned char pcb,
		ct_buf_t *bp, size_t *lenp)
{
	unsigned int	len;
	char more = FALSE;

	len = bp? ct_buf_avail(bp) : 0;
	if (len > t1->ifsc) {
		pcb |= T1_MORE_BLOCKS;
		len = t1->ifsc;
		more = TRUE;
	}

	/* Add the sequence number */
	switch (t1_block_type(pcb)) {
	case T1_R_BLOCK:
		pcb |= t1->nr << T1_R_SEQ_SHIFT;
		break;
	case T1_I_BLOCK:
		pcb |= t1->ns << T1_I_SEQ_SHIFT;
		t1->more = more;
		DEBUG_COMM2("more bit: %d", more);
		break;
	}

	block[0] = dad;
	block[1] = pcb;
	block[2] = len;

	if (len)
		memcpy(block + 3, ct_buf_head(bp), len);
	if (lenp)
		*lenp = len;

	len = t1_compute_checksum(t1, block, len + 3);

	/* memorize the last sent block */
	/* only 4 bytes since we are only interesed in R-blocks */
	memcpy(t1->previous_block, block, 4);

	return len;
}

unsigned int
t1_rebuild(t1_state_t *t1, unsigned char *block)
{
	unsigned char pcb = t1 -> previous_block[1];

	/* copy the last sent block */
	if (T1_R_BLOCK == t1_block_type(pcb))
		memcpy(block, t1 -> previous_block, 4);
	else
	{
		DEBUG_CRITICAL2("previous block was not R-Block: %02X", pcb);
		return 0;
	}

	return 4;
}

/*
 * Protocol struct
 */
#if 0
struct ifd_protocol_ops	ifd_protocol_t1 = {
	IFD_PROTOCOL_T1,	/* id */
	"T=1",			/* name */
	sizeof(t1_state_t),	/* size */
	t1_init,		/* init */
	t1_release,		/* release */
	t1_set_param,		/* set_param */
	t1_get_param,		/* get_param */
	t1_resynchronize,	/* resynchronize */
	t1_transceive,		/* transceive */
	NULL,			/* sync_read */
	NULL,			/* sync_write */
};
#endif

/*
 * Build/verify checksum
 */
unsigned int
t1_compute_checksum(t1_state_t *t1, unsigned char *data, size_t len)
{
	return len + t1->checksum(data, len, data + len);
}

int
t1_verify_checksum(t1_state_t *t1, unsigned char *rbuf, size_t len)
{
	unsigned char	csum[2];
	int		m, n;

	m = len - t1->rc_bytes;
	n = t1->rc_bytes;

	if (m < 0)
		return 0;

	t1->checksum(rbuf, m, csum);
	if (!memcmp(rbuf + m, csum, n))
		return 1;

	return 0;
}

/*
 * Send/receive block
 */
int
t1_xcv(t1_state_t *t1, unsigned char *block, size_t slen, size_t rmax)
{
	int		n, m;

	DEBUG_XXD("sending: ", block, slen);

	n = CCID_Transmit(t1 -> lun, slen, block, t1->wtx);
	t1->wtx = 0;	/* reset to default value */
	if (n != IFD_SUCCESS)
		return n;

	/* Get the response en bloc */
	n = CCID_Receive(t1 -> lun, &rmax, block);
	if (n == IFD_PARITY_ERROR)
		return -2;
	if (n != IFD_SUCCESS)
		return -1;

	n = rmax;
	if (n >= 0) {
		m = block[2] + 3 + t1->rc_bytes;
		if (m < n)
			n = m;
	}

	if (n >= 0)
		DEBUG_XXD("received: ", block, n);

	return n;
}

int
t1_negociate_ifsd(t1_state_t *t1, int dad, int ifsd)
{
	ct_buf_t	sbuf;
	unsigned char sdata[T1_BUFFER_SIZE];
	unsigned int slen;
	unsigned int retries;
	size_t snd_len;
	int n;
	unsigned char snd_buf[1];

	retries = t1->retries;

	/* S-block IFSD request */
	snd_buf[0] = ifsd;
	snd_len = 1;

	/* Initialize send/recv buffer */
	ct_buf_set(&sbuf, (void *) snd_buf, snd_len);

	while (TRUE)
	{
		/* Build the block */
		slen = t1_build(t1, sdata, 0, T1_S_BLOCK | T1_S_IFS, &sbuf, NULL);

		/* Send the block */
		n = t1_xcv(t1, sdata, slen, sizeof(sdata));

		retries--;
		if (retries == 0)
			goto error;

		if (-1 == n)
		{
			DEBUG_CRITICAL("fatal: transmit/receive failed");
			goto error;
		}

		if ((-2 == n)								/* Parity error */
			|| (sdata[DATA] != ifsd)				/* Wrong ifsd received */
			|| (sdata[NAD] != swap_nibbles(dad))	/* wrong NAD */
			|| (!t1_verify_checksum(t1, sdata, n))	/* checksum failed */
			|| ((n != 5) || (sdata[LEN] != 1))		/* wrong length */
			|| (sdata[PCB] != (T1_S_BLOCK | T1_S_RESPONSE | T1_S_IFS))) /* wrong PCB */
			continue;

		/* no more error */
		goto done;
	}

done:
	return n;

error:
	t1->state = DEAD;
	return -1;
}


--- NEW FILE: proto-t1.h ---
/*
    proto-t1.h: header file for proto-t1.c
    Copyright (C) 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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* $Id: proto-t1.h,v 1.1 2004/06/30 09:37:08 rousseau Exp $ */

#ifndef __PROTO_T1_H__
#define __PROTO_T1_H__

#include <unistd.h>

enum {
	IFD_PROTOCOL_RECV_TIMEOUT = 0x0000,
	IFD_PROTOCOL_T1_BLOCKSIZE,
	IFD_PROTOCOL_T1_CHECKSUM_CRC,
	IFD_PROTOCOL_T1_CHECKSUM_LRC,
	IFD_PROTOCOL_T1_IFSC,
	IFD_PROTOCOL_T1_IFSD,
	IFD_PROTOCOL_STATE,
	IFD_PROTOCOL_MORE
};

#define T1_BUFFER_SIZE		(3 + 254 + 2)

typedef struct {
	int		lun;
	int		state;

	unsigned char	ns;	/* reader side */
	unsigned char	nr;	/* card side */
	unsigned int	ifsc;
	unsigned int	ifsd;

	unsigned char	wtx;
	unsigned int	retries;
	unsigned int	rc_bytes;

	unsigned int	(*checksum)(const unsigned char *,
					size_t, unsigned char *);

	char			more;	/* more data bit */
	unsigned char	previous_block[4];	/* to store the last R-block */
} t1_state_t;

int t1_transceive(t1_state_t *t1, int dad,
		const void *snd_buf, size_t snd_len,
		void *rcv_buf, size_t rcv_len);
int t1_init(t1_state_t *t1);
void t1_release(t1_state_t *t1);
int t1_set_param(t1_state_t *t1, int type, long value);
int t1_negociate_ifsd(t1_state_t *t1, int dad, int ifsd);

#endif