* 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:
martin 2006-08-16 16:36:00 +00:00
parent 4608716c53
commit adc082b957
2 changed files with 183 additions and 23 deletions

115
src/libopensc/part10.h Normal file
View File

@ -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

View File

@ -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) {