aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scd/apdu.c497
-rw-r--r--scd/apdu.h6
-rw-r--r--scd/app-dinsig.c2
-rw-r--r--scd/app-nks.c4
-rw-r--r--scd/app-openpgp.c133
-rw-r--r--scd/app.c8
-rw-r--r--scd/ccid-driver.c134
-rw-r--r--scd/iso7816.c107
-rw-r--r--scd/iso7816.h13
-rw-r--r--scd/pcsc-wrapper.c50
10 files changed, 786 insertions, 168 deletions
diff --git a/scd/apdu.c b/scd/apdu.c
index 541dd05fa..0e52909f4 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -61,6 +61,7 @@
#include "apdu.h"
#include "ccid-driver.h"
+#include "iso7816.h"
/* Due to conflicting use of threading libraries we usually can't link
@@ -109,6 +110,8 @@ struct reader_table_s {
int (*check_keypad)(int, int, int, int, int, int);
void (*dump_status_reader)(int);
int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
+ int (*keypad_verify)(int, int, int, int, int, struct pininfo_s *);
+ int (*keypad_modify)(int, int, int, int, int, struct pininfo_s *);
struct {
ccid_driver_t handle;
@@ -117,6 +120,8 @@ struct reader_table_s {
unsigned long context;
unsigned long card;
unsigned long protocol;
+ unsigned long verify_ioctl;
+ unsigned long modify_ioctl;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
@@ -218,6 +223,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_W_REMOVED_CARD 0x80100069
+#define CM_IOCTL_GET_FEATURE_REQUEST (0x42000000 + 3400)
+#define FEATURE_VERIFY_PIN_DIRECT 0x06
+#define FEATURE_MODIFY_PIN_DIRECT 0x07
+
+
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
@@ -286,6 +296,13 @@ long (* DLSTDCALL pcsc_transmit) (unsigned long card,
unsigned long *recv_len);
long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
+long (* DLSTDCALL pcsc_control) (unsigned long card,
+ unsigned long control_code,
+ const void *send_buffer,
+ unsigned long send_len,
+ void *recv_buffer,
+ unsigned long recv_len,
+ unsigned long *bytes_returned);
/* Flag set if PC/SC returned the no-service error. */
static int pcsc_no_service;
@@ -297,6 +314,12 @@ static int reset_pcsc_reader (int slot);
static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
unsigned int *status,
unsigned int *changed);
+static int check_pcsc_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen);
+static int pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
+ struct pininfo_s *pininfo);
+static int pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
+ struct pininfo_s *pininfo);
@@ -340,9 +363,11 @@ new_reader_slot (void)
reader_table[reader].reset_reader = NULL;
reader_table[reader].get_status_reader = NULL;
reader_table[reader].send_apdu_reader = NULL;
- reader_table[reader].check_keypad = NULL;
+ reader_table[reader].check_keypad = check_pcsc_keypad;
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].set_progress_cb = NULL;
+ reader_table[reader].keypad_verify = pcsc_keypad_verify;
+ reader_table[reader].keypad_modify = pcsc_keypad_modify;
reader_table[reader].used = 1;
reader_table[reader].any_status = 0;
@@ -353,6 +378,8 @@ new_reader_slot (void)
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
+ reader_table[reader].pcsc.verify_ioctl = 0;
+ reader_table[reader].pcsc.modify_ioctl = 0;
return reader;
}
@@ -627,6 +654,8 @@ open_ct_reader (int port)
reader_table[reader].send_apdu_reader = ct_send_apdu;
reader_table[reader].check_keypad = NULL;
reader_table[reader].dump_status_reader = ct_dump_reader_status;
+ reader_table[reader].keypad_verify = NULL;
+ reader_table[reader].keypad_modify = NULL;
dump_reader_status (reader);
return reader;
@@ -1154,6 +1183,150 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifndef NEED_PCSC_WRAPPER
static int
+control_pcsc_direct (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+ long err;
+
+ err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
+ cntlbuf, len, buffer, *buflen, buflen);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+control_pcsc_wrapped (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+ long err = PCSC_E_NOT_TRANSACTED;
+ reader_table_t slotp;
+ unsigned char msgbuf[9];
+ int i, n;
+ size_t full_len;
+
+ slotp = reader_table + slot;
+
+ msgbuf[0] = 0x06; /* CONTROL command. */
+ msgbuf[1] = ((len + 4) >> 24);
+ msgbuf[2] = ((len + 4) >> 16);
+ msgbuf[3] = ((len + 4) >> 8);
+ msgbuf[4] = ((len + 4) );
+ msgbuf[5] = (ioctl_code >> 24);
+ msgbuf[6] = (ioctl_code >> 16);
+ msgbuf[7] = (ioctl_code >> 8);
+ msgbuf[8] = (ioctl_code );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 9)
+ || writen (slotp->pcsc.req_fd, cntlbuf, len))
+ {
+ log_error ("error sending PC/SC CONTROL request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ full_len = len;
+
+ n = *buflen < len ? *buflen : len;
+ if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ *buflen = n;
+
+ full_len -= len;
+ if (full_len)
+ {
+ log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+ err = PCSC_E_INVALID_VALUE;
+ }
+ /* We need to read any rest of the response, to keep the
+ protocol running. */
+ while (full_len)
+ {
+ unsigned char dummybuf[128];
+
+ n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+ if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ if (!err)
+ return 0;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return pcsc_error_to_sw (err);
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+
+/* Do some control with the value of IOCTL_CODE to the card inserted
+ to SLOT. Input buffer is specified by CNTLBUF of length LEN.
+ Output buffer is specified by BUFFER of length *BUFLEN, and the
+ actual output size will be stored at BUFLEN. Returns: A status word.
+ This routine is used for PIN pad input support. */
+static int
+control_pcsc (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return control_pcsc_wrapped (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#else
+ return control_pcsc_direct (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#endif
+}
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
close_pcsc_reader_direct (int slot)
{
pcsc_release_context (reader_table[slot].pcsc.context);
@@ -1793,6 +1966,241 @@ open_pcsc_reader (const char *portstr)
}
+/* Check whether the reader supports the ISO command code COMMAND
+ on the keypad. Return 0 on success. */
+static int
+check_pcsc_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ unsigned char buf[256];
+ size_t len = 256;
+ int sw;
+
+ (void)pin_mode;
+ (void)pinlen_min;
+ (void)pinlen_max;
+ (void)pin_padlen;
+
+ check_again:
+ if (command == ISO7816_VERIFY)
+ {
+ if (reader_table[slot].pcsc.verify_ioctl == (unsigned long)-1)
+ return SW_NOT_SUPPORTED;
+ else if (reader_table[slot].pcsc.verify_ioctl != 0)
+ return 0; /* Success */
+ }
+ else if (command == ISO7816_CHANGE_REFERENCE_DATA)
+ {
+ if (reader_table[slot].pcsc.modify_ioctl == (unsigned long)-1)
+ return SW_NOT_SUPPORTED;
+ else if (reader_table[slot].pcsc.modify_ioctl != 0)
+ return 0; /* Success */
+ }
+ else
+ return SW_NOT_SUPPORTED;
+
+ reader_table[slot].pcsc.verify_ioctl = (unsigned long)-1;
+ reader_table[slot].pcsc.modify_ioctl = (unsigned long)-1;
+
+ sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
+ if (sw)
+ return SW_NOT_SUPPORTED;
+ else
+ {
+ unsigned char *p = buf;
+
+ while (p < buf + len)
+ {
+ unsigned char code = *p++;
+
+ p++; /* Skip length */
+ if (code == FEATURE_VERIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.verify_ioctl
+ = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ else if (code == FEATURE_MODIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.modify_ioctl
+ = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ }
+ }
+
+ goto check_again;
+}
+
+
+#define PIN_VERIFY_STRUCTURE_SIZE 24
+static int
+pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
+ struct pininfo_s *pininfo)
+{
+ int sw;
+ unsigned char *pin_verify;
+ int len = PIN_VERIFY_STRUCTURE_SIZE;
+ unsigned char result[2];
+ size_t resultlen = 2;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->mode != 1)
+ return SW_NOT_SUPPORTED;
+
+ if (pininfo->padlen != 0)
+ return SW_NOT_SUPPORTED;
+
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 25;
+
+ /* Note that the 25 is the maximum value the SPR532 allows. */
+ if (pininfo->minlen < 1 || pininfo->minlen > 25
+ || pininfo->maxlen < 1 || pininfo->maxlen > 25
+ || pininfo->minlen > pininfo->maxlen)
+ return SW_HOST_INV_VALUE;
+
+ pin_verify = xtrymalloc (len);
+ if (!pin_verify)
+ return SW_HOST_OUT_OF_CORE;
+
+ pin_verify[0] = 0x00; /* bTimerOut */
+ pin_verify[1] = 0x00; /* bTimerOut2 */
+ pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_verify[3] = 0x00; /* bmPINBlockString */
+ pin_verify[4] = 0x00; /* bmPINLengthFormat */
+ pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_verify[7] |= 0x01; /* Max size reached. */
+ pin_verify[8] = 0xff; /* bNumberMessage: Default */
+ pin_verify[9] = 0x09; /* wLangId: 0x0409: US English */
+ pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */
+ pin_verify[11] = 0x00; /* bMsgIndex */
+ pin_verify[12] = 0x00; /* bTeoPrologue[0] */
+ pin_verify[13] = 0x00; /* bTeoPrologue[1] */
+ pin_verify[14] = 0x00; /* bTeoPrologue[2] */
+ pin_verify[15] = 0x05; /* ulDataLength */
+ pin_verify[16] = 0x00; /* ulDataLength */
+ pin_verify[17] = 0x00; /* ulDataLength */
+ pin_verify[18] = 0x00; /* ulDataLength */
+ pin_verify[19] = class; /* abData[0] */
+ pin_verify[20] = ins; /* abData[1] */
+ pin_verify[21] = p0; /* abData[2] */
+ pin_verify[22] = p1; /* abData[3] */
+ pin_verify[23] = 0x00; /* abData[4] */
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
+ pin_verify, len, result, &resultlen);
+ xfree (pin_verify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
+
+
+#define PIN_MODIFY_STRUCTURE_SIZE 29
+static int
+pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
+ struct pininfo_s *pininfo)
+{
+ int sw;
+ unsigned char *pin_modify;
+ int len = PIN_MODIFY_STRUCTURE_SIZE;
+ unsigned char result[2];
+ size_t resultlen = 2;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->mode != 1)
+ return SW_NOT_SUPPORTED;
+
+ if (pininfo->padlen != 0)
+ return SW_NOT_SUPPORTED;
+
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 25;
+
+ /* Note that the 25 is the maximum value the SPR532 allows. */
+ if (pininfo->minlen < 1 || pininfo->minlen > 25
+ || pininfo->maxlen < 1 || pininfo->maxlen > 25
+ || pininfo->minlen > pininfo->maxlen)
+ return SW_HOST_INV_VALUE;
+
+ pin_modify = xtrymalloc (len);
+ if (!pin_modify)
+ return SW_HOST_OUT_OF_CORE;
+
+ pin_modify[0] = 0x00; /* bTimerOut */
+ pin_modify[1] = 0x00; /* bTimerOut2 */
+ pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_modify[3] = 0x00; /* bmPINBlockString */
+ pin_modify[4] = 0x00; /* bmPINLengthFormat */
+ pin_modify[5] = 0x00; /* bInsertionOffsetOld */
+ pin_modify[6] = 0x00; /* bInsertionOffsetNew */
+ pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_modify[9] = (p0 == 0 ? 0x03 : 0x01);
+ /* bConfirmPIN
+ * 0x00: new PIN once
+ * 0x01: new PIN twice (confirmation)
+ * 0x02: old PIN and new PIN once
+ * 0x03: old PIN and new PIN twice (confirmation)
+ */
+ pin_modify[10] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_modify[10] |= 0x01; /* Max size reached. */
+ pin_modify[11] = 0xff; /* bNumberMessage: Default */
+ pin_modify[12] = 0x09; /* wLangId: 0x0409: US English */
+ pin_modify[13] = 0x04; /* wLangId: 0x0409: US English */
+ pin_modify[14] = 0x00; /* bMsgIndex1 */
+ pin_modify[15] = 0x00; /* bMsgIndex2 */
+ pin_modify[16] = 0x00; /* bMsgIndex3 */
+ pin_modify[17] = 0x00; /* bTeoPrologue[0] */
+ pin_modify[18] = 0x00; /* bTeoPrologue[1] */
+ pin_modify[19] = 0x00; /* bTeoPrologue[2] */
+ pin_modify[20] = 0x05; /* ulDataLength */
+ pin_modify[21] = 0x00; /* ulDataLength */
+ pin_modify[22] = 0x00; /* ulDataLength */
+ pin_modify[23] = 0x00; /* ulDataLength */
+ pin_modify[24] = class; /* abData[0] */
+ pin_modify[25] = ins; /* abData[1] */
+ pin_modify[26] = p0; /* abData[2] */
+ pin_modify[27] = p1; /* abData[3] */
+ pin_modify[28] = 0x00; /* abData[4] */
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, (int)pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
+ pin_modify, len, result, &resultlen);
+ xfree (pin_modify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
#ifdef HAVE_LIBUSB
/*
@@ -1930,6 +2338,35 @@ check_ccid_keypad (int slot, int command, int pin_mode,
}
+static int
+ccid_keypad_operation (int slot, int class, int ins, int p0, int p1,
+ struct pininfo_s *pininfo)
+{
+ unsigned char apdu[4];
+ int err, sw;
+ unsigned char result[2];
+ size_t resultlen = 2;
+
+ apdu[0] = class;
+ apdu[1] = ins;
+ apdu[2] = p0;
+ apdu[3] = p1;
+ err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, sizeof apdu,
+ pininfo->mode, pininfo->minlen, pininfo->maxlen,
+ pininfo->padlen,
+ result, 2, &resultlen);
+ if (err)
+ return err;
+
+ if (resultlen < 2)
+ return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ return sw;
+}
+
+
/* Open the reader and try to read an ATR. */
static int
open_ccid_reader (const char *portstr)
@@ -1974,6 +2411,8 @@ open_ccid_reader (const char *portstr)
reader_table[slot].check_keypad = check_ccid_keypad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status;
reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+ reader_table[slot].keypad_verify = ccid_keypad_operation;
+ reader_table[slot].keypad_modify = ccid_keypad_operation;
/* Our CCID reader code does not support T=0 at all, thus reset the
flag. */
reader_table[slot].is_t0 = 0;
@@ -2266,6 +2705,8 @@ open_rapdu_reader (int portno,
reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
reader_table[slot].check_keypad = NULL;
reader_table[slot].dump_status_reader = NULL;
+ reader_table[slot].keypad_verify = NULL;
+ reader_table[slot].keypad_modify = NULL;
dump_reader_status (slot);
rapdu_msg_release (msg);
@@ -2433,6 +2874,7 @@ apdu_open_reader (const char *portstr, int *r_no_service)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -2445,12 +2887,13 @@ apdu_open_reader (const char *portstr, int *r_no_service)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
log_error ("apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -2462,7 +2905,8 @@ apdu_open_reader (const char *portstr, int *r_no_service)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
return -1;
}
@@ -2634,8 +3078,7 @@ apdu_connect (int slot)
;
else if (!(status & APDU_CARD_PRESENT))
sw = SW_HOST_NO_CARD;
- else if (((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
- || !reader_table[slot].atrlen)
+ else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
sw = SW_HOST_CARD_INACTIVE;
@@ -2877,6 +3320,50 @@ apdu_check_keypad (int slot, int command, int pin_mode,
}
+int
+apdu_keypad_verify (int slot, int class, int ins, int p0, int p1, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ struct pininfo_s pininfo;
+
+ pininfo.mode = pin_mode;
+ pininfo.minlen = pinlen_min;
+ pininfo.maxlen = pinlen_max;
+ pininfo.padlen = pin_padlen;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].keypad_verify)
+ return reader_table[slot].keypad_verify (slot, class, ins, p0, p1,
+ &pininfo);
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_keypad_modify (int slot, int class, int ins, int p0, int p1, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ struct pininfo_s pininfo;
+
+ pininfo.mode = pin_mode;
+ pininfo.minlen = pinlen_min;
+ pininfo.maxlen = pinlen_max;
+ pininfo.padlen = pin_padlen;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].keypad_modify)
+ return reader_table[slot].keypad_modify (slot, class, ins, p0, p1,
+ &pininfo);
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
/* Dispatcher for the actual send_apdu function. Note, that this
function should be called in locked state. */
static int
diff --git a/scd/apdu.h b/scd/apdu.h
index d79f8b48f..94d7449bb 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -114,6 +114,12 @@ int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
int apdu_check_keypad (int slot, int command, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen);
+int apdu_keypad_verify (int slot, int class, int ins, int p0, int p1,
+ int pin_mode, int pinlen_min, int pinlen_max,
+ int pin_padlen);
+int apdu_keypad_modify (int slot, int class, int ins, int p0, int p1,
+ int pin_mode, int pinlen_min, int pinlen_max,
+ int pin_padlen);
int apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data);
diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c
index 46e9a6a9b..697cdf6d1 100644
--- a/scd/app-dinsig.c
+++ b/scd/app-dinsig.c
@@ -304,7 +304,7 @@ verify_pin (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x81, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 076b9139f..b51e1fc97 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -802,8 +802,8 @@ verify_pin (app_t app, int pwid, const char *desc,
gpg_strerror (rc));
return rc;
}
-
- rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo);
+
+ rc = iso7816_verify_kp (app->slot, pwid, &pininfo);
pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
}
else
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 8a71caf36..e50297dee 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1550,7 +1550,7 @@ verify_a_chv (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x80+chvno, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
@@ -1730,7 +1730,7 @@ verify_chv3 (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x83, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
@@ -1912,11 +1912,17 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
int chvno = atoi (chvnostr);
char *resetcode = NULL;
char *oldpinvalue = NULL;
- char *pinvalue;
+ char *pinvalue = NULL;
int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
int set_resetcode = 0;
+ iso7816_pininfo_t pininfo;
+ int use_keypad = 0;
+ int minlen = 6;
(void)ctrl;
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.mode = 1;
+ pininfo.minlen = minlen;
if (reset_mode && chvno == 3)
{
@@ -1960,9 +1966,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
/* Version 2 cards. */
+ if (!opt.disable_keypad
+ && !iso7816_check_keypad (app->slot,
+ ISO7816_CHANGE_REFERENCE_DATA, &pininfo))
+ use_keypad = 1;
+
if (reset_mode)
{
/* To reset a PIN the Admin PIN is required. */
+ use_keypad = 0;
app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
@@ -1973,35 +1985,38 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
}
else if (chvno == 1 || chvno == 3)
{
- int minlen = (chvno ==3)? 8 : 6;
- char *promptbuf = NULL;
- const char *prompt;
-
- if (chvno == 3)
+ if (!use_keypad)
{
- rc = build_enter_admin_pin_prompt (app, &promptbuf);
+ char *promptbuf = NULL;
+ const char *prompt;
+
+ if (chvno == 3)
+ {
+ minlen = 8;
+ rc = build_enter_admin_pin_prompt (app, &promptbuf);
+ if (rc)
+ goto leave;
+ prompt = promptbuf;
+ }
+ else
+ prompt = _("||Please enter the PIN");
+ rc = pincb (pincb_arg, prompt, &oldpinvalue);
+ xfree (promptbuf);
+ promptbuf = NULL;
if (rc)
- goto leave;
- prompt = promptbuf;
- }
- else
- prompt = _("||Please enter the PIN");
- rc = pincb (pincb_arg, prompt, &oldpinvalue);
- xfree (promptbuf);
- promptbuf = NULL;
- if (rc)
- {
- log_info (_("PIN callback returned error: %s\n"),
- gpg_strerror (rc));
- goto leave;
- }
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
- if (strlen (oldpinvalue) < minlen)
- {
- log_info (_("PIN for CHV%d is too short;"
- " minimum length is %d\n"), chvno, minlen);
- rc = gpg_error (GPG_ERR_BAD_PIN);
- goto leave;
+ if (strlen (oldpinvalue) < minlen)
+ {
+ log_info (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), chvno, minlen);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
}
}
else if (chvno == 2)
@@ -2012,8 +2027,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
unsigned char *value;
size_t valuelen;
int remaining;
- int minlen = 8;
+ use_keypad = 0;
+ minlen = 8;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
@@ -2029,14 +2045,14 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
log_error (_("Reset Code not or not anymore available\n"));
rc = gpg_error (GPG_ERR_BAD_PIN);
goto leave;
- }
-
+ }
+
rc = pincb (pincb_arg,
_("||Please enter the Reset Code for the card"),
- &resetcode);
+ &resetcode);
if (rc)
{
- log_info (_("PIN callback returned error: %s\n"),
+ log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
goto leave;
}
@@ -2060,17 +2076,20 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
else
app->did_chv1 = app->did_chv2 = 0;
- /* TRANSLATORS: Do not translate the "|*|" prefixes but
- keep it at the start of the string. We need this elsewhere
- to get some infos on the string. */
- rc = pincb (pincb_arg,
- set_resetcode? _("|RN|New Reset Code") :
- chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
- &pinvalue);
- if (rc)
+ if (!use_keypad)
{
- log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
- goto leave;
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ rc = pincb (pincb_arg,
+ set_resetcode? _("|RN|New Reset Code") :
+ chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
+ &pinvalue);
+ if (rc)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
}
@@ -2130,10 +2149,28 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
/* Version 2 cards. */
assert (chvno == 1 || chvno == 3);
-
- rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
- oldpinvalue, strlen (oldpinvalue),
- pinvalue, strlen (pinvalue));
+
+ if (use_keypad)
+ {
+ rc = pincb (pincb_arg,
+ chvno == 3 ?
+ _("||Please enter the Admin PIN and New Admin PIN") :
+ _("||Please enter the PIN and New PIN"), NULL);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+ rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0,
+ &pininfo);
+ /* Dismiss the prompt. */
+ pincb (pincb_arg, NULL, NULL);
+ }
+ else
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+ oldpinvalue, strlen (oldpinvalue),
+ pinvalue, strlen (pinvalue));
}
if (pinvalue)
diff --git a/scd/app.c b/scd/app.c
index a23c4a546..7cbbf6ea6 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -381,11 +381,11 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
err = app_select_nks (app);
if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
err = app_select_p15 (app);
- if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
- err = app_select_dinsig (app);
if (err && is_app_allowed ("geldkarte")
&& (!name || !strcmp (name, "geldkarte")))
err = app_select_geldkarte (app);
+ if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
+ err = app_select_dinsig (app);
if (err && name)
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
@@ -419,8 +419,10 @@ get_supported_applications (void)
"openpgp",
"nks",
"p15",
- "dinsig",
"geldkarte",
+ "dinsig",
+ /* Note: "undefined" is not listed here because it needs special
+ treatment by the client. */
NULL
};
int idx;
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 9a07c7955..5281a2f8a 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -210,7 +210,8 @@ enum {
VENDOR_OMNIKEY= 0x076b,
VENDOR_GEMPC = 0x08e6,
VENDOR_KAAN = 0x0d46,
- VENDOR_FSIJ = 0x234B
+ VENDOR_FSIJ = 0x234b,
+ VENDOR_VASCO = 0x1a44
};
/* Some product ids. */
@@ -220,7 +221,7 @@ enum {
#define SCM_SCR3320 0x5117
#define SCM_SPR532 0xe003
#define CHERRY_ST2000 0x003e
-
+#define VASCO_920 0x0920
/* A list and a table with special transport descriptions. */
enum {
@@ -2589,8 +2590,8 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
/* The maximum length for a short APDU T=1 block is 261. For an
extended APDU T=1 block the maximum length 65544; however
- extended APDU exchange level is not yet supported. */
- if (apdulen > 261)
+ extended APDU exchange level is not fully supported yet. */
+ if (apdulen > 289)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
msg[0] = PC_to_RDR_XfrBlock;
@@ -2612,10 +2613,53 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
RDR_to_PC_DataBlock, seqno, 5000, 0);
if (rc)
return rc;
-
- apdu = msg + 10;
- apdulen = msglen - 10;
-
+
+ if (msg[9] == 1)
+ {
+ size_t total_msglen = msglen;
+
+ while (1)
+ {
+ unsigned char status;
+
+ msg = recv_buffer + total_msglen;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi; /* bBWI */
+ msg[8] = 0x10; /* Request next data block */
+ msg[9] = 0;
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ rc = bulk_in (handle, msg, sizeof recv_buffer - total_msglen, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+ status = msg[9];
+ memmove (msg, msg+10, msglen - 10);
+ total_msglen += msglen - 10;
+ if (total_msglen >= sizeof recv_buffer)
+ return CCID_DRIVER_ERR_OUT_OF_CORE;
+
+ if (status == 0x02)
+ break;
+ }
+
+ apdu = recv_buffer + 10;
+ apdulen = total_msglen - 10;
+ }
+ else
+ {
+ apdu = msg + 10;
+ apdulen = msglen - 10;
+ }
+
if (resp)
{
if (apdulen > maxresplen)
@@ -3058,7 +3102,7 @@ ccid_transceive_secure (ccid_driver_t handle,
if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1))
;
else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2))
- return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */
+ ;
else
return CCID_DRIVER_ERR_NO_KEYPAD;
@@ -3086,6 +3130,9 @@ ccid_transceive_secure (ccid_driver_t handle,
case VENDOR_SCM: /* Tested with SPR 532. */
case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
break;
+ case VENDOR_VASCO: /* Tested with DIGIPASS 920 */
+ pinlen_max = 15;
+ break;
case VENDOR_CHERRY:
/* The CHERRY XX44 keyboard echos an asterisk for each entered
character on the keyboard channel. We use a special variant
@@ -3120,7 +3167,8 @@ ccid_transceive_secure (ccid_driver_t handle,
msg[7] = 0; /* bBWI */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
- msg[10] = 0; /* Perform PIN verification. */
+ msg[10] = apdu_buf[1] == 0x20 ? 0 : 1;
+ /* Perform PIN verification or PIN modification. */
msg[11] = 0; /* Timeout in seconds. */
msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
if (handle->id_vendor == VENDOR_SCM)
@@ -3139,28 +3187,58 @@ ccid_transceive_secure (ccid_driver_t handle,
Units are bytes, position is 0. */
}
- /* The following is a little endian word. */
- msg[15] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
- msg[16] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
+ msglen = 15;
+ if (apdu_buf[1] == 0x24)
+ {
+ msg[msglen++] = 0; /* bInsertionOffsetOld */
+ msg[msglen++] = 0; /* bInsertionOffsetNew */
+ }
- msg[17] = 0x02; /* bEntryValidationCondition:
- Validation key pressed */
+ /* The following is a little endian word. */
+ msg[msglen++] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
+ msg[msglen++] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
+
+ if (apdu_buf[1] == 0x24)
+ msg[msglen++] = apdu_buf[2] == 0 ? 0x03 : 0x01;
+ /* bConfirmPIN
+ * 0x00: new PIN once
+ * 0x01: new PIN twice (confirmation)
+ * 0x02: old PIN and new PIN once
+ * 0x03: old PIN and new PIN twice (confirmation)
+ */
+
+ msg[msglen] = 0x02; /* bEntryValidationCondition:
+ Validation key pressed */
if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
- msg[17] |= 0x01; /* Max size reached. */
- msg[18] = 0xff; /* bNumberMessage: Default. */
- msg[19] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
- msg[20] = 0x04; /* wLangId-High. */
- msg[21] = 0; /* bMsgIndex. */
+ msg[msglen] |= 0x01; /* Max size reached. */
+ msglen++;
+
+ if (apdu_buf[1] == 0x20)
+ msg[msglen++] = 0xff; /* bNumberMessage: Default. */
+ else
+ msg[msglen++] = 0x03; /* bNumberMessage. */
+
+ msg[msglen++] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
+ msg[msglen++] = 0x04; /* wLangId-High. */
+
+ if (apdu_buf[1] == 0x20)
+ msg[msglen++] = 0; /* bMsgIndex. */
+ else
+ {
+ msg[msglen++] = 0; /* bMsgIndex1. */
+ msg[msglen++] = 1; /* bMsgIndex2. */
+ msg[msglen++] = 2; /* bMsgIndex3. */
+ }
+
/* bTeoProlog follows: */
- msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
- msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
- msg[24] = 0; /* The apdulen will be filled in by the reader. */
+ msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */
+ msg[msglen++] = 0; /* The apdulen will be filled in by the reader. */
/* APDU follows: */
- msg[25] = apdu_buf[0]; /* CLA */
- msg[26] = apdu_buf[1]; /* INS */
- msg[27] = apdu_buf[2]; /* P1 */
- msg[28] = apdu_buf[3]; /* P2 */
- msglen = 29;
+ msg[msglen++] = apdu_buf[0]; /* CLA */
+ msg[msglen++] = apdu_buf[1]; /* INS */
+ msg[msglen++] = apdu_buf[2]; /* P1 */
+ msg[msglen++] = apdu_buf[3]; /* P2 */
if (cherry_mode)
msg[msglen++] = 0;
/* An EDC is not required. */
diff --git a/scd/iso7816.c b/scd/iso7816.c
index e3f2c1beb..12daff9fb 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -281,22 +281,16 @@ iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo)
/* Perform a VERIFY command on SLOT using the card holder verification
- vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL
- the keypad of the reader will be used. Returns 0 on success. */
+ vector CHVNO. With PININFO non-NULL the keypad of the reader will
+ be used. Returns 0 on success. */
gpg_error_t
-iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
- iso7816_pininfo_t *pininfo)
+iso7816_verify_kp (int slot, int chvno, iso7816_pininfo_t *pininfo)
{
int sw;
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ sw = apdu_keypad_verify (slot, 0x00, CMD_VERIFY, 0, chvno,
+ pininfo->mode, pininfo->minlen, pininfo->maxlen,
+ pininfo->padlen);
return map_sw (sw);
}
@@ -305,49 +299,27 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
gpg_error_t
iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
{
- return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL);
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ return map_sw (sw);
}
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
- verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
- 0), a "change reference data" is done, otherwise an "exchange
- reference data". The new reference data is expected in NEWCHV of
- length NEWCHVLEN. With PININFO non-NULL the keypad of the reader
- will be used. */
+ verification vector CHVNO. With PININFO non-NULL the keypad of the
+ reader will be used. If IS_EXCHANGE is 0, a "change reference
+ data" is done, otherwise an "exchange reference data". */
gpg_error_t
-iso7816_change_reference_data_kp (int slot, int chvno,
- const char *oldchv, size_t oldchvlen,
- const char *newchv, size_t newchvlen,
+iso7816_change_reference_data_kp (int slot, int chvno, int is_exchange,
iso7816_pininfo_t *pininfo)
{
int sw;
- char *buf;
- if ((!oldchv && oldchvlen)
- || (oldchv && !oldchvlen)
- || !newchv || !newchvlen )
- return gpg_error (GPG_ERR_INV_VALUE);
-
- buf = xtrymalloc (oldchvlen + newchvlen);
- if (!buf)
- return gpg_error (gpg_err_code_from_errno (errno));
- if (oldchvlen)
- memcpy (buf, oldchv, oldchvlen);
- memcpy (buf+oldchvlen, newchv, newchvlen);
-
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
- oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
- oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
- xfree (buf);
+ sw = apdu_keypad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ is_exchange ? 1 : 0,
+ chvno, pininfo->mode, pininfo->minlen,
+ pininfo->maxlen, pininfo->padlen);
return map_sw (sw);
-
}
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
@@ -360,33 +332,26 @@ iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen)
{
- return iso7816_change_reference_data_kp (slot, chvno, oldchv, oldchvlen,
- newchv, newchvlen, NULL);
-}
-
-
-gpg_error_t
-iso7816_reset_retry_counter_kp (int slot, int chvno,
- const char *newchv, size_t newchvlen,
- iso7816_pininfo_t *pininfo)
-{
int sw;
+ char *buf;
- if (!newchv || !newchvlen )
+ if ((!oldchv && oldchvlen)
+ || (oldchv && !oldchvlen)
+ || !newchv || !newchvlen )
return gpg_error (GPG_ERR_INV_VALUE);
- /* FIXME: The keypad mode has not yet been tested. */
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER,
- 2, chvno, newchvlen, newchv,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
- 2, chvno, newchvlen, newchv);
+ buf = xtrymalloc (oldchvlen + newchvlen);
+ if (!buf)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ if (oldchvlen)
+ memcpy (buf, oldchv, oldchvlen);
+ memcpy (buf+oldchvlen, newchv, newchvlen);
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+ xfree (buf);
return map_sw (sw);
+
}
@@ -409,7 +374,11 @@ gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen)
{
- return iso7816_reset_retry_counter_kp (slot, chvno, newchv, newchvlen, NULL);
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+ 2, chvno, newchvlen, newchv);
+ return map_sw (sw);
}
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 85197124d..d12855b1c 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -63,22 +63,15 @@ gpg_error_t iso7816_check_keypad (int slot, int command,
iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen);
-gpg_error_t iso7816_verify_kp (int slot,
- int chvno, const char *chv, size_t chvlen,
- iso7816_pininfo_t *pininfo);
+gpg_error_t iso7816_verify_kp (int slot, int chvno, iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen);
gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno,
- const char *oldchv, size_t oldchvlen,
- const char *newchv, size_t newchvlen,
- iso7816_pininfo_t *pininfo);
+ int is_exchange,
+ iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen);
-gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
- const char *newchv,
- size_t newchvlen,
- iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
const char *data,
size_t datalen);
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index ee974ac7b..73b25f4b3 100644
--- a/scd/pcsc-wrapper.c
+++ b/scd/pcsc-wrapper.c
@@ -178,6 +178,13 @@ long (* pcsc_transmit) (unsigned long card,
unsigned long *recv_len);
long (* pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
+long (* pcsc_control) (unsigned long card,
+ unsigned long control_code,
+ const void *send_buffer,
+ unsigned long send_len,
+ void *recv_buffer,
+ unsigned long recv_len,
+ unsigned long *bytes_returned);
@@ -335,6 +342,7 @@ load_pcsc_driver (const char *libname)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -347,13 +355,14 @@ load_pcsc_driver (const char *libname)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
fprintf (stderr,
"apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -365,7 +374,8 @@ load_pcsc_driver (const char *libname)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
exit (1);
}
@@ -720,6 +730,38 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
}
+/* Handle a control request. The argument is expected to be a buffer
+ which contains CONTROL_CODE (4-byte) and INPUT_BYTES.
+ */
+static void
+handle_control (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ unsigned long ioctl_code;
+ unsigned long recv_len = 1024;
+ unsigned char buffer[1024];
+
+ if (arglen < 4)
+ bad_request ("CONTROL");
+
+ ioctl_code = (argbuf[0] << 24) | (argbuf[1] << 16) | (argbuf[2] << 8) | argbuf[3];
+ argbuf += 4;
+ arglen -= 4;
+
+ recv_len = sizeof (buffer);
+ err = pcsc_control (pcsc_card, ioctl_code, argbuf, arglen,
+ buffer, recv_len, &recv_len);
+ if (err)
+ {
+ if (verbose)
+ fprintf (stderr, PGM": pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+ request_succeeded (buffer, recv_len);
+}
+
static void
print_version (int with_help)
@@ -831,6 +873,10 @@ main (int argc, char **argv)
handle_reset (argbuffer, arglen);
break;
+ case 6:
+ handle_control (argbuffer, arglen);
+ break;
+
default:
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
exit (1);