* 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
This commit is contained in:
parent
4608716c53
commit
adc082b957
|
@ -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
|
|
@ -32,6 +32,13 @@
|
|||
#include <winscard.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Winsock.h>
|
||||
#include "part10.h"
|
||||
#define PINPAD_ENABLED
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef HAVE_READER_H
|
||||
#include <reader.h>
|
||||
#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) {
|
||||
|
|
Loading…
Reference in New Issue