Files
vsmartcard/ccid/ccid.c
frankmorgner 05861588ed - switched from libpcsc to libopensc. This brings some new features:
- Support for 4 slots per reader
  - abstraction of user interface
  - interactive pin verification
  - BCD encoding of PIN
  - Support for new readers (OpenCT)
  - automatically skip to a reader with a card
- added defines
- changed interface of ccid_initialize


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@23 96b47cad-a561-4643-ad3b-153ac7d7599c
2010-01-21 21:48:01 +00:00

1072 lines
34 KiB
C

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <asm/byteorder.h>
#include <opensc/opensc.h>
#include <opensc/ui.h>
#include "ccid.h"
//static const char *app_name = "ccid";
static sc_context_t *ctx = NULL;
static sc_card_t *card_in_slot[SC_MAX_SLOTS];
static sc_reader_t *reader;
struct ccid_class_descriptor
ccid_desc = {
.bLength = sizeof ccid_desc,
.bDescriptorType = 0x21,
.bcdCCID = __constant_cpu_to_le16(0x0110),
.bMaxSlotIndex = SC_MAX_SLOTS,
.bVoltageSupport = 0x01,
.dwProtocols = __constant_cpu_to_le32(0x01| // T=0
0x02), // T=1
.dwDefaultClock = __constant_cpu_to_le32(0xDFC),
.dwMaximumClock = __constant_cpu_to_le32(0xDFC),
.bNumClockSupport = 1,
.dwDataRate = __constant_cpu_to_le32(0x2580),
.dwMaxDataRate = __constant_cpu_to_le32(0x2580),
.bNumDataRatesSupported = 1,
.dwMaxIFSD = __constant_cpu_to_le32(0xFF), // FIXME
.dwSynchProtocols = __constant_cpu_to_le32(0),
.dwMechanical = __constant_cpu_to_le32(0),
.dwFeatures = __constant_cpu_to_le32(
0x2| // Automatic parameter configuration based on ATR data
0x8| // Automatic ICC voltage selection
0x10| // Automatic ICC clock frequency change
0x20| // Automatic baud rate change
0x40| // Automatic parameters negotiation
0x80| // Automatic PPS
0x20000| // Short APDU level exchange
0x100000),// USB Wake up signaling supported
.dwMaxCCIDMessageLength = __constant_cpu_to_le32(261+10),
.bClassGetResponse = 0xFF,
.bclassEnvelope = 0xFF,
.wLcdLayout = __constant_cpu_to_le16(
//0),
0xFF00| // Number of lines for the LCD display
0x00FF), // Number of characters per line
//.bPINSupport = 0,
.bPINSupport = 0x1| // PIN Verification supported
0x2, // PIN Modification supported
.bMaxCCIDBusySlots = 0x01,
};
int ccid_initialize(int reader_id, int verbose)
{
sc_context_param_t ctx_param;
int sc_result;
int i;
memset(&ctx_param, 0, sizeof(ctx_param));
//ctx_param.ver = 0;
//ctx_param.app_name = app_name;
sc_result = sc_context_create(&ctx, &ctx_param);
if (sc_result < 0) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(sc_result));
return 0;
}
if (verbose > 1)
ctx->debug = verbose-1;
for (i = 0; i < sizeof card_in_slot; i++) {
card_in_slot[i] = NULL;
}
if (sc_ctx_get_reader_count(ctx) == 0) {
fprintf(stderr,
"No smart card readers found.\n");
return 0;
}
if (reader_id < 0) {
/* Automatically try to skip to a reader with a card if reader not specified */
for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) {
reader = sc_ctx_get_reader(ctx, i);
if (sc_detect_card_presence(reader, 0) & SC_SLOT_CARD_PRESENT) {
reader_id = i;
fprintf(stderr, "Using reader with a card: %s\n", reader->name);
goto autofound;
}
}
reader_id = 0;
}
autofound:
if ((unsigned int)reader_id >= sc_ctx_get_reader_count(ctx)) {
fprintf(stderr,
"Illegal reader number. "
"Only %d reader(s) configured.\n",
sc_ctx_get_reader_count(ctx));
return 0;
}
reader = sc_ctx_get_reader(ctx, reader_id);
ccid_desc.bMaxSlotIndex = reader->slot_count - 1;
return 1;
}
int ccid_shutdown()
{
int i;
for (i = 0; i < sizeof card_in_slot; i++) {
if (card_in_slot[i]) {
sc_unlock(card_in_slot[i]);
sc_disconnect_card(card_in_slot[i], 0);
}
}
if (ctx)
sc_release_context(ctx);
return 1;
}
int build_apdu(const __u8 *buf, size_t len, sc_apdu_t *apdu)
{
const __u8 *p;
size_t len0;
len0 = len;
if (len < 4) {
puts("APDU too short (must be at least 4 bytes)");
return 0;
}
memset(apdu, 0, sizeof(*apdu));
p = buf;
apdu->cla = *p++;
apdu->ins = *p++;
apdu->p1 = *p++;
apdu->p2 = *p++;
len -= 4;
if (len > 1) {
apdu->lc = *p++;
len--;
apdu->data = p;
apdu->datalen = apdu->lc;
if (len < apdu->lc) {
printf("APDU too short (need %lu bytes)\n",
(unsigned long) apdu->lc - len);
return 0;
}
len -= apdu->lc;
p += apdu->lc;
if (len) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 256;
len--;
apdu->cse = SC_APDU_CASE_4_SHORT;
} else {
apdu->cse = SC_APDU_CASE_3_SHORT;
}
if (len) {
printf("APDU too long (%lu bytes extra)\n",
(unsigned long) len);
return 0;
}
} else if (len == 1) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 256;
len--;
apdu->cse = SC_APDU_CASE_2_SHORT;
} else {
apdu->cse = SC_APDU_CASE_1;
}
apdu->flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
return 1;
}
int get_rapdu(sc_apdu_t *apdu, size_t slot, __u8 **buf, size_t *resplen, int *sc_result)
{
if (!apdu || !buf || !resplen || slot > sizeof card_in_slot || !sc_result)
goto err;
apdu->resplen = apdu->le;
/* Get two more bytes to later use as return buffer including sw1 and sw2 */
apdu->resp = malloc(apdu->resplen + sizeof(__u8) + sizeof(__u8));
if (!apdu->resp) {
*sc_result = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*sc_result = sc_transmit_apdu(card_in_slot[slot], apdu);
if (*sc_result < 0) {
goto err;
}
if (apdu->sw1 > 0xff || apdu->sw2 > 0xff) {
*sc_result = SC_ERROR_INVALID_DATA;
goto err;
}
apdu->resp[apdu->resplen] = apdu->sw1;
apdu->resp[apdu->resplen + sizeof(__u8)] = apdu->sw2;
*buf = apdu->resp;
*resplen = apdu->resplen + sizeof(__u8) + sizeof(__u8);
return 1;
err:
if (apdu->resp)
free(apdu->resp);
return 0;
}
__u8 get_bError(int sc_result)
{
if (sc_result < 0) {
switch (sc_result) {
case SC_SUCCESS:
return CCID_BERROR_OK;
case SC_ERROR_SLOT_ALREADY_CONNECTED:
return CCID_BERROR_CMD_SLOT_BUSY;
case SC_ERROR_KEYPAD_TIMEOUT:
return CCID_BERROR_PIN_TIMEOUT;
case SC_ERROR_KEYPAD_CANCELLED:
return CCID_BERROR_PIN_CANCELLED;
case SC_ERROR_EVENT_TIMEOUT:
case SC_ERROR_CARD_UNRESPONSIVE:
return CCID_BERROR_ICC_MUTE;
default:
return CCID_BERROR_HW_ERROR;
}
} else
return CCID_BERROR_OK;
}
__u8 get_bStatus(int sc_result, __u8 bSlot)
{
__u8 result;
if (sc_result < 0) {
if (bSlot < sizeof card_in_slot
&& card_in_slot[bSlot]
&& sc_card_valid(card_in_slot[bSlot])) {
result = CCID_BSTATUS_ERROR_ACTIVE;
} else {
if (bSlot < reader->slot_count
&& sc_detect_card_presence(reader, bSlot) & SC_SLOT_CARD_PRESENT) {
result = CCID_BSTATUS_ERROR_INACTIVE;
} else {
result = CCID_BSTATUS_ERROR_NOICC;
}
}
} else {
if (bSlot < sizeof card_in_slot
&& card_in_slot[bSlot]
&& sc_card_valid(card_in_slot[bSlot])) {
result = CCID_BSTATUS_OK_ACTIVE;
} else {
if (bSlot < reader->slot_count
&& sc_detect_card_presence(reader, bSlot) & SC_SLOT_CARD_PRESENT) {
result = CCID_BSTATUS_OK_INACTIVE;
} else {
result = CCID_BSTATUS_OK_NOICC;
}
}
}
char buf[30];
switch (result) {
case CCID_BSTATUS_OK_ACTIVE:
case CCID_BSTATUS_ERROR_ACTIVE:
sprintf(buf, "active card in slot %d", bSlot);
break;
case CCID_BSTATUS_OK_INACTIVE:
case CCID_BSTATUS_ERROR_INACTIVE:
sprintf(buf, "inactive card in slot %d", bSlot);
break;
case CCID_BSTATUS_OK_NOICC:
case CCID_BSTATUS_ERROR_NOICC:
sprintf(buf, "no card in slot %d", bSlot);
break;
}
sc_ui_display_debug(ctx, buf);
return result;
}
RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq,
int sc_result)
{
RDR_to_PC_SlotStatus_t result;
result.bMessageType = 0x81;
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(sc_result, bSlot);
result.bError = get_bError(sc_result);
result.bClockStatus = 0;
return result;
}
RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq,
int sc_result, __le32 dwLength)
{
RDR_to_PC_DataBlock_t result;
result.bMessageType = 0x80;
result.dwLength = dwLength;
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(sc_result, bSlot);
result.bError = get_bError(sc_result);
result.bChainParameter = 0;
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request)
{
if ( request.bMessageType != 0x65 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_GetSlotStatus");
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, SC_SUCCESS);
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOn(const PC_to_RDR_IccPowerOn_t request, char ** pATR)
{
if ( request.bMessageType != 0x62 ||
request.dwLength != __constant_cpu_to_le32(0) ||
!( request.bPowerSelect == 0 ||
request.bPowerSelect & ccid_desc.bVoltageSupport ) ||
request.abRFU != 0)
sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_IccPowerOn");
RDR_to_PC_SlotStatus_t result;
int sc_result;
if (!pATR)
goto err;
*pATR = NULL;
result.dwLength = __constant_cpu_to_le32(0);
if (request.bSlot > sizeof card_in_slot)
goto err;
sc_result = sc_connect_card(reader, request.bSlot,
&card_in_slot[request.bSlot]);
result = get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, sc_result);
if (sc_result < 0)
goto err;
*pATR = (char*) card_in_slot[request.bSlot]->atr;
result.dwLength = __cpu_to_le32(card_in_slot[request.bSlot]->atr_len);
err:
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request)
{
if ( request.bMessageType != 0x63 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_IccPowerOff");
if (request.bSlot > sizeof card_in_slot)
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
SC_ERROR_INVALID_DATA);
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
sc_disconnect_card(card_in_slot[request.bSlot], 0));
}
RDR_to_PC_DataBlock_t
perform_PC_to_RDR_XfrBlock(const PC_to_RDR_XfrBlock_t request, const __u8*
abDataIn, __u8** abDataOut)
{
char buf[50];
int sc_result;
size_t resplen = 0;
sc_apdu_t apdu;
if ( request.bMessageType != 0x6F ||
request.bBWI != 0)
sc_ui_display_error(ctx, "malformed PC_to_RDR_XfrBlock, will continue anyway");
if (request.bSlot > sizeof card_in_slot)
goto err;
if (!build_apdu(abDataIn, request.dwLength, &apdu)) {
sc_result = SC_ERROR_INVALID_DATA;
goto err;
}
snprintf(buf, sizeof buf, "APDU, %d byte(s):\tins=%02x p1=%02x p2=%02x lc=%02x le=%02x",
request.dwLength, apdu.ins, apdu.p1, apdu.p2, (int) apdu.lc, (int) apdu.le);
sc_ui_display_debug(ctx, buf);
if (!get_rapdu(&apdu, request.bSlot, abDataOut, &resplen, &sc_result))
goto err;
snprintf(buf, sizeof buf, "RAPDU, %d byte(s):\tsw1=%02x sw2=%02x",
(int) resplen, apdu.sw1, apdu.sw2);
sc_ui_display_debug(ctx, buf);
err:
if (sc_result < 0)
sc_ui_display_error(ctx, sc_strerror(sc_result));
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
sc_result, __cpu_to_le32(resplen));
}
RDR_to_PC_Parameters_t
get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, int sc_result, __u8
**abProtocolDataStructure)
{
RDR_to_PC_Parameters_t result;
result.bMessageType = 0x82;
result.bSlot = bSlot;
result.bSeq = bSeq;
if (sc_result < 0) {
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
} else {
switch (reader->slot[bSlot].active_protocol) {
case SC_PROTO_T0:
result.bProtocolNum = 0;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T0_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T0\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T0_t));
abProtocolDataStructure_T0_t * t0 =
*(abProtocolDataStructure_T0_t**) abProtocolDataStructure;
/* values taken from ISO 7816-3 defaults
* FIXME analyze ATR to get values */
t0->bmFindexDindex =
1<<4| // index to table 7 ISO 7816-3 (Fi)
1; // index to table 8 ISO 7816-3 (Di)
t0->bmTCCKST0 = 0<<1; // convention (direct)
t0->bGuardTimeT0 = 0xFF;
t0->bWaitingIntegerT0 = 0x10;
t0->bClockStop = 0; // (not allowed)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
sc_result = SC_ERROR_OUT_OF_MEMORY;
}
break;
case SC_PROTO_T1:
result.bProtocolNum = 1;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T1_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T1\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T1_t));
abProtocolDataStructure_T1_t * t1 =
*(abProtocolDataStructure_T1_t**) abProtocolDataStructure;
/* values taken from OpenPGP-card
* FIXME analyze ATR to get values */
t1->bmFindexDindex =
1<<4| // index to table 7 ISO 7816-3 (Fi)
3; // index to table 8 ISO 7816-3 (Di)
t1->bmTCCKST1 =
0| // checksum type (CRC)
0<<1| // convention (direct)
0x10;
t1->bGuardTimeT1 = 0xFF;
t1->bWaitingIntegersT1 =
4<<4| // BWI
5; // CWI
t1->bClockStop = 0; // (not allowed)
t1->bIFSC = 0x80;
t1->bNadValue = 0; // see 7816-3 9.4.2.1 (only default value)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
sc_result = SC_ERROR_OUT_OF_MEMORY;
}
break;
default:
fprintf (stderr, "unknown protocol\n");
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
}
}
result.bStatus = get_bStatus(sc_result, bSlot);
result.bError = get_bError(sc_result);
return result;
}
RDR_to_PC_Parameters_t
perform_PC_to_RDR_GetParamters(const PC_to_RDR_GetParameters_t request,
__u8** abProtocolDataStructure)
{
if ( request.bMessageType != 0x6C ||
request.dwLength != __constant_cpu_to_le32(0))
sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_GetParamters");
return get_RDR_to_PC_Parameters(request.bSlot, request.bSeq,
SC_SUCCESS, abProtocolDataStructure);
}
RDR_to_PC_DataBlock_t
perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request,
const __u8* abData, __u8** abDataOut)
{
char buf[50];
int sc_result = SC_SUCCESS;
size_t resplen = 0;
sc_apdu_t apdu;
struct sc_pin_cmd_pin pin;
sc_ui_hints_t hints;
memset(&hints, 0, sizeof(hints));
memset(&pin, 0, sizeof(pin));
if (request.bMessageType != 0x69)
sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_Secure");
if (request.bSlot > sizeof card_in_slot)
goto err;
if (request.wLevelParameter != CCID_WLEVEL_DIRECT) {
sc_result = SC_ERROR_NOT_SUPPORTED;
goto err;
}
__u8 bmPINLengthFormat, bmPINBlockString, bmFormatString;
__u8 *abPINApdu;
uint32_t apdulen;
uint16_t wPINMaxExtraDigit;
abPINDataStucture_Verification_t *verify = NULL;
abPINDataStucture_Modification_t *modify = NULL;
switch (*abData) { // first byte of abData is bPINOperation
case 0x00:
// PIN Verification
verify = (abPINDataStucture_Verification_t *)
(abData + sizeof(__u8));
wPINMaxExtraDigit = verify->wPINMaxExtraDigit;
bmPINLengthFormat = verify->bmPINLengthFormat;
bmPINBlockString = verify->bmPINBlockString;
bmFormatString = verify->bmFormatString;
abPINApdu = (__u8*) verify + sizeof(*verify);
apdulen = __le32_to_cpu(request.dwLength) - sizeof(*verify) - sizeof(__u8);
break;
case 0x01:
// PIN Modification
modify = (abPINDataStucture_Modification_t *)
(abData + sizeof(__u8));
wPINMaxExtraDigit = modify->wPINMaxExtraDigit;
bmPINLengthFormat = modify->bmPINLengthFormat;
bmPINBlockString = modify->bmPINBlockString;
bmFormatString = modify->bmFormatString;
abPINApdu = (__u8*) modify + sizeof(*modify);
apdulen = __le32_to_cpu(request.dwLength) - sizeof(*modify) - sizeof(__u8);
break;
case 0x04:
// Cancel PIN function
default:
sc_result = SC_ERROR_NOT_SUPPORTED;
goto err;
}
if (!build_apdu(abPINApdu, apdulen, &apdu)) {
sc_result = SC_ERROR_INVALID_DATA;
goto err;
}
apdu.sensitive = 1;
snprintf(buf, sizeof buf, "APDU, %d byte(s):\tins=%02x p1=%02x p2=%02x lc=%02x le=%02x",
request.dwLength, apdu.ins, apdu.p1, apdu.p2, (int) apdu.lc, (int) apdu.le);
sc_ui_display_debug(ctx, buf);
__u8 *p;
/* Note: offset and length_offset are relative to the first databyte */
pin.min_length = wPINMaxExtraDigit >> 8;
pin.max_length = wPINMaxExtraDigit & 0x00ff;
pin.offset = (bmFormatString >> 3) & 0xf;
uint8_t system_units_bytes = bmFormatString & CCID_PIN_UNITS_BYTES;
uint8_t justify_right = bmFormatString & CCID_PIN_JUSTIFY_RIGHT;
uint8_t encoding = bmFormatString & 2;
uint8_t offset_length = bmPINBlockString >> 4;
uint8_t blocksize = bmPINBlockString & 0xf;
uint8_t length_shift = bmPINLengthFormat & 0xf;
/* get the PIN */
hints.prompt = "PIN Verification";
hints.dialog_name = "ccid.PC_to_RDR_Secure";
hints.usage = SC_UI_USAGE_OTHER;
hints.card = card_in_slot[request.bSlot];
sc_result = sc_ui_get_pin(&hints, (char **) &pin.data);
if (sc_result < 0)
goto err;
/* check length of PIN */
pin.len = strlen((char *)pin.data);
if (pin.len > pin.max_length || pin.len < pin.min_length) {
sc_result = SC_ERROR_PIN_CODE_INCORRECT;
goto err;
}
/* write length of PIN */
if (offset_length) {
if (offset_length == 8) {
if (system_units_bytes) {
pin.length_offset = length_shift;
} else {
if (length_shift == 0)
pin.length_offset = 0;
if (length_shift == 8)
pin.length_offset = 1;
else {
goto err;
}
}
p = (u8 *) apdu.data + pin.length_offset;
*p = pin.len;
} else {
goto err;
}
}
/* offset due to right alignment */
uint8_t justify_offset;
if (justify_right) {
if (encoding == CCID_PIN_ENCODING_BCD) {
if (pin.len % 2)
goto err;
else
justify_offset = pin.len/2;
} else
justify_offset = blocksize - pin.len;
}
else
justify_offset = 0;
/* set p to the first byte where to write the PIN */
if (system_units_bytes) {
p = (u8 *) apdu.data + pin.offset + justify_offset;
} else {
if (pin.offset == 0)
p = (u8 *) apdu.data + justify_offset;
else if (pin.offset == 8)
p = (u8 *) apdu.data + 1 + justify_offset;
else {
goto err;
}
}
/* encode and write the pin into the APDU */
const u8 *ppin;
if (encoding == CCID_PIN_ENCODING_BIN) {
for (ppin = pin.data; *ppin; p++, ppin++) {
switch (*ppin) {
case '0':
*p = 0x00;
break;
case '1':
*p = 0x01;
break;
case '2':
*p = 0x02;
break;
case '3':
*p = 0x03;
break;
case '4':
*p = 0x04;
break;
case '5':
*p = 0x05;
break;
case '6':
*p = 0x06;
break;
case '7':
*p = 0x07;
break;
case '8':
*p = 0x08;
break;
case '9':
*p = 0x09;
break;
default:
sc_result = SC_ERROR_PIN_CODE_INCORRECT;
goto err;
}
}
} else {
if (encoding == CCID_PIN_ENCODING_BCD)
pin.encoding = SC_PIN_ENCODING_BCD;
else if (encoding == CCID_PIN_ENCODING_ASCII)
pin.encoding = SC_PIN_ENCODING_ASCII;
else {
sc_result = SC_ERROR_NOT_SUPPORTED;
goto err;
}
sc_result = sc_build_pin(p, abPINApdu - p, &pin, 0);
if (sc_result < 0) {
goto err;
}
}
if (!get_rapdu(&apdu, request.bSlot, abDataOut, &resplen, &sc_result))
goto err;
snprintf(buf, sizeof buf, "RAPDU, %d byte(s):\tsw1=%02x sw2=%02x",
(int) resplen, apdu.sw1, apdu.sw2);
sc_ui_display_debug(ctx, buf);
err:
if (sc_result < 0)
sc_ui_display_error(ctx, sc_strerror(sc_result));
if (pin.data) {
sc_mem_clear((u8 *) pin.data, pin.len);
sc_mem_clear(abPINApdu, apdulen);
free((u8 *) pin.data);
}
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, sc_result,
__constant_cpu_to_le32(resplen));
}
RDR_to_PC_NotifySlotChange_t
get_RDR_to_PC_NotifySlotChange ()
{
RDR_to_PC_NotifySlotChange_t result;
result.bMessageType = 0x50;
result.bmSlotICCState = CCID_SLOTS_UNCHANGED;
int i;
int sc_result;
uint8_t changed [] = {
CCID_SLOT1_CHANGED,
CCID_SLOT2_CHANGED,
CCID_SLOT3_CHANGED,
CCID_SLOT4_CHANGED,
};
uint8_t present [] = {
CCID_SLOT1_CARD_PRESENT,
CCID_SLOT2_CARD_PRESENT,
CCID_SLOT3_CARD_PRESENT,
CCID_SLOT4_CARD_PRESENT,
};
for (i = 0; i < reader->slot_count; i++) {
sc_result = sc_detect_card_presence(reader, i);
if (sc_result < 0) {
fprintf (stderr, "error getting slot state\n");
continue;
}
if (sc_result & SC_SLOT_CARD_PRESENT)
result.bmSlotICCState |= present[i];
if (sc_result & SC_SLOT_CARD_CHANGED)
result.bmSlotICCState |= changed[i];
}
return result;
}
RDR_to_PC_SlotStatus_t
perform_unknown(const PC_to_RDR_GetSlotStatus_t request)
{
RDR_to_PC_SlotStatus_t result;
switch (request.bMessageType) {
case 0x62:
case 0x6F:
case 0x69:
result.bMessageType = 0x80;
break;
case 0x63:
case 0x65:
case 0x6E:
case 0x6A:
case 0x71:
case 0x72:
result.bMessageType = 0x81;
break;
case 0x61:
case 0x6C:
case 0x6D:
result.bMessageType = 0x82;
break;
case 0x6B:
result.bMessageType = 0x83;
break;
case 0x73:
result.bMessageType = 0x84;
break;
default:
sc_ui_display_debug(ctx, "unknown message type");
result.bMessageType = 0;
}
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = request.bSlot,
result.bSeq = request.bSeq;
result.bStatus = get_bStatus(SC_ERROR_UNKNOWN_DATA_RECEIVED, request.bSlot);
result.bError = 0;
result.bClockStatus = 0;
return result;
}
int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf)
{
if (inbuf == NULL)
return 0;
int result = -1;
/*if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {*/
/*if (ccid_initialize(reader_num) == NULL)*/
/*goto error;*/
/*}*/
switch (*inbuf) {
case 0x62:
{ sc_ui_display_debug(ctx, "PC_to_RDR_IccPowerOn");
char* atr;
PC_to_RDR_IccPowerOn_t input =
*(PC_to_RDR_IccPowerOn_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOn(input, &atr);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, atr,
__le32_to_cpu(output.dwLength));
} break;
case 0x63:
{ sc_ui_display_debug(ctx, "PC_to_RDR_IccPowerOff");
PC_to_RDR_IccPowerOff_t input =
*(PC_to_RDR_IccPowerOff_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOff(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x65:
{ sc_ui_display_debug(ctx, "PC_to_RDR_GetSlotStatus");
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_GetSlotStatus(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x6F:
{ sc_ui_display_debug(ctx, "PC_to_RDR_XfrBlock");
__u8* rapdu;
PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf;
RDR_to_PC_DataBlock_t output =
perform_PC_to_RDR_XfrBlock(input, inbuf + sizeof input,
&rapdu);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
free(rapdu);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, rapdu,
__le32_to_cpu(output.dwLength));
free(rapdu);
} break;
case 0x6C:
{ sc_ui_display_debug(ctx, "PC_to_RDR_GetParameters");
__u8* abProtocolDataStructure;
PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*)
inbuf;
RDR_to_PC_Parameters_t output = perform_PC_to_RDR_GetParamters(
input, &abProtocolDataStructure );
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
if (abProtocolDataStructure)
free(abProtocolDataStructure);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, abProtocolDataStructure,
__le32_to_cpu(output.dwLength));
if (abProtocolDataStructure)
free(abProtocolDataStructure);
} break;
case 0x69:
{ sc_ui_display_debug(ctx, "PC_to_RDR_Secure");
__u8* rapdu;
PC_to_RDR_Secure_t input = *(PC_to_RDR_Secure_t *) inbuf;
RDR_to_PC_DataBlock_t output =
perform_PC_to_RDR_Secure(input, inbuf + sizeof input,
&rapdu);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
free(rapdu);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, rapdu,
__le32_to_cpu(output.dwLength));
free(rapdu);
} break;
default:
{ fprintf(stderr, "unknown ccid command: 0x%4X\n", *inbuf);
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_unknown(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
}
}
return result;
}
int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf)
{
int result = -1;
__u16 value, index, length;
value = __le16_to_cpu(setup->wValue);
index = __le16_to_cpu(setup->wIndex);
length = __le16_to_cpu(setup->wLength);
if (setup->bRequestType == USB_REQ_CCID) {
switch(setup->bRequest) {
case CCID_CONTROL_ABORT:
{
sc_ui_display_debug(ctx, "ABORT");
if (length != 0x00) {
sc_ui_display_debug(ctx, "warning: malformed ABORT");
}
result = 0;
} break;
case CCID_CONTROL_GET_CLOCK_FREQUENCIES:
{
sc_ui_display_debug(ctx, "GET_CLOCK_FREQUENCIES");
if (value != 0x00) {
fprintf(stderr,
"warning: malformed GET_CLOCK_FREQUENCIES\n");
}
result = sizeof(__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 clock = ccid_desc.dwDefaultClock;
memcpy(*outbuf, &clock, sizeof (__le32));
} break;
case CCID_CONTROL_GET_DATA_RATES:
{
sc_ui_display_debug(ctx, "GET_DATA_RATES");
if (value != 0x00) {
sc_ui_display_debug(ctx, "warning: malformed GET_DATA_RATES");
}
result = sizeof (__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 drate = ccid_desc.dwDataRate;
memcpy(*outbuf, &drate, sizeof (__le32));
} break;
default:
printf("unknown status setup->bRequest == %d", setup->bRequest);
}
}
return result;
}
int ccid_state_changed(RDR_to_PC_NotifySlotChange_t *slotchange)
{
if (slotchange) {
*slotchange = get_RDR_to_PC_NotifySlotChange();
if (slotchange->bmSlotICCState)
return 1;
}
return 0;
}