From 3158fa3e05244cea43ffb63a41f36e3568efbb68 Mon Sep 17 00:00:00 2001 From: nils Date: Mon, 24 Jan 2005 11:31:11 +0000 Subject: [PATCH] merge Martin Paljak's ccid pinpad changes from the OPENSC_0_9 branch to the cvs head git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2095 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 3 +- src/libopensc/opensc.h | 2 +- src/libopensc/pinpad-ccid.c | 155 ++++++++++++++++++++++++++++++++++ src/libopensc/pinpad-ccid.h | 32 +++++++ src/libopensc/reader-ctapi.c | 2 +- src/libopensc/reader-openct.c | 4 +- src/libopensc/reader-pcsc.c | 31 ++++++- 7 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 src/libopensc/pinpad-ccid.c create mode 100644 src/libopensc/pinpad-ccid.h diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 99314ecf..d4632251 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -24,6 +24,7 @@ libopensc_la_SOURCES = \ emv.c \ \ ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c \ + pinpad-ccid.c \ \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-etoken.c card-tcos.c card-emv.c card-default.c \ @@ -40,7 +41,7 @@ include_HEADERS = \ cardctl.h asn1.h log.h ui.h \ errors.h types.h -noinst_HEADERS = ctbcs.h internal.h esteid.h card-oberthur.h +noinst_HEADERS = ctbcs.h internal.h esteid.h card-oberthur.h pinpad-ccid.h pkgconfigdir = @libdir@/pkgconfig pkgconfig_DATA = libopensc.pc libpkcs15init.pc \ diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index b1b27163..dccb9f16 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -384,7 +384,7 @@ struct sc_reader_operations { int (*transmit)(struct sc_reader *reader, struct sc_slot_info *slot, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, - int control); + unsigned long control); int (*lock)(struct sc_reader *reader, struct sc_slot_info *slot); int (*unlock)(struct sc_reader *reader, struct sc_slot_info *slot); int (*set_protocol)(struct sc_reader *reader, struct sc_slot_info *slot, diff --git a/src/libopensc/pinpad-ccid.c b/src/libopensc/pinpad-ccid.c new file mode 100644 index 00000000..c722d054 --- /dev/null +++ b/src/libopensc/pinpad-ccid.c @@ -0,0 +1,155 @@ +/* + * OpenSC pinpad support for CCID compatible readers. + * + * These functions build CCID PIN control blocks to be used with + * CCID compatible pinpad readers. + * Currently known to work only with libccid under unices via SCardControl(). + * + * Tested with: SPR532 with firmware 5.04, ccid-0.9.2mp1, EstEID, opensc-0.9.4mp3 (CVS) + * + * (C) 2004 Martin Paljak + */ +#ifdef MP_CCID_PINPAD +#include "internal.h" +#include "pinpad-ccid.h" +#include +#include +#include + +/* Build a pin verification CCID block + APDU */ +int ccid_build_verify_pin_block(u8 * buf, size_t * size, struct sc_pin_cmd_data *data) +{ + size_t buflen, count = 0; + sc_apdu_t *apdu = data->apdu; + u8 tmp; + buflen = sizeof(buf); + + /* CCID PIN verification control message */ + buf[count++] = SC_CCID_PIN_TIMEOUT; /* bTimeOut */ + + tmp = 0x00; + if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) + tmp |= SC_CCID_PIN_ENCODING_ASCII; + else if (data->pin1.encoding == SC_PIN_ENCODING_BCD) + tmp |= SC_CCID_PIN_ENCODING_BCD; + else + return SC_ERROR_NOT_SUPPORTED; + /* Only byte-aligend cards are cupported */ + tmp |= SC_CCID_PIN_UNITS_BYTES; + tmp |= (data->pin1.length_offset - 5) << 3; + buf[count++] = tmp; /* bmFormatString */ + + /* Ignored */ + buf[count++] = 0x00; /* bmPINBlockString */ + /* Ignored */ + buf[count++] = 0x00; /* bmPINLengthFormat */ + + if (!data->pin1.min_length || !data->pin1.max_length) + return SC_ERROR_INVALID_PIN_LENGTH; + buf[count++] = data->pin1.max_length; /* wPINMaxExtraDigit: max */ + buf[count++] = data->pin1.min_length; /* wPINMaxExtraDigit: min */ + + buf[count++] = 0x02; /* bEntryValidationCondition, keypress only */ + + /* ignore language and T=1 parameters. */ + buf[count++] = 0x00; /* bNumberMessage */ + buf[count++] = 0x00; /* wLangId */ + buf[count++] = 0x00; /* " */ + buf[count++] = 0x00; /* bMsgIndex */ + buf[count++] = 0x00; /* bTeoPrologue */ + buf[count++] = 0x00; /* " */ + buf[count++] = 0x00; /* " */ + + /* APDU itself */ + buf[count++] = apdu->cla; + buf[count++] = apdu->ins; + buf[count++] = apdu->p1; + buf[count++] = apdu->p2; + + /* If the effective PIN length offset == 4 (Lc) the condition is + * not possible to handle with standard CCID capabilities, as + * CCID defines all reader insertion offsets as relative to the first + * byte _after_ Lc ... Too bad. + * Do a special reader-dependant trick that is known to work with SPR532 + * reader and previously mentioned library versions (We omit the APDU + * from the command block and send only APDU headers) + * + * Otherwise we assume a proper APDU and CCID compatible operations + * and the APDU is copied verbatim. + */ + if (data->pin1.length_offset > 4) { + memcpy(&buf[count], apdu->data, apdu->datalen); + count += apdu->datalen; + } + + *size = count; + return SC_SUCCESS; +} + +/* Do the PIN command */ +int +ccid_pin_cmd(struct sc_reader *reader, sc_slot_info_t * slot, + struct sc_pin_cmd_data *data) +{ + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE]; + size_t rcount = sizeof(rbuf), scount = 0; + int r; + unsigned long code; + sc_apdu_t *apdu; + + SC_FUNC_CALLED(reader->ctx, 3); + + /* The APDU must be provided by the card driver */ + if (!data->apdu) { + sc_error(reader->ctx, "No APDU provided for CCID PinPad verification!"); + return SC_ERROR_NOT_SUPPORTED; + } + + /* Only T=0 is currently supported */ + if (slot->active_protocol != SC_PROTO_T0) { + sc_error(reader->ctx, "Only T=0 is currently supported!"); + return SC_ERROR_NOT_SUPPORTED; + } + + apdu = data->apdu; + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + r = ccid_build_verify_pin_block(sbuf, &scount, data); + code = IOCTL_SMARTCARD_VENDOR_VERIFY_PIN; + break; + case SC_PIN_CMD_CHANGE: + case SC_PIN_CMD_UNBLOCK: + return SC_ERROR_NOT_SUPPORTED; + break; + default: + sc_error(reader->ctx, "Unknown PIN command %d", data->cmd); + return SC_ERROR_NOT_SUPPORTED; + } + + /* If CCID block building failed, we fail too */ + SC_TEST_RET(reader->ctx, r, "CCID PIN block building failed!"); + + /* The slot must be manually locked, as the control does not pass through card.c + * wrappers that lock the card (card_transmit is not OK in this case, as it assumes + * a proper APDU as a parameter, not a arbitary binary blob to be sent to the reader) + */ + r = reader->ops->lock(reader, slot); + SC_TEST_RET(reader->ctx, r, "CCID PIN: Could not lock!"); + r = reader->ops->transmit(reader, slot, sbuf, scount, rbuf, &rcount, code); + reader->ops->unlock(reader, slot); + + SC_TEST_RET(reader->ctx, r, "CCID PIN block transmit failed!"); + + /* We expect only two bytes of result data (SW1 and SW2) */ + if (rcount != 2) { + SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + /* Extract the SWs for the result APDU */ + apdu->sw1 = (unsigned int) rbuf[rcount - 2]; + apdu->sw2 = (unsigned int) rbuf[rcount - 1]; + + /* PIN command completed, all is good */ + return SC_SUCCESS; +} +#endif /* MP_CCID_PINPAD */ diff --git a/src/libopensc/pinpad-ccid.h b/src/libopensc/pinpad-ccid.h new file mode 100644 index 00000000..e240a5d4 --- /dev/null +++ b/src/libopensc/pinpad-ccid.h @@ -0,0 +1,32 @@ +/* + * pinpad-ccid.h: CCID ifdhandler control codes for pinpad support. + * + * Copy from pcsc-lite package created by Ludovic Rousseau + * + * Martin Paljak + */ + +#ifdef MP_CCID_PINPAD +#ifndef _PINPAD_CCID_H +#define _PINPAD_CCID_H + +#define SCARD_CTL_CODE(code) (0x42000000 + (code)) + +#define IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE SCARD_CTL_CODE(1) +#define IOCTL_SMARTCARD_VENDOR_VERIFY_PIN SCARD_CTL_CODE(2) +#define IOCTL_SMARTCARD_VENDOR_MODIFY_PIN SCARD_CTL_CODE(3) +#define IOCTL_SMARTCARD_VENDOR_TRANSFER_PIN SCARD_CTL_CODE(4) + +#define SC_CCID_PIN_TIMEOUT 30 + +#define SC_CCID_PIN_ENCODING_BIN 0x00 +#define SC_CCID_PIN_ENCODING_BCD 0x01 +#define SC_CCID_PIN_ENCODING_ASCII 0x02 + +#define SC_CCID_PIN_UNITS_BYTES 0x80 + +/* CCID reader operation for pin commands */ +int ccid_pin_cmd(struct sc_reader *, sc_slot_info_t *, struct sc_pin_cmd_data *); + +#endif /* _PINPAD_CCID_H */ +#endif /* MP_CCID_PINPAD */ diff --git a/src/libopensc/reader-ctapi.c b/src/libopensc/reader-ctapi.c index 216844a5..6218d8ac 100644 --- a/src/libopensc/reader-ctapi.c +++ b/src/libopensc/reader-ctapi.c @@ -105,7 +105,7 @@ static int refresh_slot_attributes(struct sc_reader *reader, static int ctapi_transmit(struct sc_reader *reader, struct sc_slot_info *slot, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, - int control) + unsigned long control) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); u8 dad, sad; diff --git a/src/libopensc/reader-openct.c b/src/libopensc/reader-openct.c index 47029dcf..36a19507 100644 --- a/src/libopensc/reader-openct.c +++ b/src/libopensc/reader-openct.c @@ -45,7 +45,7 @@ static int openct_reader_disconnect(struct sc_reader *reader, static int openct_reader_transmit(struct sc_reader *reader, struct sc_slot_info *slot, const u8 *sendbuf, size_t sendsize, - u8 *recvbuf, size_t *recvsize, int control); + u8 *recvbuf, size_t *recvsize, unsigned long control); static int openct_reader_perform_verify(struct sc_reader *reader, struct sc_slot_info *slot, struct sc_pin_cmd_data *info); @@ -274,7 +274,7 @@ int openct_reader_transmit(struct sc_reader *reader, struct sc_slot_info *slot, const u8 *sendbuf, size_t sendsize, - u8 *recvbuf, size_t *recvsize, int control) + u8 *recvbuf, size_t *recvsize, unsigned long control) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; diff --git a/src/libopensc/reader-pcsc.c b/src/libopensc/reader-pcsc.c index fe61853d..5f779bf1 100644 --- a/src/libopensc/reader-pcsc.c +++ b/src/libopensc/reader-pcsc.c @@ -21,6 +21,9 @@ #include "internal.h" #ifdef HAVE_PCSC #include "ctbcs.h" +#ifdef MP_CCID_PINPAD +#include "pinpad-ccid.h" +#endif #include #include #include @@ -125,7 +128,7 @@ static DWORD opensc_proto_to_pcsc(unsigned int proto) static int pcsc_transmit(struct sc_reader *reader, struct sc_slot_info *slot, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, - int control) + unsigned long control) { SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; @@ -155,7 +158,7 @@ static int pcsc_transmit(struct sc_reader *reader, struct sc_slot_info *slot, rv = SCardControl(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); #else - rv = SCardControl(card, 0, sendbuf, dwSendLength, + rv = SCardControl(card, (DWORD) control, sendbuf, dwSendLength, recvbuf, dwRecvLength, &dwRecvLength); #endif } @@ -436,6 +439,25 @@ static int pcsc_connect(struct sc_reader *reader, struct sc_slot_info *slot) slot->active_protocol = pcsc_proto_to_opensc(active_proto); pslot->pcsc_card = card_handle; +#ifdef MP_CCID_PINPAD + /* check for PINPAD support, addon by -mp */ + { + unsigned char attribute[1]; + DWORD attribute_length; + sc_debug(reader->ctx, "Testing for CCID pinpad support ... "); + /* See if this reader supports pinpad... */ + attribute_length = 1; + rv = SCardGetAttrib(pslot->pcsc_card, IOCTL_SMARTCARD_VENDOR_VERIFY_PIN, attribute, &attribute_length); + if (rv == SCARD_S_SUCCESS) { + if (attribute[0] != 0) { + sc_debug(reader->ctx, "Reader supports CCID pinpad"); + slot->capabilities |= SC_SLOT_CAP_PIN_PAD; + } + } else { + PCSC_ERROR(reader->ctx, "SCardGetAttrib failed", rv) + } + } +#endif return 0; } @@ -618,10 +640,13 @@ struct sc_reader_driver * sc_get_pcsc_driver(void) pcsc_ops.release = pcsc_release; pcsc_ops.connect = pcsc_connect; pcsc_ops.disconnect = pcsc_disconnect; +#ifdef MP_CCID_PINPAD + pcsc_ops.perform_verify = ccid_pin_cmd; +#else pcsc_ops.perform_verify = ctbcs_pin_cmd; +#endif pcsc_ops.wait_for_event = pcsc_wait_for_event; return &pcsc_drv; } - #endif /* HAVE_PCSC */