Commit e81cc4a3 authored by Ludovic Rousseau's avatar Ludovic Rousseau

public release


git-svn-id: svn://anonscm.debian.org/svn/pcsclite/trunk/HandlerTest@268 0ce88b0d-b2fd-0310-8134-9614164e65ea
parents
# $Id$
CFLAGS += -O2 -g -Wall
# -ldl to use ldopen(), etc.
# -rdynamic so that the driver can use debug_msg()
LDFLAGS = -ldl -rdynamic
targets = handler_test.o debug.o tokenparser.o
all: handler_test
handler_test: $(targets)
$(CC) $(LDFLAGS) -o $@ $^
clean:
rm -f .dependencies
touch .dependencies
rm -f handler_test $(targets)
distclean: clean
rm -f dep_stamp
rm -f tags
ctags:
ctags-exuberant *.c *.h
dep: dep_stamp
dep_stamp:
@echo "Making dependencies..."
makedepend -f - -I. *.c > .dependencies
touch dep_stamp
.PHONY: all clean distclean ctags dep
include .dependencies
- Support T=1 or T=x on the user request
$Id$
/*
* $Id$
* GCdebug.c: log (or not) messages
* Copyright (C) 2001 Ludovic Rousseau <ludovic.rousseau@free.fr>
*
* License: this code is under a double licence COPYING.BSD and COPYING.GPL
*
*/
#include "debug.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#define DEBUG_BUF_SIZE (256*3+30)
static char DebugBuffer[DEBUG_BUF_SIZE];
void debug_msg(char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
vsnprintf(DebugBuffer, DEBUG_BUF_SIZE, fmt, argptr);
va_end(argptr);
fprintf(stderr, "%s\n", DebugBuffer);
} /* debug_msg */
void debug_xxd(const char *msg, const unsigned char *buffer, const int len)
{
int i;
unsigned char *c, *debug_buf_end;
debug_buf_end = DebugBuffer + DEBUG_BUF_SIZE - 5;
strncpy(DebugBuffer, msg, sizeof(DebugBuffer)-1);
c = DebugBuffer + strlen(DebugBuffer);
for (i = 0; (i < len) && (c < debug_buf_end); ++i)
{
sprintf(c, "%02X ", buffer[i]);
c += strlen(c);
}
fprintf(stderr, "%s\n", DebugBuffer);
} /* debug_xxd */
/*
* $Id$
* gcdebug.h: log (or not) messages using syslog
* Copyright (C) 2001 Ludovic Rousseau <ludovic.rousseau@free.fr>
*
* License: this code is under a double licence COPYING.BSD and COPYING.GPL
*
*/
/*
* DEBUG_CRITICAL("text");
* print "text" is DEBUG_LEVEL_CRITICAL and DEBUG_STDERR is defined
* send "text" to syslog if DEBUG_LEVEL_CRITICAL is defined
*
* DEBUG_CRITICAL2("text: %d", 1234)
* print "text: 1234" is DEBUG_LEVEL_CRITICAL and DEBUG_STDERR is defined
* send "text: 1234" to syslog if DEBUG_LEVEL_CRITICAL is defined
* the format string can be anything printf() can understand
*
* same thing for DEBUG_INFO and DEBUG_COMM
*
* DEBUG_XXD(msg, buffer, size) is only defined if DEBUG_LEVEL_COMM if defined
*
*/
#ifndef _GCDEBUG_H_
#define _GCDEBUG_H_
#define DEBUG(fmt) debug_msg("%s:%d " fmt, __FILE__, __LINE__)
#define DEBUG2(fmt, data) debug_msg("%s:%d " fmt, __FILE__, __LINE__, data)
#define DEBUG3(fmt, data1, data2) debug_msg("%s:%d " fmt, __FILE__, __LINE__, data1, data2)
void debug_msg(char *fmt, ...);
void debug_xxd(const char *msg, const unsigned char *buffer, const int size);
#endif
.TH "handler_test" "1" "August 2003" "User Commands"
.SH "NAME"
.LP
handler_test \- PC/SC-lite smart card reader driver tester
.SH "SYNTAX"
.B handler_test
libname [ channel ]
.B handler_test
[ channel ]
.SH "DESCRIPTION"
handler_test is used to test and find bugs in PC/SC-lite smart card
reader drivers.
USB reader drivers do not use channel. If omitted the parameter will be
set to 0.
The driver to use is dynamically loaded. You can define the LIB
environment variable to the value of libname. You will then avoid to do
'set args ../../ccid-0.1.0/src/libccid.so' at each start of gdb(1).
.SH OPTIONS
.TP
.B libname
name of the library driver to use
.TP
.B channel
channel number to communicate to the driver
For serial reader drivers the channel is the serial port to use. 1 ->
/dev/pcsc/1, 2 -> /dev/pcsc/2, etc.
.SH "EXAMPLES"
handler_test /usr/lib/pcsc/drivers/serial/libGemPC410.so 2
To use the libGemPC410 driver on channel 2 (/dev/pcsc/2)
export LIB=../../ccid-0.1.0/src/libccid.so
handler_test
to avoid using command line arguments.
.SH "AUTHORS"
This manual page was written by Ludovic Rousseau <ludovic.rousseau@free.fr>
.SH "SEE ALSO"
.BR pcscd (8),
/*
handler_test.c: main function used for IFDH debug
Copyright (C) 2001-2003 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$
*/
#include <stdio.h>
#include <unistd.h>
#include <winscard.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "ifdhandler.h"
#include "debug.h"
#define LUN 0
#define ENV_LIBNAME "LIB"
int handler_test(int lun, int channel);
void pcsc_error(int rv);
int exchange(char *text, DWORD lun, SCARD_IO_HEADER SendPci,
PSCARD_IO_HEADER RecvPci,
UCHAR s[], DWORD s_length,
UCHAR r[], PDWORD r_length,
UCHAR e[], int e_length);
#define DLSYM(func) f.func = dlsym(lib_handle, "" # func); \
if (f.func == NULL) { \
DEBUG("dlsym: " # func); \
return 1; }
#define COMPARE(r, e, l) { int i; \
for (i=0; i<l; i++) \
if (r[i] != e[i]) \
{ \
printf("ERROR byte %d: expected 0x%02X, got 0x%02X\n", i, e[i], r[i]);\
return 1; \
} \
printf("--------> OK\n"); \
}
struct f_t {
RESPONSECODE (*IFDHCreateChannel)(DWORD, DWORD);
RESPONSECODE (*IFDHCloseChannel)(DWORD);
//RESPONSECODE IFDHGetCapabilities ( DWORD, DWORD, PDWORD, PUCHAR );
//RESPONSECODE IFDHSetCapabilities ( DWORD, DWORD, DWORD, PUCHAR );
//RESPONSECODE IFDHSetProtocolParameters ( DWORD, DWORD, UCHAR, UCHAR, UCHAR, UCHAR );
RESPONSECODE (*IFDHPowerICC)(DWORD, DWORD, PUCHAR, PDWORD);
RESPONSECODE (*IFDHTransmitToICC)(DWORD, SCARD_IO_HEADER, PUCHAR,
DWORD, PUCHAR, PDWORD,
PSCARD_IO_HEADER);
//RESPONSECODE IFDHControl ( DWORD, PUCHAR, DWORD, PUCHAR, PDWORD );
RESPONSECODE (*IFDHICCPresence)(DWORD);
};
struct f_t f = { NULL, NULL, NULL, NULL, NULL };
int main(int argc, char *argv[])
{
void *lib_handle = NULL;
int ret;
int channel = 0;
char *driver;
driver = getenv(ENV_LIBNAME);
if (driver == NULL)
{
if (!(argc == 2 || argc == 3))
{
printf("Usage: %s libname [channel]\n", argv[0]);
printf("example: %s /usr/lib/pcsc/drivers/serial/libGemPC410.so 2\n",
argv[0]);
printf(" to load the libGemPC410 and use /dev/pcsc/2\n");
return 1;
}
// driver
driver = argv[1];
// channel
if (argc == 3)
channel = atoi(argv[2]);
}
else
{
// channel
if (argc == 2)
channel = atoi(argv[1]);
}
lib_handle = dlopen(driver, RTLD_LAZY);
if (lib_handle == NULL)
{
DEBUG2("dlopen: %s", dlerror());
return 1;
}
DLSYM(IFDHCreateChannel)
DLSYM(IFDHCloseChannel)
DLSYM(IFDHPowerICC)
DLSYM(IFDHTransmitToICC)
DLSYM(IFDHICCPresence)
ret = handler_test(LUN, channel);
dlclose(lib_handle);
return ret;
} /* main */
int handler_test(int lun, int channel)
{
int rv, i, len_i, len_o;
UCHAR atr[MAX_ATR_SIZE];
DWORD atrlength;
UCHAR s[MAX_BUFFER_SIZE], r[MAX_BUFFER_SIZE];
DWORD dwSendLength, dwRecvLength;
SCARD_IO_HEADER SendPci, RecvPci;
UCHAR e[MAX_BUFFER_SIZE]; // expected result
int e_length; // expected result length
char *text = NULL;
rv = f.IFDHCreateChannel(lun, channel);
if (rv != IFD_SUCCESS)
{
printf("IFDHCreateChannel: %d\n", rv);
return 1;
}
rv = f.IFDHICCPresence(LUN);
pcsc_error(rv);
rv = f.IFDHPowerICC(LUN, IFD_RESET, atr, &atrlength);
if (rv != IFD_SUCCESS)
{
printf("IFDHPowerICC: %d\n", rv);
goto end;
}
debug_xxd("ATR: ", atr, atrlength);
rv = f.IFDHICCPresence(LUN);
pcsc_error(rv);
memset(&SendPci, 0, sizeof(SendPci));
memset(&RecvPci, 0, sizeof(RecvPci));
/* Select applet */
text = "Select applet: ";
s[0] = 0x00;
s[1] = 0xA4;
s[2] = 0x04;
s[3] = 0x00;
s[4] = 0x06;
s[5] = 0xA0;
s[6] = 0x00;
s[7] = 0x00;
s[8] = 0x00;
s[9] = 0x18;
s[10] = 0xFF;
dwSendLength = 11;
dwRecvLength = sizeof(r);
e[0] = 0x90;
e[1] = 0x00;
e_length = 2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 1, APDU */
text = "Case 1, APDU: CLA INS P1 P2, L(Cmd) = 4";
s[0] = 0x80;
s[1] = 0x21;
s[2] = 0x00;
s[3] = 0x00;
dwSendLength = 4;
dwRecvLength = sizeof(r);
e[0] = 0x90;
e[1] = 0x00;
e_length = 2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 1, TPDU */
text = "Case 1, TPDU: CLA INS P1 P2 P3 (=0), L(Cmd) = 5";
s[0] = 0x80;
s[1] = 0x21;
s[2] = 0x00;
s[3] = 0x00;
s[4] = 0x00;
dwSendLength = 5;
dwRecvLength = sizeof(r);
e[0] = 0x90;
e[1] = 0x00;
e_length = 2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 2 */
/*
* 248 (0xF8) is max size for one USB or GBP paquet
* 255 (0xFF) maximum, 1 minimum
*/
text = "Case 2: CLA INS P1 P2 Le, L(Cmd) = 5";
len_i = 255;
s[0] = 0x80;
s[1] = 0x22;
s[2] = 0x00;
s[3] = 0x00;
s[4] = len_i;
for (i=0; i<len_i; i++)
s[5+i] = i;
dwSendLength = len_i + 5;
dwRecvLength = sizeof(r);
e[0] = 0x90;
e[1] = 0x00;
e_length = 2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 3 */
/*
* 252 (0xFC) is max size for one USB or GBP paquet
* 256 (0x100) maximum, 1 minimum
*/
text = "Case 3: CLA INS P1 P2 Lc Data, L(Cmd) = 5 + Lc";
len_o = 256;
s[0] = 0x80;
s[1] = 0x23;
if (len_o > 255)
{
s[2] = 0x01;
s[3] = len_o-256;
}
else
{
s[2] = 0x00;
s[3] = len_o;
}
s[4] = len_o;
dwSendLength = 5;
dwRecvLength = sizeof(r);
for (i=0; i<len_o; i++)
e[i] = i;
e[i++] = 0x90;
e[i++] = 0x00;
e_length = len_o+2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 4, TPDU */
/*
* len_i
* 248 (0xF8) is max size for one USB or GBP paquet
* 255 (0xFF) maximum, 1 minimum
*
* len_o
* 252 (0xFC) is max size for one USB or GBP paquet
* 256 (0x100) maximum, 1 minimum
*/
text = "Case 4, TPDU: CLA INS P1 P2 Lc Data, L(Cmd) = 5 + Lc";
len_i = 2; //255;
len_o = 3; //256;
s[0] = 0x80;
s[1] = 0x24;
if (len_o > 255)
{
s[2] = 0x01;
s[3] = len_o-256;
}
else
{
s[2] = 0x00;
s[3] = len_o;
}
s[4] = len_i;
for (i=0; i<len_i; i++)
s[5+i] = i;
dwSendLength = len_i + 5;
dwRecvLength = sizeof(r);
e[0] = 0x61;
e[1] = len_o & 0xFF;
e_length = 2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Get response */
text = "Case 4, TPDU, Get response: ";
s[0] = 0x00;
s[1] = 0xC0;
s[2] = 0x00;
s[3] = 0x00;
s[4] = r[1]; /* SW2 of previous command */
dwSendLength = 5;
dwRecvLength = sizeof(r);
for (i=0; i<len_o; i++)
e[i] = i;
e[i++] = 0x90;
e[i++] = 0x00;
e_length = len_o+2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
/* Case 4, APDU */
/*
* len_i
* 248 (0xF8) is max size for one USB or GBP paquet
* 255 (0xFF) maximum, 1 minimum
*
* len_o
* 252 (0xFC) is max size for one USB or GBP paquet
* 256 (0x100) maximum, 1 minimum
*/
text = "Case 4, APDU: CLA INS P1 P2 Lc Data Le, L(Cmd) = 5 + Lc +1";
len_i = 2; //255;
len_o = 3; //256;
s[0] = 0x80;
s[1] = 0x24;
if (len_o > 255)
{
s[2] = 0x01;
s[3] = len_o-256;
}
else
{
s[2] = 0x00;
s[3] = len_o;
}
s[4] = len_i;
for (i=0; i<len_i; i++)
s[5+i] = i;
s[5+len_i] = len_o & 0xFF;
dwSendLength = len_i + 6;
dwRecvLength = sizeof(r);
for (i=0; i<len_o; i++)
e[i] = i;
e[i++] = 0x90;
e[i++] = 0x00;
e_length = len_o+2;
if (exchange(text, lun, SendPci, &RecvPci,
s, dwSendLength, r, &dwRecvLength, e, e_length))
goto end;
end:
/* Close */
rv = f.IFDHCloseChannel(LUN);
if (rv != IFD_SUCCESS)
{
printf("IFDHCloseChannel: %d\n", rv);
return 1;
}
return 0;
} /* handler_test */
void pcsc_error(int rv)
{
switch (rv)
{
case IFD_ICC_PRESENT:
DEBUG("IFD: card present");
break;
case IFD_ICC_NOT_PRESENT:
DEBUG("IFD: card _NOT_ present");
break;
case IFD_COMMUNICATION_ERROR:
DEBUG("IFD: communication error");
break;
case IFD_PROTOCOL_NOT_SUPPORTED:
DEBUG("IFD: protocol not supported");
break;
case IFD_RESPONSE_TIMEOUT:
DEBUG("IFD: response timeout");
break;
default:
DEBUG2("IFD: undocumented error: %d", rv);
}
} /* pcsc_error */
int exchange(char *text, DWORD lun, SCARD_IO_HEADER SendPci,
PSCARD_IO_HEADER RecvPci,
UCHAR s[], DWORD s_length,
UCHAR r[], PDWORD r_length,
UCHAR e[], int e_length)
{
int rv, i;
printf("\n%s\n", text);
debug_xxd("Sent: ", s, s_length);
rv = f.IFDHTransmitToICC(lun, SendPci, s, s_length, r, r_length, RecvPci);
debug_xxd("Received: ", r, *r_length);
if (rv)
{
pcsc_error(rv);
return 1;
}
for (i=0; i<e_length; i++)
if (r[i] != e[i])
{
printf("ERROR byte %d: expected 0x%02X, got 0x%02X\n", i, e[i], r[i]);
return 1;
}
printf("--------> OK\n");
return 0;
} /* exchange */
/*****************************************************************
/
/ File : ifdhandler.h
/ Author : David Corcoran <corcoran@linuxnet.com>
/ Date : June 15, 2000
/ Purpose: This provides reader specific low-level calls.
/ See http://www.linuxnet.com for more information.
/ License: See file LICENSE.BSD
/
/ $Id$
/
******************************************************************/
#ifndef _ifd_handler_h_
#define _ifd_handler_h_
#ifdef __cplusplus
extern "C" {
#endif
/* List of data structures available to ifdhandler */
typedef struct _DEVICE_CAPABILITIES {
LPSTR Vendor_Name; /* Tag 0x0100 */
LPSTR IFD_Type; /* Tag 0x0101 */
DWORD IFD_Version; /* Tag 0x0102 */
LPSTR IFD_Serial; /* Tag 0x0103 */
DWORD IFD_Channel_ID; /* Tag 0x0110 */
DWORD Asynch_Supported; /* Tag 0x0120 */
DWORD Default_Clock; /* Tag 0x0121 */
DWORD Max_Clock; /* Tag 0x0122 */
DWORD Default_Data_Rate; /* Tag 0x0123 */
DWORD Max_Data_Rate; /* Tag 0x0124 */
DWORD Max_IFSD; /* Tag 0x0125 */
DWORD Synch_Supported; /* Tag 0x0126 */
DWORD Power_Mgmt; /* Tag 0x0131 */
DWORD Card_Auth_Devices; /* Tag 0x0140 */
DWORD User_Auth_Device; /* Tag 0x0142 */
DWORD Mechanics_Supported; /* Tag 0x0150 */
DWORD Vendor_Features; /* Tag 0x0180 - 0x01F0 User Defined. */
} DEVICE_CAPABILITIES, *PDEVICE_CAPABILITIES;
typedef struct _ICC_STATE {
UCHAR ICC_Presence; /* Tag 0x0300 */
UCHAR ICC_Interface_Status; /* Tag 0x0301 */
UCHAR ATR[MAX_ATR_SIZE]; /* Tag 0x0303 */
UCHAR ICC_Type; /* Tag 0x0304 */
} ICC_STATE, *PICC_STATE;
typedef struct _PROTOCOL_OPTIONS {
DWORD Protocol_Type; /* Tag 0x0201 */
DWORD Current_Clock; /* Tag 0x0202 */
DWORD Current_F; /* Tag 0x0203 */
DWORD Current_D; /* Tag 0x0204 */
DWORD Current_N; /* Tag 0x0205 */
DWORD Current_W; /* Tag 0x0206 */
DWORD Current_IFSC; /* Tag 0x0207 */
DWORD Current_IFSD; /* Tag 0x0208 */
DWORD Current_BWT; /* Tag 0x0209 */
DWORD Current_CWT; /* Tag 0x020A */
DWORD Current_EBC; /* Tag 0x020B */
} PROTOCOL_OPTIONS, *PPROTOCOL_OPTIONS;
typedef struct _SCARD_IO_HEADER {
DWORD Protocol;
DWORD Length;
} SCARD_IO_HEADER, *PSCARD_IO_HEADER;
/* End of structure list */
/* The list of tags should be alot more but
this is all I use in the meantime */
#define TAG_IFD_ATR 0x0303
#define TAG_IFD_SLOTNUM 0x0180
#define TAG_IFD_SLOTS_NUMBER 0x0FAE
#define TAG_IFD_SIMULTANEOUS_ACCESS 0x0FAF
/* End of tag list */
/* List of defines available to ifdhandler */
#define IFD_POWER_UP 500
#define IFD_POWER_DOWN 501
#define IFD_RESET 502
#define IFD_NEGOTIATE_PTS1 1
#define IFD_NEGOTIATE_PTS2 2
#define IFD_NEGOTIATE_PTS3 4
#define IFD_SUCCESS 0
#define IFD_ERROR_TAG 600
#define IFD_ERROR_SET_FAILURE 601
#define IFD_ERROR_VALUE_READ_ONLY 602
#define IFD_ERROR_PTS_FAILURE 605
#define IFD_ERROR_NOT_SUPPORTED 606
#define IFD_PROTOCOL_NOT_SUPPORTED 607
#define IFD_ERROR_POWER_ACTION 608
#define IFD_ERROR_SWALLOW 609