From adc082b95736477e9858423a20e8f6ec123b504f Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 16 Aug 2006 16:36:00 +0000 Subject: [PATCH] * Fix endianness for PCSCv2 part 10 IOCTLs * Add support for start/finish style IOCTLs * Add support for the same pinpad functionality on windows Some code from Robert Konklewski and Ludovic Rousseau git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3007 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/part10.h | 115 ++++++++++++++++++++++++++++++++++++ src/libopensc/reader-pcsc.c | 91 ++++++++++++++++++++-------- 2 files changed, 183 insertions(+), 23 deletions(-) create mode 100644 src/libopensc/part10.h diff --git a/src/libopensc/part10.h b/src/libopensc/part10.h new file mode 100644 index 00000000..a67bf9f7 --- /dev/null +++ b/src/libopensc/part10.h @@ -0,0 +1,115 @@ +#ifndef __part10_h__ +#define __part10_h__ + +/* Copied from pcsc-lite reader.h */ + +/** + * TeleTrust Class 2 reader tags + */ +#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) + +#define FEATURE_VERIFY_PIN_START 0x01 /**< OMNIKEY Proposal */ +#define FEATURE_VERIFY_PIN_FINISH 0x02 /**< OMNIKEY Proposal */ +#define FEATURE_MODIFY_PIN_START 0x03 /**< OMNIKEY Proposal */ +#define FEATURE_MODIFY_PIN_FINISH 0x04 /**< OMNIKEY Proposal */ +#define FEATURE_GET_KEY_PRESSED 0x05 /**< OMNIKEY Proposal */ +#define FEATURE_VERIFY_PIN_DIRECT 0x06 /**< USB CCID PIN Verify */ +#define FEATURE_MODIFY_PIN_DIRECT 0x07 /**< USB CCID PIN Modify */ +#define FEATURE_MCT_READERDIRECT 0x08 /**< KOBIL Proposal */ +#define FEATURE_MCT_UNIVERSAL 0x09 /**< KOBIL Proposal */ +#define FEATURE_IFD_PIN_PROP 0x0A /**< Gemplus Proposal */ +#define FEATURE_ABORT 0x0B /**< SCM Proposal */ + +/* structures used (but not defined) in PCSC Part 10 revision 2.01.02: + * "IFDs with Secure Pin Entry Capabilities" */ + +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; + +/* Set structure elements aligment on bytes + * http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html */ +#ifdef __APPLE__ +#pragma pack(1) +#else +#pragma pack(push, 1) +#endif + +/** the structure must be 6-bytes long */ +typedef struct +{ + uint8_t tag; + uint8_t length; + uint32_t value; /**< This value is always in BIG ENDIAN format as documented in PCSC v2 part 10 ch 2.2 page 2. You can use ntohl() for example */ +} PCSC_TLV_STRUCTURE; + +/** the wLangId and wPINMaxExtraDigit are 16-bits long so are subject to byte + * ordering */ +#define HOST_TO_CCID_16(x) (x) +#define HOST_TO_CCID_32(x) (x) + +/** structure used with \ref FEATURE_VERIFY_PIN_DIRECT */ +typedef struct +{ + uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ + uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ + uint8_t bmFormatString; /**< formatting options */ + uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, + * bits 3-0 PIN block size in bytes after + * justification and formatting */ + uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, + * bit 4 set if system units are bytes, clear if + * system units are bits, + * bits 3-0 PIN length position in system units */ + uint16_t wPINMaxExtraDigit; /**< 0xXXYY where XX is minimum PIN size in digits, + and YY is maximum PIN size in digits */ + uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should + * be considered complete */ + uint8_t bNumberMessage; /**< Number of messages to display for PIN verification */ + uint16_t wLangId; /**< Language for messages */ + uint8_t bMsgIndex; /**< Message index (should be 00) */ + uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ + uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ + uint8_t abData[1]; /**< Data to send to the ICC */ +} PIN_VERIFY_STRUCTURE; + +/** structure used with \ref FEATURE_MODIFY_PIN_DIRECT */ +typedef struct +{ + uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ + uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ + uint8_t bmFormatString; /**< formatting options */ + uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, + * bits 3-0 PIN block size in bytes after + * justification and formatting */ + uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, + * bit 4 set if system units are bytes, clear if + * system units are bits, + * bits 3-0 PIN length position in system units */ + uint8_t bInsertionOffsetOld; /**< Insertion position offset in bytes for + the current PIN */ + uint8_t bInsertionOffsetNew; /**< Insertion position offset in bytes for + the new PIN */ + uint16_t wPINMaxExtraDigit; + /**< 0xXXYY where XX is minimum PIN size in digits, + and YY is maximum PIN size in digits */ + uint8_t bConfirmPIN; /**< Flags governing need for confirmation of new PIN */ + uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should + * be considered complete */ + uint8_t bNumberMessage; /**< Number of messages to display for PIN verification*/ + uint16_t wLangId; /**< Language for messages */ + uint8_t bMsgIndex1; /**< index of 1st prompting message */ + uint8_t bMsgIndex2; /**< index of 2d prompting message */ + uint8_t bMsgIndex3; /**< index of 3d prompting message */ + uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ + uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ + uint8_t abData[1]; /**< Data to send to the ICC */ +} PIN_MODIFY_STRUCTURE; + +/* restore default structure elements alignment */ +#ifdef __APPLE__ +#pragma pack() +#else +#pragma pack(pop) +#endif +#endif diff --git a/src/libopensc/reader-pcsc.c b/src/libopensc/reader-pcsc.c index 08f411d5..da854889 100644 --- a/src/libopensc/reader-pcsc.c +++ b/src/libopensc/reader-pcsc.c @@ -32,6 +32,13 @@ #include #endif +#ifdef _WIN32 +#include +#include "part10.h" +#define PINPAD_ENABLED +#else +#include +#endif #ifdef HAVE_READER_H #include #ifdef HOST_TO_CCID_32 @@ -92,7 +99,12 @@ struct pcsc_slot_data { SCARDHANDLE pcsc_card; SCARD_READERSTATE_A reader_state; DWORD verify_ioctl; + DWORD verify_ioctl_start; + DWORD verify_ioctl_finish; + DWORD modify_ioctl; + DWORD modify_ioctl_start; + DWORD modify_ioctl_finish; int locked; }; @@ -196,7 +208,7 @@ static int pcsc_internal_transmit(sc_reader_t *reader, sc_slot_info_t *slot, return SC_ERROR_TRANSMIT_FAILED; } } - if (dwRecvLength < 2) + if (!control && dwRecvLength < 2) return SC_ERROR_UNKNOWN_DATA_RECEIVED; *recvsize = dwRecvLength; @@ -538,34 +550,49 @@ static int pcsc_connect(sc_reader_t *reader, sc_slot_info_t *slot) if (rv == SCARD_S_SUCCESS) { if (!(feature_len % sizeof(PCSC_TLV_STRUCTURE))) { + char *log_disabled = "but it's disabled in configuration file"; /* get the number of elements instead of the complete size */ feature_len /= sizeof(PCSC_TLV_STRUCTURE); pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; for (i = 0; i < feature_len; i++) { - char *log_disabled = "but it's disabled in configuration file"; if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { - char *log_text = "Reader supports pinpad PIN verification"; - pslot->verify_ioctl = pcsc_tlv[i].value; - if (priv->gpriv->enable_pinpad) { - sc_debug(reader->ctx, log_text); - slot->capabilities |= SC_SLOT_CAP_PIN_PAD; - } else { - sc_debug(reader->ctx, "%s %s", log_text, log_disabled); - } + pslot->verify_ioctl = ntohl(pcsc_tlv[i].value); + } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { + pslot->verify_ioctl_start = ntohl(pcsc_tlv[i].value); + } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { + pslot->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { - char *log_text = "Reader supports pinpad PIN modification"; - pslot->modify_ioctl = pcsc_tlv[i].value; - if (priv->gpriv->enable_pinpad) { - sc_debug(reader->ctx, log_text); - slot->capabilities |= SC_SLOT_CAP_PIN_PAD; - } else { - sc_debug(reader->ctx, "%s %s", log_text, log_disabled); - } + pslot->modify_ioctl = ntohl(pcsc_tlv[i].value); + } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { + pslot->modify_ioctl_start = ntohl(pcsc_tlv[i].value); + } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { + pslot->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else { - sc_debug(reader->ctx, "Reader pinpad feature: %02x not recognized", pcsc_tlv[i].tag); + sc_debug(reader->ctx, "Reader pinpad feature: %02x not supported", pcsc_tlv[i].tag); } } + + /* Set slot capabilities based on detected IOCTLs */ + if (pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish)) { + char *log_text = "Reader supports pinpad PIN verification"; + if (priv->gpriv->enable_pinpad) { + sc_debug(reader->ctx, log_text); + slot->capabilities |= SC_SLOT_CAP_PIN_PAD; + } else { + sc_debug(reader->ctx, "%s %s", log_text, log_disabled); + } + } + + if (pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish)) { + char *log_text = "Reader supports pinpad PIN modification"; + if (priv->gpriv->enable_pinpad) { + sc_debug(reader->ctx, log_text); + slot->capabilities |= SC_SLOT_CAP_PIN_PAD; + } else { + sc_debug(reader->ctx, "%s %s", log_text, log_disabled); + } + } } else sc_debug(reader->ctx, "Inconsistent TLV from reader!"); } else { @@ -881,6 +908,8 @@ static int part10_build_verify_pin_block(u8 * buf, size_t * size, struct sc_pin_ if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pin length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; + } else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) { + tmp |= data->pin1.pad_length; } pin_verify->bmPINBlockString = tmp; @@ -916,6 +945,7 @@ static int part10_build_verify_pin_block(u8 * buf, size_t * size, struct sc_pin_ /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { + pin_verify->abData[offset++] = apdu->lc; memcpy(&pin_verify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } @@ -969,6 +999,8 @@ static int part10_build_modify_pin_block(u8 * buf, size_t * size, struct sc_pin_ if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pin length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; + } else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) { + tmp |= data->pin1.pad_length; } pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */ @@ -1010,6 +1042,7 @@ static int part10_build_modify_pin_block(u8 * buf, size_t * size, struct sc_pin_ /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { + pin_modify->abData[offset++] = apdu->lc; memcpy(&pin_modify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } @@ -1054,21 +1087,21 @@ part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot, apdu = data->apdu; switch (data->cmd) { case SC_PIN_CMD_VERIFY: - if (!pslot->verify_ioctl) { + if (!(pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish))) { sc_error(reader->ctx, "Pinpad reader does not support verification!"); return SC_ERROR_NOT_SUPPORTED; } r = part10_build_verify_pin_block(sbuf, &scount, data); - ioctl = pslot->verify_ioctl; + ioctl = pslot->verify_ioctl ? pslot->verify_ioctl : pslot->verify_ioctl_start; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_UNBLOCK: - if (!pslot->modify_ioctl) { + if (!(pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish))) { sc_error(reader->ctx, "Pinpad reader does not support modification!"); return SC_ERROR_NOT_SUPPORTED; } r = part10_build_modify_pin_block(sbuf, &scount, data); - ioctl = pslot->modify_ioctl; + ioctl = pslot->modify_ioctl ? pslot->modify_ioctl : pslot->modify_ioctl_start; break; default: sc_error(reader->ctx, "Unknown PIN command %d", data->cmd); @@ -1084,6 +1117,18 @@ part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot, r = pcsc_internal_transmit(reader, slot, sbuf, scount, rbuf, &rcount, ioctl); SC_TEST_RET(reader->ctx, r, "Part 10: block transmit failed!"); + // finish the call if it was a two-phase operation + if ((ioctl == pslot->verify_ioctl_start) + || (ioctl == pslot->modify_ioctl_start)) { + if (rcount != 0) { + SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + ioctl = (ioctl == pslot->verify_ioctl_start) ? pslot->verify_ioctl_finish : pslot->modify_ioctl_finish; + + rcount = sizeof(rbuf); + r = pcsc_internal_transmit(reader, slot, sbuf, 0, rbuf, &rcount, ioctl); + SC_TEST_RET(reader->ctx, r, "Part 10: finish operation failed!"); + } /* We expect only two bytes of result data (SW1 and SW2) */ if (rcount != 2) {