From 05861588ed2ad490ecf3726d7f5e5cef02a99701 Mon Sep 17 00:00:00 2001 From: frankmorgner Date: Thu, 21 Jan 2010 21:48:01 +0000 Subject: [PATCH] - 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 --- ccid/Makefile | 6 +- ccid/ccid.c | 1053 ++++++++++++++++++++++++++++--------------------- ccid/ccid.h | 101 ++--- ccid/usb.c | 124 +++++- 4 files changed, 764 insertions(+), 520 deletions(-) diff --git a/ccid/Makefile b/ccid/Makefile index 19217d6..759843e 100644 --- a/ccid/Makefile +++ b/ccid/Makefile @@ -9,7 +9,9 @@ bindir = $(exec_prefix)/bin # Compiler CC = gcc CFLAGS = -Wall -g -LIBPCSCLITE_CFLAGS = `pkg-config --cflags --libs libpcsclite` +#LIBPCSCLITE_CFLAGS = `pkg-config --cflags --libs libpcsclite` +#OPENSSL_CFLAGS = `pkg-config --cflags --libs libssl` +OPENSC_CFLAGS = `pkg-config --cflags --libs libopensc` PTHREAD_CFLAGS = -pthread INSTALL = install @@ -23,7 +25,7 @@ all: $(TARGETS) ccid: ccid.h ccid.c usbstring.c usbstring.h usb.c - $(CC) $(LIBPCSCLITE_CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS) usbstring.c ccid.c usb.c -o $@ + $(CC) $(LIBPCSCLITE_CFLAGS) $(OPENSC_CFLAGS) $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS) usbstring.c ccid.c usb.c -o $@ install: $(TARGETS) installdirs diff --git a/ccid/ccid.c b/ccid/ccid.c index a035026..5b89c32 100644 --- a/ccid/ccid.c +++ b/ccid/ccid.c @@ -1,184 +1,309 @@ -#include #include #include #include #include #include +#include +#include #include "ccid.h" -static SCARDCONTEXT hcontext = 0; -static SCARDHANDLE hcard = 0; -static SCARD_READERSTATE rstate; -static DWORD dwActiveProtocol; -static char reader_name[MAX_READERNAME]; -static int reader_num; +//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; -const char* ccid_initialize(int num) +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) { - char *readers, *str; - DWORD size; - LONG r; + sc_context_param_t ctx_param; + int sc_result; + int i; - reader_num = num; - r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hcontext); - if (r != SCARD_S_SUCCESS) { - fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r)); - SCardReleaseContext(hcontext); - return NULL; + 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; } - r = SCardListReaders(hcontext, NULL, NULL, &size); - if (size == 0) - r = SCARD_E_UNKNOWN_READER; - if (r != SCARD_S_SUCCESS) { - fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r)); - SCardReleaseContext(hcontext); - return NULL; + if (verbose > 1) + ctx->debug = verbose-1; + + for (i = 0; i < sizeof card_in_slot; i++) { + card_in_slot[i] = NULL; } - /* get all readers */ - readers = (char *) malloc(size); - if (readers == NULL) { - fprintf(stderr, "pc/sc error: %s\n", - pcsc_stringify_error(SCARD_E_NO_MEMORY)); - SCardReleaseContext(hcontext); - return NULL; + if (sc_ctx_get_reader_count(ctx) == 0) { + fprintf(stderr, + "No smart card readers found.\n"); + return 0; } - r = SCardListReaders(hcontext, NULL, readers, &size); - if (r != SCARD_S_SUCCESS) { - free(readers); - fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r)); - SCardReleaseContext(hcontext); - return NULL; - } - - /* name of reader number num */ - str = readers; - for (size = 0; size < num; size++) { - /* go to the next name */ - str += strlen(str) + 1; - - /* no more readers available? */ - if (strlen(str) == 0) { - free(readers); - fprintf(stderr, "pc/sc error: %s\n", - pcsc_stringify_error(SCARD_E_UNKNOWN_READER)); - SCardReleaseContext(hcontext); - return NULL; + 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; } - strncpy(reader_name, str, MAX_READERNAME); - free(readers); - rstate.dwCurrentState = SCARD_STATE_UNAWARE; - rstate.dwEventState = SCARD_STATE_UNAWARE; - rstate.szReader = reader_name; + reader = sc_ctx_get_reader(ctx, reader_id); + ccid_desc.bMaxSlotIndex = reader->slot_count - 1; - return reader_name; + return 1; } int ccid_shutdown() { - SCardDisconnect(hcard, SCARD_UNPOWER_CARD); - hcard = 0; - rstate.dwCurrentState = SCARD_STATE_UNAWARE; - rstate.dwEventState = SCARD_STATE_UNAWARE; - return SCardReleaseContext(hcontext); -} - -__u8 get_bError(LONG pcsc_result) -{ - switch (pcsc_result) { - case SCARD_S_SUCCESS : /**< No error was encountered. */ - // Command not supported - return 0; - case SCARD_E_CANCELLED : /**< The action was cancelled by an SCardCancel request. */ - fprintf(stderr, "CMD_ABORTED\n"); - return 0xFF; - case SCARD_E_INVALID_HANDLE : /**< The supplied handle was invalid. */ - case SCARD_E_NO_SMARTCARD : /**< The operation requires a Smart Card, but no Smart Card is currently in the device. */ - case SCARD_E_UNKNOWN_CARD : /**< The specified smart card name is not recognized. */ - case SCARD_E_NOT_READY : /**< The reader or smart card is not ready to accept commands. */ - case SCARD_W_UNRESPONSIVE_CARD : /**< The smart card is not responding to a reset. */ - case SCARD_W_UNPOWERED_CARD : /**< Power has been removed from the smart card, so that further communication is not possible. */ - case SCARD_W_REMOVED_CARD : /**< The smart card has been removed, so further communication is not possible. */ - fprintf(stderr, "ICC_MUTE\n"); - return 0xFE; - case SCARD_E_SHARING_VIOLATION : /**< The smart card cannot be accessed because of other connections outstanding. */ - fprintf(stderr, "CMD_SLOT_BUSY\n"); - return 0xE0; - case SCARD_E_PROTO_MISMATCH : /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */ - fprintf(stderr, "ICC_PROTOCOL_NOT_SUPPORTED\n"); - return 0xF6; - // case SCARD_F_INTERNAL_ERROR : /**< An internal consistency check failed. */ - // case SCARD_E_INVALID_PARAMETER : /**< One or more of the supplied parameters could not be properly interpreted. */ - // case SCARD_E_INVALID_TARGET : /**< Registry startup information is missing or invalid. */ - // case SCARD_E_NO_MEMORY : /**< Not enough memory available to complete this command. */ - // case SCARD_F_WAITED_TOO_LONG : /**< An internal consistency timer has expired. */ - // case SCARD_E_INSUFFICIENT_BUFFER : /**< The data buffer to receive returned data is too small for the returned data. */ - // case SCARD_E_UNKNOWN_READER : /**< The specified reader name is not recognized. */ - // case SCARD_E_TIMEOUT : /**< The user-specified timeout value has expired. */ - // case SCARD_E_CANT_DISPOSE : /**< The system could not dispose of the media in the requested manner. */ - // case SCARD_E_INVALID_VALUE : /**< One or more of the supplied parameters values could not be properly interpreted. */ - // case SCARD_E_SYSTEM_CANCELLED : /**< The action was cancelled by the system, presumably to log off or shut down. */ - // case SCARD_F_COMM_ERROR : /**< An internal communications error has been detected. */ - // case SCARD_F_UNKNOWN_ERROR : /**< An internal error has been detected, but the source is unknown. */ - // case SCARD_E_INVALID_ATR : /**< An ATR obtained from the registry is not a valid ATR string. */ - // case SCARD_E_NOT_TRANSACTED : /**< An attempt was made to end a non-existent transaction. */ - // case SCARD_E_READER_UNAVAILABLE : /**< The specified reader is not currently available for use. */ - // case SCARD_W_UNSUPPORTED_CARD : /**< The reader cannot communicate with the card, due to ATR string configuration conflicts. */ - // case SCARD_W_RESET_CARD : /**< The smart card has been reset, so any shared state information is invalid. */ - // case SCARD_E_PCI_TOO_SMALL : /**< The PCI Receive buffer was too small. */ - // case SCARD_E_READER_UNSUPPORTED : /**< The reader driver does not meet minimal requirements for support. */ - // case SCARD_E_DUPLICATE_READER : /**< The reader driver did not produce a unique reader name. */ - // case SCARD_E_CARD_UNSUPPORTED : /**< The smart card does not meet minimal requirements for support. */ - // case SCARD_E_NO_SERVICE : /**< The Smart card resource manager is not running. */ - // case SCARD_E_SERVICE_STOPPED : /**< The Smart card resource manager has shut down. */ - // case SCARD_E_NO_READERS_AVAILABLE : /**< Cannot find a smart card reader. */ - default: - fprintf(stderr, "HW_ERROR\n"); - return 0xFB; + 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; } -__u8 get_bStatus(LONG pcsc_result) -{ - __u8 bStatus = 0; - if (rstate.dwEventState & SCARD_STATE_PRESENT) { - if (rstate.dwEventState & SCARD_STATE_MUTE || - rstate.dwEventState & SCARD_STATE_UNPOWERED) { - // inactive - fprintf(stderr, "card inactive\n"); - bStatus = 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 { - // active - /*fprintf(stderr, "card active\n");*/ - bStatus = 0; + 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 { - // absent - /*fprintf(stderr, "card absent\n");*/ - bStatus = 2; - if (hcard != 0) { - pcsc_result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD); - hcard = 0; + 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; + } } } - if (pcsc_result != SCARD_S_SUCCESS) { - bStatus |= (1<<6); - fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(pcsc_result)); + 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 bStatus; + return result; } RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq, - LONG pcsc_result) + int sc_result) { RDR_to_PC_SlotStatus_t result; @@ -186,15 +311,15 @@ RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq, result.dwLength = __constant_cpu_to_le32(0); result.bSlot = bSlot; result.bSeq = bSeq; - result.bStatus = get_bStatus(pcsc_result); - result.bError = get_bError(pcsc_result); + 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, - LONG pcsc_result, __le32 dwLength) + int sc_result, __le32 dwLength) { RDR_to_PC_DataBlock_t result; @@ -202,8 +327,8 @@ RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq, result.dwLength = dwLength; result.bSlot = bSlot; result.bSeq = bSeq; - result.bStatus = get_bStatus(pcsc_result); - result.bError = get_bError(pcsc_result); + result.bStatus = get_bStatus(sc_result, bSlot); + result.bError = get_bError(sc_result); result.bChainParameter = 0; return result; @@ -214,13 +339,11 @@ perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request) { if ( request.bMessageType != 0x65 || request.dwLength != __constant_cpu_to_le32(0) || - request.bSlot != 0 || request.abRFU1 != 0 || request.abRFU2 != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_GetSlotStatus\n"); + sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_GetSlotStatus"); - return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, - SCardGetStatusChange(hcontext, 1, &rstate, 1)); + return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, SC_SUCCESS); } RDR_to_PC_SlotStatus_t @@ -228,36 +351,35 @@ 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.bSlot != 0 || !( request.bPowerSelect == 0 || request.bPowerSelect & ccid_desc.bVoltageSupport ) || request.abRFU != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOn\n"); + sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_IccPowerOn"); - LONG pcsc_result; - if (hcard) { - pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE, - SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD, - &dwActiveProtocol); - } else { - pcsc_result = SCardConnect(hcontext, reader_name, - SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, - &hcard, &dwActiveProtocol); - } - if (pcsc_result == SCARD_S_SUCCESS) - pcsc_result = SCardGetStatusChange(hcontext, 1, &rstate, 1); + RDR_to_PC_SlotStatus_t result; + int sc_result; - RDR_to_PC_SlotStatus_t result = get_RDR_to_PC_SlotStatus(request.bSlot, - request.bSeq, pcsc_result); + if (!pATR) + goto err; - if (pcsc_result != SCARD_S_SUCCESS) { - *pATR = NULL; - result.dwLength = __constant_cpu_to_le32(0); - } else { - *pATR = (char*) rstate.rgbAtr; - result.dwLength = __cpu_to_le32(rstate.cbAtr); - } + *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 @@ -265,51 +387,60 @@ perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request) { if ( request.bMessageType != 0x63 || request.dwLength != __constant_cpu_to_le32(0) || - request.bSlot != 0 || request.abRFU1 != 0 || request.abRFU2 != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOff\n"); + sc_ui_display_debug(ctx, "warning: malformed PC_to_RDR_IccPowerOff"); - LONG result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD); - hcard = 0; - if (result == SCARD_E_INVALID_HANDLE) { - result = SCardGetStatusChange(hcontext, 1, &rstate, 1); - } + 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, result); + 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) { - if ( request.bMessageType != 0x6F || - request.bSlot != 0 || - request.bBWI != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_XfrBlock\n"); + char buf[50]; + int sc_result; + size_t resplen = 0; + sc_apdu_t apdu; - DWORD dwRecvLength = MAX_BUFFER_SIZE; - *abDataOut = (__u8 *) malloc(dwRecvLength); - if (*abDataOut == NULL) { - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0)); + 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; } - LPCSCARD_IO_REQUEST pioSendPci; - if (dwActiveProtocol == SCARD_PROTOCOL_T0) - pioSendPci = SCARD_PCI_T0; - else - pioSendPci = SCARD_PCI_T1; - int pcsc_result = SCardTransmit(hcard, pioSendPci, abDataIn, - __le32_to_cpu(request.dwLength), NULL, *abDataOut, &dwRecvLength); + 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, - pcsc_result, __cpu_to_le32(dwRecvLength)); + sc_result, __cpu_to_le32(resplen)); } RDR_to_PC_Parameters_t -get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8 +get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, int sc_result, __u8 **abProtocolDataStructure) { RDR_to_PC_Parameters_t result; @@ -317,67 +448,78 @@ get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8 result.bMessageType = 0x82; result.bSlot = bSlot; result.bSeq = bSeq; - if (pcsc_result == SCARD_S_SUCCESS) { - if (dwActiveProtocol == SCARD_PROTOCOL_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; - pcsc_result = SCARD_E_INSUFFICIENT_BUFFER; - } - } else { - 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; - pcsc_result = SCARD_E_INSUFFICIENT_BUFFER; - } - } - } else { + 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(pcsc_result); - result.bError = get_bError(pcsc_result); + result.bStatus = get_bStatus(sc_result, bSlot); + result.bError = get_bError(sc_result); return result; } @@ -387,38 +529,42 @@ 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) || - request.bSlot != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_GetParamters\n"); - - LONG pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE, - SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD, - &dwActiveProtocol); + 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, - pcsc_result, abProtocolDataStructure); + 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) { - /* only short APDUs supported so Lc is always the fiths byte */ - if ( request.bMessageType != 0x69 || - request.bSlot != 0) - fprintf(stderr, "warning: malformed PC_to_RDR_Secure\n"); + 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; - if (request.wLevelParameter != __constant_cpu_to_le16(0)) { - fprintf(stderr, "warning: Only APDUs, that begin and end with this command are supported.\n"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0)); + 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; } - printf(":"); - - __u8 PINMin, PINMax, bmPINLengthFormat, bmPINBlockString, bmFormatString; + __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 @@ -426,8 +572,7 @@ perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request, // PIN Verification verify = (abPINDataStucture_Verification_t *) (abData + sizeof(__u8)); - PINMin = verify->wPINMaxExtraDigit >> 8; - PINMax = verify->wPINMaxExtraDigit & 0x00ff; + wPINMaxExtraDigit = verify->wPINMaxExtraDigit; bmPINLengthFormat = verify->bmPINLengthFormat; bmPINBlockString = verify->bmPINBlockString; bmFormatString = verify->bmFormatString; @@ -438,8 +583,7 @@ perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request, // PIN Modification modify = (abPINDataStucture_Modification_t *) (abData + sizeof(__u8)); - PINMin = modify->wPINMaxExtraDigit >> 8; - PINMax = modify->wPINMaxExtraDigit & 0x00ff; + wPINMaxExtraDigit = modify->wPINMaxExtraDigit; bmPINLengthFormat = modify->bmPINLengthFormat; bmPINBlockString = modify->bmPINBlockString; bmFormatString = modify->bmFormatString; @@ -449,174 +593,169 @@ perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request, case 0x04: // Cancel PIN function default: - fprintf(stderr, "warning: unknown pin operation\n"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0)); + sc_result = SC_ERROR_NOT_SUPPORTED; + goto err; } - // copy the apdu - __u8 *apdu = (__u8*) malloc(apdulen); - if (!apdu) { - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0)); + if (!build_apdu(abPINApdu, apdulen, &apdu)) { + sc_result = SC_ERROR_INVALID_DATA; + goto err; } - memcpy(apdu, abPINApdu, apdulen); + apdu.sensitive = 1; - // TODO - char *pin = "1234"; + 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; - /* if system units are bytes or bits */ - uint8_t bytes = bmFormatString >> 7; - /* PIN position after format in the APDU command (relative to the first - * data after Lc). The position is based on the system units’ type - * indicator (maximum1111 for fifteen system units */ - uint8_t pos = (bmFormatString >> 3) & 0xf; - /* Right or left justify data */ - uint8_t right = (bmFormatString >> 2) & 1; - /* Bit wise for the PIN format type */ - uint8_t type = bmFormatString & 2; + /* 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; - uint8_t pinlen = strnlen(pin, PINMax + 1); - if (pinlen > PINMax) { - fprintf(stderr, "warning: PIN was too long, " - "should be between %d and %d\n", PINMin, PINMax); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0)); + /* 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; } - /* Size in bits of the PIN length inserted in the APDU command. */ - uint8_t lenlen = bmPINBlockString >> 4; - /* PIN block size in bytes after justification and formatting. */ - uint8_t blocksize = bmPINBlockString & 0xf; - /* PIN length position in the APDU command */ - uint8_t lenshift = bmPINLengthFormat & 0xf; - if (lenlen) { - /* write PIN Length */ - if (lenlen == 8) { - if (bytes) { - p = apdu + 5 + lenshift; + /* write length of PIN */ + if (offset_length) { + if (offset_length == 8) { + if (system_units_bytes) { + pin.length_offset = length_shift; } else { - if (lenshift == 0) - p = apdu + 5; - if (lenshift == 8) - p = apdu + 5 + 1; + if (length_shift == 0) + pin.length_offset = 0; + if (length_shift == 8) + pin.length_offset = 1; else { - fprintf(stderr, "warning: PIN Block too complex, aborting\n"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, - __constant_cpu_to_le32(0)); + goto err; } } - *p = pinlen; + p = (u8 *) apdu.data + pin.length_offset; + *p = pin.len; + } else { + goto err; } - - fprintf(stderr, "warning: PIN Block too complex, aborting\n"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0)); } - uint8_t justify; - if (right) - justify = blocksize - pinlen; + /* 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 = 0; + justify_offset = 0; - if (bytes) { - p = apdu + 5 + pos + justify; + /* 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 (pos == 0) - p = apdu + 5 + justify; - else if (pos == 8) - p = apdu + 5 + 1 + justify; + if (pin.offset == 0) + p = (u8 *) apdu.data + justify_offset; + else if (pin.offset == 8) + p = (u8 *) apdu.data + 1 + justify_offset; else { - fprintf(stderr, "warning: PIN Block too complex, aborting\n"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, - __constant_cpu_to_le32(0)); + goto err; } } - while (*pin) { - uint8_t c; - switch (type) { - case 0: - // binary - switch (*pin) { - case '0': - c = 0x00; - break; - case '1': - c = 0x01; - break; - case '2': - c = 0x02; - break; - case '3': - c = 0x03; - break; - case '4': - c = 0x04; - break; - case '5': - c = 0x05; - break; - case '6': - c = 0x06; - break; - case '7': - c = 0x07; - break; - case '8': - c = 0x08; - break; - case '9': - c = 0x09; - break; - default: - fprintf(stderr, "warning: PIN character %c not supported, aborting", *pin); - return get_RDR_to_PC_DataBlock(request.bSlot, - request.bSeq, SCARD_F_INTERNAL_ERROR, - __constant_cpu_to_le32(0)); - } - break; - case 1: - // BCD - fprintf(stderr, "warning: BCD format not supported, aborting"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0)); - case 2: - // ASCII - c = *pin; - break; - default: - fprintf(stderr, "warning: unknown formatting, aborting"); - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0)); + /* 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; } - *p = c; - p++; - pin++; } + if (!get_rapdu(&apdu, request.bSlot, abDataOut, &resplen, &sc_result)) + goto err; - DWORD dwRecvLength = MAX_BUFFER_SIZE; - *abDataOut = (__u8 *) malloc(dwRecvLength); - if (*abDataOut == NULL) { - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0)); + 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); } - LPCSCARD_IO_REQUEST pioSendPci; - if (dwActiveProtocol == SCARD_PROTOCOL_T0) - pioSendPci = SCARD_PCI_T0; - else - pioSendPci = SCARD_PCI_T1; - int pcsc_result = SCardTransmit(hcard, pioSendPci, apdu, apdulen, NULL, - *abDataOut, &dwRecvLength); - - return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, - pcsc_result, __cpu_to_le32(dwRecvLength)); + return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq, sc_result, + __constant_cpu_to_le32(resplen)); } RDR_to_PC_NotifySlotChange_t @@ -624,15 +763,33 @@ get_RDR_to_PC_NotifySlotChange () { RDR_to_PC_NotifySlotChange_t result; result.bMessageType = 0x50; - result.bmSlotICCState = 0; // no change + result.bmSlotICCState = CCID_SLOTS_UNCHANGED; - DWORD current = rstate.dwEventState; - if (SCARD_S_SUCCESS != SCardGetStatusChange(hcontext, 1, &rstate, 1)) { - fprintf(stderr, "state changed: error\n"); - result.bmSlotICCState = 2; // changed (error) - } else if (!(current & rstate.dwEventState)) { - fprintf(stderr, "state changed\n"); - result.bmSlotICCState = 2; // changed + 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; @@ -668,13 +825,13 @@ perform_unknown(const PC_to_RDR_GetSlotStatus_t request) result.bMessageType = 0x84; break; default: - fprintf(stderr, "unknown message type\n"); + 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(SCARD_F_UNKNOWN_ERROR); + result.bStatus = get_bStatus(SC_ERROR_UNKNOWN_DATA_RECEIVED, request.bSlot); result.bError = 0; result.bClockStatus = 0; @@ -686,21 +843,20 @@ 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; - } + /*if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {*/ + /*if (ccid_initialize(reader_num) == NULL)*/ + /*goto error;*/ + /*}*/ switch (*inbuf) { case 0x62: - { fprintf(stderr, "PC_to_RDR_IccPowerOn\n"); + { 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) { @@ -714,7 +870,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; case 0x63: - { fprintf(stderr, "PC_to_RDR_IccPowerOff\n"); + { sc_ui_display_debug(ctx, "PC_to_RDR_IccPowerOff"); PC_to_RDR_IccPowerOff_t input = *(PC_to_RDR_IccPowerOff_t*) inbuf; @@ -732,7 +888,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; case 0x65: - { /*fprintf(stderr, "PC_to_RDR_GetSlotStatus\n");*/ + { sc_ui_display_debug(ctx, "PC_to_RDR_GetSlotStatus"); PC_to_RDR_GetSlotStatus_t input = *(PC_to_RDR_GetSlotStatus_t*) inbuf; @@ -750,7 +906,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; case 0x6F: - { fprintf(stderr, "PC_to_RDR_XfrBlock\n"); + { sc_ui_display_debug(ctx, "PC_to_RDR_XfrBlock"); __u8* rapdu; PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf; @@ -773,7 +929,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; case 0x6C: - { fprintf(stderr, "PC_to_RDR_GetParameters\n"); + { sc_ui_display_debug(ctx, "PC_to_RDR_GetParameters"); __u8* abProtocolDataStructure; PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*) @@ -798,7 +954,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; case 0x69: - { fprintf(stderr, "PC_to_RDR_Secure\n"); + { sc_ui_display_debug(ctx, "PC_to_RDR_Secure"); __u8* rapdu; PC_to_RDR_Secure_t input = *(PC_to_RDR_Secure_t *) inbuf; @@ -821,7 +977,7 @@ int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf) } break; default: -error: + { fprintf(stderr, "unknown ccid command: 0x%4X\n", *inbuf); PC_to_RDR_GetSlotStatus_t input = @@ -851,23 +1007,19 @@ int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf) value = __le16_to_cpu(setup->wValue); index = __le16_to_cpu(setup->wIndex); length = __le16_to_cpu(setup->wLength); - if (setup->bRequestType == USB_REQ_CCID) + if (setup->bRequestType == USB_REQ_CCID) { switch(setup->bRequest) { case CCID_CONTROL_ABORT: { - fprintf(stderr, "ABORT\n"); + sc_ui_display_debug(ctx, "ABORT"); if (length != 0x00) { - fprintf(stderr, "warning: malformed ABORT\n"); + sc_ui_display_debug(ctx, "warning: malformed ABORT"); } - result = SCardCancel(hcontext); - if (result != SCARD_S_SUCCESS) - fprintf(stderr, "pc/sc error: %s\n", - pcsc_stringify_error(result)); result = 0; } break; case CCID_CONTROL_GET_CLOCK_FREQUENCIES: { - fprintf(stderr, "GET_CLOCK_FREQUENCIES\n"); + sc_ui_display_debug(ctx, "GET_CLOCK_FREQUENCIES"); if (value != 0x00) { fprintf(stderr, "warning: malformed GET_CLOCK_FREQUENCIES\n"); @@ -884,9 +1036,9 @@ int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf) } break; case CCID_CONTROL_GET_DATA_RATES: { - fprintf(stderr, "GET_DATA_RATES\n"); + sc_ui_display_debug(ctx, "GET_DATA_RATES"); if (value != 0x00) { - fprintf(stderr, "warning: malformed GET_DATA_RATES\n"); + sc_ui_display_debug(ctx, "warning: malformed GET_DATA_RATES"); } result = sizeof (__le32); @@ -901,6 +1053,7 @@ int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf) default: printf("unknown status setup->bRequest == %d", setup->bRequest); } + } return result; } diff --git a/ccid/ccid.h b/ccid/ccid.h index ee71bdd..2a8dd6c 100644 --- a/ccid/ccid.h +++ b/ccid/ccid.h @@ -16,21 +16,69 @@ * You should have received a copy of the GNU General Public License along with * ccid. If not, see . */ -#ifndef _VPCD_H_ -#define _VPCD_H_ +#ifndef _VPCD_H +#define _VPCD_H #include -#include #ifdef __cplusplus extern "C" { #endif -#ifndef USB_REQ_CCID #define USB_REQ_CCID 0xA1 + #define CCID_CONTROL_ABORT 0x01 #define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x02 #define CCID_CONTROL_GET_DATA_RATES 0x03 + +#define CCID_BERROR_CMD_ABORTED 0xff /* Host aborted the current activity */ +#define CCID_BERROR_ICC_MUTE 0xfe /* CCID timed out while talking to the ICC */ +#define CCID_BERROR_XFR_PARITY_ERROR 0xfd /* Parity error while talking to the ICC */ +#define CCID_BERROR_XFR_OVERRUN 0xfc /* Overrun error while talking to the ICC */ +#define CCID_BERROR_HW_ERROR 0xfb /* An all inclusive hardware error occurred */ +#define CCID_BERROR_BAD_ATR_TS 0xf +#define CCID_BERROR_BAD_ATR_TCK 0xf +#define CCID_BERROR_ICC_PROTOCOL_NOT_SUPPORTED 0xf6 +#define CCID_BERROR_ICC_CLASS_NOT_SUPPORTED 0xf5 +#define CCID_BERROR_PROCEDURE_BYTE_CONFLICT 0xf4 +#define CCID_BERROR_DEACTIVATED_PROTOCOL 0xf3 +#define CCID_BERROR_BUSY_WITH_AUTO_SEQUENCE 0xf2 /* Automatic Sequence Ongoing */ +#define CCID_BERROR_PIN_TIMEOUT 0xf0 +#define CCID_BERROR_PIN_CANCELLED 0xef +#define CCID_BERROR_CMD_SLOT_BUSY 0xe0 /* A second command was sent to a slot which was already processing a command. */ +#define CCID_BERROR_CMD_NOT_SUPPORTED 0x00 +#define CCID_BERROR_OK 0x00 + +#define CCID_BSTATUS_OK_ACTIVE 0x00 /* No error. An ICC is present and active */ +#define CCID_BSTATUS_OK_INACTIVE 0x01 /* No error. ICC is present and inactive */ +#define CCID_BSTATUS_OK_NOICC 0x02 /* No error. No ICC is present */ +#define CCID_BSTATUS_ERROR_ACTIVE 0x40 /* Failed. An ICC is present and active */ +#define CCID_BSTATUS_ERROR_INACTIVE 0x41 /* Failed. ICC is present and inactive */ +#define CCID_BSTATUS_ERROR_NOICC 0x42 /* Failed. No ICC is present */ + +#define CCID_WLEVEL_DIRECT __constant_cpu_to_le16(0) /* APDU begins and ends with this command */ +#define CCID_WLEVEL_CHAIN_NEXT_XFRBLOCK __constant_cpu_to_le16(1) /* APDU begins with this command, and continue in the next PC_to_RDR_XfrBlock */ +#define CCID_WLEVEL_CHAIN_END __constant_cpu_to_le16(2) /* abData field continues a command APDU and ends the APDU command */ +#define CCID_WLEVEL_CHAIN_CONTINUE __constant_cpu_to_le16(3) /* abData field continues a command APDU and another block is to follow */ +#define CCID_WLEVEL_RESPONSE_IN_DATABLOCK __constant_cpu_to_le16(0x10) /* empty abData field, continuation of response APDU is expected in the next RDR_to_PC_DataBlock */ + +#define CCID_PIN_ENCODING_BIN 0x00 +#define CCID_PIN_ENCODING_BCD 0x01 +#define CCID_PIN_ENCODING_ASCII 0x02 + +#define CCID_PIN_UNITS_BYTES 0x80 +#define CCID_PIN_JUSTIFY_RIGHT 0x04 + +#define CCID_SLOTS_UNCHANGED 0x00 +#define CCID_SLOT1_CARD_PRESENT 0x01 +#define CCID_SLOT1_CHANGED 0x02 +#define CCID_SLOT2_CARD_PRESENT 0x04 +#define CCID_SLOT2_CHANGED 0x08 +#define CCID_SLOT3_CARD_PRESENT 0x10 +#define CCID_SLOT3_CHANGED 0x20 +#define CCID_SLOT4_CARD_PRESENT 0x40 +#define CCID_SLOT4_CHANGED 0x80 + struct ccid_class_descriptor { __u8 bLength; __u8 bDescriptorType; @@ -199,9 +247,8 @@ typedef struct { } __attribute__ ((packed)) RDR_to_PC_Parameters_t; typedef struct { __u8 bMessageType; - __u8 bmSlotICCState; + __u8 bmSlotICCState; /* we support 4 slots, so we need 2*4 bits = 1 byte */ } __attribute__ ((packed)) RDR_to_PC_NotifySlotChange_t; -#endif struct hid_class_descriptor { __u8 bLength; @@ -211,47 +258,7 @@ struct hid_class_descriptor { __u8 bNumDescriptors; } __attribute__ ((packed)); - -static struct ccid_class_descriptor -ccid_desc = { - .bLength = sizeof ccid_desc, - .bDescriptorType = 0x21, - .bcdCCID = __constant_cpu_to_le16(0x0110), - .bMaxSlotIndex = 0, - .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, -}; -const char* ccid_initialize(int num); +int ccid_initialize(int reader_id, int verbose); int ccid_shutdown(); int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf); diff --git a/ccid/usb.c b/ccid/usb.c index 93a01e5..f53b313 100644 --- a/ccid/usb.c +++ b/ccid/usb.c @@ -35,7 +35,11 @@ #include #include #include +#include +#include //#include +// +#include #ifdef AIO /* this aio code works with libaio-0.3.106 */ @@ -50,13 +54,32 @@ #define DRIVER_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */ static int vendorid = DRIVER_VENDOR_NUM; static int productid = DRIVER_PRODUCT_NUM; -static int verbose = 0; -static int debug = 0; +static int verbose = 1; +static int debug = 1; static int dohid = 0; static int doint = 0; -const char *usb_reader_name = NULL; int usb_reader_num = 0; +/*static const struct option options[] = {*/ + /*{ "hid", no_argument, &dohid, 1 },*/ + /*{ "interrupt", no_argument, &doint, 1 },*/ + /*{ "product", required_argument, NULL, 'p' },*/ + /*{ "reader", required_argument, NULL, 'r' },*/ + /*{ "serial", required_argument, NULL, 's' },*/ + /*{ "vendor", required_argument, NULL, 'v' },*/ + /*{ "verbose", no_argument, NULL, 'b' },*/ + /*{ NULL, 0, NULL, 0 }*/ +/*};*/ +/*static const char *option_help[] = {*/ + /*"Emulate HID device",*/ + /*"Uses reader number [auto-detect]",*/ + /*"Serial number of gadget [random]",*/ + /*"Add interrupt pipe for CCID",*/ + /*"Verbose operation. Use several times to enable debug output.",*/ + /*"USB vendor ID [0x0D46]",*/ + /*"USB product ID [0x3010]",*/ +/*};*/ + /* NOTE: these IDs don't imply endpoint numbering; host side drivers * should use endpoint descriptors, or perhaps bcdDevice, to configure * such things. Other product IDs could have different policies. @@ -66,6 +89,8 @@ int usb_reader_num = 0; /* these descriptors are modified based on what controller we find */ +extern struct ccid_class_descriptor ccid_desc; + #define STRINGID_MFGR 1 #define STRINGID_PRODUCT 2 #define STRINGID_SERIAL 3 @@ -899,9 +924,7 @@ ep_config (char *name, const char *label, /* ccid thread, forwards ccid requests to pcsc and returns results */ static void *ccid (void *param) -{ - - //int tmp; +{ pthread_t interrupt_thread; void *interrupt (void *param) @@ -958,11 +981,10 @@ static void *ccid (void *param) void close_pcsc() { - int result = ccid_shutdown(); - if (result != SCARD_S_SUCCESS) - fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(result)); + if (!ccid_shutdown()) + fprintf(stderr, "pc/sc error\n"); else if (verbose) - fprintf(stderr, "closed connection to %s\n", usb_reader_name); + fprintf(stderr, "closed connection\n"); } char **names = (char **) param; @@ -987,15 +1009,7 @@ static void *ccid (void *param) } pthread_cleanup_push (close_fd, &sink_fd); - usb_reader_name = ccid_initialize(usb_reader_num); - if (usb_reader_name == NULL) { - if (debug) - perror("ccid_initialize"); - goto error; - } pthread_cleanup_push (close_pcsc, NULL); - if (verbose) - fprintf (stderr, "connected to %s\n", usb_reader_name); if (doint) { static char * interruptnames[1]; @@ -1071,7 +1085,7 @@ error: static void *hid (void *param) { - + printf("%s:%d\n", __FILE__, __LINE__); char **names = (char **) param; char *status_name = names[0]; int result = 0; @@ -1502,7 +1516,6 @@ special: stall: if (verbose) { - fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); fprintf (stderr, "... protocol stall %02x.%02x\n", setup->bRequestType, setup->bRequest); for (tmp = 0; tmp<5; tmp++) { @@ -1678,11 +1691,53 @@ done: } /*-------------------------------------------------------------------------*/ +void util_print_usage_and_die(const char *app_name, const struct option options[], + const char *option_help[]) +{ + int i = 0; + printf("Usage: %s [OPTIONS]\nOptions:\n", app_name); + + while (options[i].name) { + char buf[40], tmp[5]; + const char *arg_str; + + /* Skip "hidden" options */ + if (option_help[i] == NULL) { + i++; + continue; + } + + if (options[i].val > 0 && options[i].val < 128) + sprintf(tmp, ", -%c", options[i].val); + else + tmp[0] = 0; + switch (options[i].has_arg) { + case 1: + arg_str = " "; + break; + case 2: + arg_str = " [arg]"; + break; + default: + arg_str = ""; + break; + } + sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); + if (strlen(buf) > 29) { + printf(" %s\n", buf); + buf[0] = '\0'; + } + printf(" %-29s %s\n", buf, option_help[i]); + i++; + } + exit(2); +} int main (int argc, char **argv) { int fd, c, i; + /*int long_optind = 0;*/ /* random initial serial number */ srand ((int) time (0)); @@ -1721,8 +1776,9 @@ main (int argc, char **argv) (strcmp(argv[i], "-d") == 0)) { verbose++; if ((strcmp(argv[i], "--debug") == 0) || - (strcmp(argv[i], "-d") == 0)) + (strcmp(argv[i], "-d") == 0)) { debug++; + } continue; } if (strcmp(argv[i], "--hid") == 0) { @@ -1768,6 +1824,27 @@ main (int argc, char **argv) fprintf(stderr, "unrecognized option \"%s\"\n", argv[i]); return 1; } + /*while (1) {*/ + /*c = getopt_long(argc, argv, "p:r:s:v:b", options, &long_optind);*/ + /*if (c == -1)*/ + /*break;*/ + /*if (c == '?')*/ + /*util_print_usage_and_die("ccid" , options, option_help);*/ + /*switch (c) {*/ + /*case 'r':*/ + /*opt_reader = atoi(optarg);*/ + /*break;*/ + /*case 'c':*/ + /*opt_driver = optarg;*/ + /*break;*/ + /*case 'w':*/ + /*opt_wait = 1;*/ + /*break;*/ + /*case 'v':*/ + /*verbose++;*/ + /*break;*/ + /*}*/ + /*}*/ if (verbose) fprintf (stderr, "serial=\"%s\"\n", serial); @@ -1777,6 +1854,11 @@ main (int argc, char **argv) return 1; } + if (!ccid_initialize(usb_reader_num, verbose)) { + perror("can't initialize ccid"); + return 1; + } + fd = init_device (); if (fd < 0) return 1;