[git] GnuPG - branch, scd-backport-2-0, updated. gnupg-2.0.19-26-g29b431f

by NIIBE Yutaka cvs at cvs.gnupg.org
Fri Jun 15 11:07:23 CEST 2012


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, scd-backport-2-0 has been updated
       via  29b431fcf97a9e615d34a7a40e570e63834b360a (commit)
       via  3e39a9aeaadd9d4ea7ea578dc77504bd1fd6e30a (commit)
       via  d138fe5c30c877856b4c397147bb0090705821f8 (commit)
       via  2586eac997911869008cb3ba57865a7d63a2afc3 (commit)
       via  685955444823a5ca1190b109043ee773bf206bae (commit)
       via  f78cdf6d45708b67bc3d7bf7b90283254fdd23fd (commit)
       via  1b0968ce9a642671f902ceddbafb5af4fe51535b (commit)
       via  b480f18e1de71766fc6b4fb3c50e157d0abffda5 (commit)
       via  7cd8b12e25b6daabedf6fdc2f350327e2116fc78 (commit)
       via  6dc187f2dbf3b5e7322fa40c7e085da12ad373d2 (commit)
      from  cc13771675f83624a7d850b73f92e43e085b71c8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 29b431fcf97a9e615d34a7a40e570e63834b360a
Merge: 3e39a9a cc13771
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Fri Jun 15 16:50:45 2012 +0900

    Merge branch 'scd-backport-2-0' of git+ssh://playfair.gnupg.org/git/gnupg into scd-backport-2-0


commit 3e39a9aeaadd9d4ea7ea578dc77504bd1fd6e30a
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Fri Jun 15 16:46:59 2012 +0900

    scd: Fix merge mistake.
    * scd/iso7816.c (iso7816_reset_retry_counter): Implement.

diff --git a/scd/iso7816.c b/scd/iso7816.c
index dd73fc6..12daff9 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -374,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);
 }
 
 

commit d138fe5c30c877856b4c397147bb0090705821f8
Author: Werner Koch <wk at gnupg.org>
Date:   Thu Dec 15 21:45:35 2011 +0100

    scd: Prefer application Geldkarte over DINSIG.
    
    * scd/app.c (select_application): Reorder application tests.
    --
    
    Although the DINSIG application is available on most German cards, it
    is in reality not used.  Thus showing the Geldkarte application is
    more desirable for a good user experience.
    
    Conflicts:
    
    	scd/app.c

diff --git a/scd/app.c b/scd/app.c
index a23c4a5..7cbbf6e 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;

commit 2586eac997911869008cb3ba57865a7d63a2afc3
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Dec 19 18:26:47 2011 +0100

    scd: Fix for card change returning GPG_ERR_CARD_RESET.
    
    * scd/apdu.c (apdu_connect): Do not test for zero atrlen.
    --
    When gpg-agent prompts for insertion of a card this error would be
    returned.
    
    Co-authored-by: Ben Kibbey <bjk at luxsci.net>

diff --git a/scd/apdu.c b/scd/apdu.c
index 2effa8c..0e52909 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -3078,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;
 
 

commit 685955444823a5ca1190b109043ee773bf206bae
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Fri Jan 6 13:50:21 2012 +0900

    Merge ccid_driver_improvement branch. (backport)
    
    * scd/apdu.c (ccid_keypad_operation): Rename from ccid_keypad_verify.
    (open_ccid_reader): Use ccid_keypad_operation for verify and modify.
    
    * scd/ccid-driver.c (VENDOR_VASCO, VASCO_920): New.
    (ccid_transceive_apdu_level): Permit sending packet where
    apdulen <= 289.  Support receiving packets in a chain.
    (ccid_transceive_secure): Maximum is 15 for VASCO DIGIPASS 920.
    Support keypad_modify method such as CHANGE_REFERENCE_DATA: 0x24.

diff --git a/scd/apdu.c b/scd/apdu.c
index 142426a..2effa8c 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -2339,8 +2339,8 @@ check_ccid_keypad (int slot, int command, int pin_mode,
 
 
 static int
-ccid_keypad_verify (int slot, int class, int ins, int p0, int p1,
-                    struct pininfo_s *pininfo)
+ccid_keypad_operation (int slot, int class, int ins, int p0, int p1,
+		       struct pininfo_s *pininfo)
 {
   unsigned char apdu[4];
   int err, sw;
@@ -2411,8 +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_verify;
-  reader_table[slot].keypad_modify = NULL;
+  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;
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 9a07c79..5281a2f 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. */

commit f78cdf6d45708b67bc3d7bf7b90283254fdd23fd
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Tue Dec 20 13:34:27 2011 +0900

    Add error log and debug log for pcsc_keypad_verify and pcsc_keypad_modify.
    
    * scd/apdu.c (pcsc_keypad_verify): Add debug log and error log.
    (pcsc_keypad_modify): Likewise.

diff --git a/scd/apdu.c b/scd/apdu.c
index 38097c0..142426a 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -2035,7 +2035,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
 {
   int sw;
   unsigned char *pin_verify;
-  unsigned long len = PIN_VERIFY_STRUCTURE_SIZE;
+  int len = PIN_VERIFY_STRUCTURE_SIZE;
   unsigned char result[2];
   size_t resultlen = 2;
 
@@ -2091,12 +2091,21 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   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)
-    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    {
+      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;
 }
 
@@ -2108,7 +2117,7 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
 {
   int sw;
   unsigned char *pin_modify;
-  unsigned long len = PIN_MODIFY_STRUCTURE_SIZE;
+  int len = PIN_MODIFY_STRUCTURE_SIZE;
   unsigned char result[2];
   size_t resultlen = 2;
 
@@ -2175,12 +2184,21 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   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)
-    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    {
+      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;
 }
 

commit 1b0968ce9a642671f902ceddbafb5af4fe51535b
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Fri Dec 2 13:57:12 2011 +0900

    Fix pinpad input support for passphrase modification. (backport)
    
    * apdu.c (pcsc_keypad_verify): Have dummy Lc field with value 0.
    (pcsc_keypad_modify): Likewise.
    (pcsc_keypad_modify): It's only for ISO7816_CHANGE_REFERENCE_DATA.
    bConfirmPIN value is determined by the parameter p0.
    
    * app-openpgp.c (do_change_pin): The flag use_keypad should be 0 when
    reset_mode is on, or resetcode is on.  use_keypad only makes sense for
    iso7816_change_reference_data_kp.
    
    * iso7816.h (iso7816_put_data_kp): Remove.
    (iso7816_reset_retry_counter_kp): Remove.
    (iso7816_reset_retry_counter_with_rc_kp): Remove.
    (iso7816_change_reference_data_kp): Add an argument: IS_EXCHANGE.
    
    * iso7816.c (iso7816_put_data_kp): Remove.
    (iso7816_reset_retry_counter_kp): Remove.
    (iso7816_reset_retry_counter_with_rc_kp): Remove.
    (iso7816_change_reference_data_kp): Add an argument: IS_EXCHANGE.

diff --git a/scd/apdu.c b/scd/apdu.c
index 4062b1e..38097c0 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -2028,7 +2028,7 @@ check_pcsc_keypad (int slot, int command, int pin_mode,
 }
 
 
-#define PIN_VERIFY_STRUCTURE_SIZE 23
+#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)
@@ -2081,7 +2081,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   pin_verify[12] = 0x00; /* bTeoPrologue[0] */
   pin_verify[13] = 0x00; /* bTeoPrologue[1] */
   pin_verify[14] = 0x00; /* bTeoPrologue[2] */
-  pin_verify[15] = 0x04; /* ulDataLength */
+  pin_verify[15] = 0x05; /* ulDataLength */
   pin_verify[16] = 0x00; /* ulDataLength */
   pin_verify[17] = 0x00; /* ulDataLength */
   pin_verify[18] = 0x00; /* ulDataLength */
@@ -2089,6 +2089,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   pin_verify[20] = ins; /* abData[1] */
   pin_verify[21] = p0; /* abData[2] */
   pin_verify[22] = p1; /* abData[3] */
+  pin_verify[23] = 0x00; /* abData[4] */
 
   sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
                      pin_verify, len, result, &resultlen);
@@ -2100,7 +2101,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
 }
 
 
-#define PIN_MODIFY_STRUCTURE_SIZE 28
+#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)
@@ -2145,12 +2146,13 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[6] = 0x00; /* bInsertionOffsetNew */
   pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
   pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
-  pin_modify[9] = 0x03;  /* 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[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.  */
@@ -2163,7 +2165,7 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[17] = 0x00; /* bTeoPrologue[0] */
   pin_modify[18] = 0x00; /* bTeoPrologue[1] */
   pin_modify[19] = 0x00; /* bTeoPrologue[2] */
-  pin_modify[20] = 0x04; /* ulDataLength */
+  pin_modify[20] = 0x05; /* ulDataLength */
   pin_modify[21] = 0x00; /* ulDataLength */
   pin_modify[22] = 0x00; /* ulDataLength */
   pin_modify[23] = 0x00; /* ulDataLength */
@@ -2171,6 +2173,7 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[25] = ins; /* abData[1] */
   pin_modify[26] = p0; /* abData[2] */
   pin_modify[27] = p1; /* abData[3] */
+  pin_modify[28] = 0x00; /* abData[4] */
 
   sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
                      pin_modify, len, result, &resultlen);
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 4bae875..e50297d 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1974,6 +1974,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
       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)
@@ -1982,37 +1983,40 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           if (chvno == 2)
             set_resetcode = 1;
         }
-      else if (!use_keypad && (chvno == 1 || chvno == 3))
+      else if (chvno == 1 || chvno == 3)
         {
-          char *promptbuf = NULL;
-          const char *prompt;
-
-          if (chvno == 3)
+	  if (!use_keypad)
             {
-              minlen = 8;
-              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)
@@ -2024,6 +2028,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           size_t valuelen;
           int remaining;
 
+          use_keypad = 0;
           minlen = 8;
           relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
           if (!relptr || valuelen < 7)
@@ -2042,24 +2047,21 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
               goto leave;
             }
 
-          if (!use_keypad)
+          rc = pincb (pincb_arg,
+                      _("||Please enter the Reset Code for the card"),
+                      &resetcode);
+          if (rc)
             {
-              rc = pincb (pincb_arg,
-                          _("||Please enter the Reset Code for the card"),
-                          &resetcode);
-              if (rc)
-                {
-                  log_info (_("PIN callback returned error: %s\n"),
-                            gpg_strerror (rc));
-                  goto leave;
-                }
-              if (strlen (resetcode) < minlen)
-                {
-                  log_info (_("Reset Code is too short; minimum length is %d\n"),
-                            minlen);
-                  rc = gpg_error (GPG_ERR_BAD_PIN);
-                  goto leave;
-                }
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              goto leave;
+            }
+          if (strlen (resetcode) < minlen)
+            {
+              log_info (_("Reset Code is too short; minimum length is %d\n"),
+                        minlen);
+              rc = gpg_error (GPG_ERR_BAD_PIN);
+              goto leave;
             }
         }
       else
@@ -2093,36 +2095,18 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
 
   if (resetcode)
     {
-      if (use_keypad)
-	{
-	  rc = pincb (pincb_arg,
-		      _("||Please enter the Reset Code for the card and New PIN"),
-		      NULL);
-	  if (rc)
-	    {
-	      log_info (_("PIN callback returned error: %s\n"),
-			gpg_strerror (rc));
-	      goto leave;
-	    }
-	  rc = iso7816_reset_retry_counter_with_rc_kp (app->slot, 0x81,
-						       &pininfo);
-	  pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
-	}
-      else
-	{
-	  char *buffer;
+      char *buffer;
 
-	  buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
-	  if (!buffer)
-	    rc = gpg_error_from_syserror ();
-	  else
-	    {
-	      strcpy (stpcpy (buffer, resetcode), pinvalue);
-	      rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
-							buffer, strlen (buffer));
-	      wipememory (buffer, strlen (buffer));
-	      xfree (buffer);
-	    }
+      buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+      if (!buffer)
+        rc = gpg_error_from_syserror ();
+      else
+        {
+          strcpy (stpcpy (buffer, resetcode), pinvalue);
+          rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+                                                    buffer, strlen (buffer));
+          wipememory (buffer, strlen (buffer));
+          xfree (buffer);
         }
     }
   else if (set_resetcode)
@@ -2131,48 +2115,18 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
         {
           log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
           rc = gpg_error (GPG_ERR_BAD_PIN);
-	}
+        }
       else
-	{
-	  if (use_keypad)
-	    {
-	      rc = pincb (pincb_arg,  _("|RN|New Reset Code"), NULL);
-	      if (rc)
-		{
-		  log_info (_("PIN callback returned error: %s\n"),
-			    gpg_strerror (rc));
-		  goto leave;
-		}
-	      rc = iso7816_put_data_kp (app->slot, 0xD3, &pininfo);
-	      pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
-	    }
-	  else
-	    rc = iso7816_put_data (app->slot, 0, 0xD3,
-				   pinvalue, strlen (pinvalue));
-	}
+        rc = iso7816_put_data (app->slot, 0, 0xD3,
+                               pinvalue, strlen (pinvalue));
     }
   else if (reset_mode)
     {
-      if (use_keypad)
-        {
-          rc = pincb (pincb_arg, _("|N|New PIN"), NULL);
-          if (rc)
-            {
-              log_info (_("PIN callback returned error: %s\n"),
-                        gpg_strerror (rc));
-              goto leave;
-            }
-          rc = iso7816_reset_retry_counter_kp (app->slot, 0x81, &pininfo);
-          pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
-        }
-      else
-        {
-          rc = iso7816_reset_retry_counter (app->slot, 0x81,
-                                            pinvalue, strlen (pinvalue));
-          if (!rc && !app->app_local->extcap.is_v2)
-            rc = iso7816_reset_retry_counter (app->slot, 0x82,
-                                              pinvalue, strlen (pinvalue));
-        }
+      rc = iso7816_reset_retry_counter (app->slot, 0x81,
+                                        pinvalue, strlen (pinvalue));
+      if (!rc && !app->app_local->extcap.is_v2)
+        rc = iso7816_reset_retry_counter (app->slot, 0x82,
+                                          pinvalue, strlen (pinvalue));
     }
   else if (!app->app_local->extcap.is_v2)
     {
@@ -2208,7 +2162,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
                         gpg_strerror (rc));
               goto leave;
             }
-          rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno,
+          rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0,
                                                  &pininfo);
           /* Dismiss the prompt. */
           pincb (pincb_arg, NULL, NULL);
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 38ce79a..dd73fc6 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -307,16 +307,18 @@ iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
 
 /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
    verification vector CHVNO.  With PININFO non-NULL the keypad of the
-   reader will be used.  */
+   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,
+iso7816_change_reference_data_kp (int slot, int chvno, int is_exchange,
                                   iso7816_pininfo_t *pininfo)
 {
   int sw;
 
-  sw = apdu_keypad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, 0, chvno,
-                           pininfo->mode, pininfo->minlen, pininfo->maxlen,
-                           pininfo->padlen);
+  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);
 }
 
@@ -354,31 +356,6 @@ iso7816_change_reference_data (int slot, int chvno,
 
 
 gpg_error_t
-iso7816_reset_retry_counter_kp (int slot, int chvno,
-                                const char *newchv, size_t newchvlen,
-                                iso7816_pininfo_t *pininfo)
-{
-  int sw;
-
-  if (!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);
-  return map_sw (sw);
-}
-
-
-gpg_error_t
 iso7816_reset_retry_counter_with_rc (int slot, int chvno,
                                      const char *data, size_t datalen)
 {
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 6d52702..d12855b 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -68,13 +68,10 @@ 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,
+					      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);

commit b480f18e1de71766fc6b4fb3c50e157d0abffda5
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Thu Dec 1 11:09:51 2011 +0900

    scd: Fix pinpad input support (backport from master)
    
    * app-openpgp.c (do_change_pin): Fix pincb messages when
    use_keypad == 1.

diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 950968c..4bae875 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1982,7 +1982,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           if (chvno == 2)
             set_resetcode = 1;
         }
-      else if (chvno == 1 || chvno == 3)
+      else if (!use_keypad && (chvno == 1 || chvno == 3))
         {
           char *promptbuf = NULL;
           const char *prompt;
@@ -1997,7 +1997,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
             }
           else
             prompt = _("||Please enter the PIN");
-          rc = pincb (pincb_arg, prompt, use_keypad ? NULL : &oldpinvalue);
+          rc = pincb (pincb_arg, prompt, &oldpinvalue);
           xfree (promptbuf);
           promptbuf = NULL;
           if (rc)
@@ -2007,7 +2007,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
               goto leave;
             }
 
-          if (!use_keypad && strlen (oldpinvalue) < minlen)
+          if (strlen (oldpinvalue) < minlen)
             {
               log_info (_("PIN for CHV%d is too short;"
                           " minimum length is %d\n"), chvno, minlen);
@@ -2040,23 +2040,26 @@ 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); 
-          if (rc)
-            {
-              log_info (_("PIN callback returned error: %s\n"), 
-                        gpg_strerror (rc));
-              goto leave;
             }
-          if (strlen (resetcode) < minlen)
+
+          if (!use_keypad)
             {
-              log_info (_("Reset Code is too short; minimum length is %d\n"),
-                        minlen);
-              rc = gpg_error (GPG_ERR_BAD_PIN);
-              goto leave;
+              rc = pincb (pincb_arg,
+                          _("||Please enter the Reset Code for the card"),
+                          &resetcode);
+              if (rc)
+                {
+                  log_info (_("PIN callback returned error: %s\n"),
+                            gpg_strerror (rc));
+                  goto leave;
+                }
+              if (strlen (resetcode) < minlen)
+                {
+                  log_info (_("Reset Code is too short; minimum length is %d\n"),
+                            minlen);
+                  rc = gpg_error (GPG_ERR_BAD_PIN);
+                  goto leave;
+                }
             }
         }
       else
@@ -2090,18 +2093,36 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
 
   if (resetcode)
     {
-      char *buffer;
-
-      buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
-      if (!buffer)
-        rc = gpg_error_from_syserror ();
+      if (use_keypad)
+	{
+	  rc = pincb (pincb_arg,
+		      _("||Please enter the Reset Code for the card and New PIN"),
+		      NULL);
+	  if (rc)
+	    {
+	      log_info (_("PIN callback returned error: %s\n"),
+			gpg_strerror (rc));
+	      goto leave;
+	    }
+	  rc = iso7816_reset_retry_counter_with_rc_kp (app->slot, 0x81,
+						       &pininfo);
+	  pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+	}
       else
-        {
-          strcpy (stpcpy (buffer, resetcode), pinvalue);
-          rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
-                                                    buffer, strlen (buffer));
-          wipememory (buffer, strlen (buffer));
-          xfree (buffer);
+	{
+	  char *buffer;
+
+	  buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+	  if (!buffer)
+	    rc = gpg_error_from_syserror ();
+	  else
+	    {
+	      strcpy (stpcpy (buffer, resetcode), pinvalue);
+	      rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+							buffer, strlen (buffer));
+	      wipememory (buffer, strlen (buffer));
+	      xfree (buffer);
+	    }
         }
     }
   else if (set_resetcode)
@@ -2110,18 +2131,48 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
         {
           log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
           rc = gpg_error (GPG_ERR_BAD_PIN);
-        }
+	}
       else
-        rc = iso7816_put_data (app->slot, 0, 0xD3,
-                               pinvalue, strlen (pinvalue));
+	{
+	  if (use_keypad)
+	    {
+	      rc = pincb (pincb_arg,  _("|RN|New Reset Code"), NULL);
+	      if (rc)
+		{
+		  log_info (_("PIN callback returned error: %s\n"),
+			    gpg_strerror (rc));
+		  goto leave;
+		}
+	      rc = iso7816_put_data_kp (app->slot, 0xD3, &pininfo);
+	      pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+	    }
+	  else
+	    rc = iso7816_put_data (app->slot, 0, 0xD3,
+				   pinvalue, strlen (pinvalue));
+	}
     }
   else if (reset_mode)
     {
-      rc = iso7816_reset_retry_counter (app->slot, 0x81,
-                                        pinvalue, strlen (pinvalue));
-      if (!rc && !app->app_local->extcap.is_v2)
-        rc = iso7816_reset_retry_counter (app->slot, 0x82,
-                                          pinvalue, strlen (pinvalue));
+      if (use_keypad)
+        {
+          rc = pincb (pincb_arg, _("|N|New PIN"), NULL);
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              goto leave;
+            }
+          rc = iso7816_reset_retry_counter_kp (app->slot, 0x81, &pininfo);
+          pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+        }
+      else
+        {
+          rc = iso7816_reset_retry_counter (app->slot, 0x81,
+                                            pinvalue, strlen (pinvalue));
+          if (!rc && !app->app_local->extcap.is_v2)
+            rc = iso7816_reset_retry_counter (app->slot, 0x82,
+                                              pinvalue, strlen (pinvalue));
+        }
     }
   else if (!app->app_local->extcap.is_v2)
     {
@@ -2147,6 +2198,16 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
 
       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,
                                                  &pininfo);
           /* Dismiss the prompt. */

commit 7cd8b12e25b6daabedf6fdc2f350327e2116fc78
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Tue Nov 29 11:59:32 2011 +0900

    scd: PC/SC pinpad support (pinpad input for modify pass phrase). (backport)
    
    * iso7816.h (iso7816_change_reference_data_kp): Remove arguments
    of OLDCHV, OLDCHVLEN, NEWCHV, and NEWCHVLEN.
    
    * iso7816.c (iso7816_change_reference_data_kp): Call
    apdu_keypad_modify.
    (iso7816_change_reference_data): Don't call
    iso7816_change_reference_data_kp.
    
    * apdu.h (apdu_keypad_modify): New.
    
    * apdu.c (pcsc_keypad_modify, apdu_keypad_modify): New.
    (struct reader_table_s): New memeber function keypad_modify.
    (new_reader_slot, open_ct_reader, open_ccid_reader)
    (open_rapdu_reader): Initialize keypad_modify.
    
    * app-openpgp.c (do_change_pin): Handle keypad and call
    iso7816_change_reference_data_kp if it is the case.

diff --git a/scd/apdu.c b/scd/apdu.c
index a124d1c..4062b1e 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -111,6 +111,7 @@ struct reader_table_s {
   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;
@@ -317,6 +318,8 @@ 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);
 
 
 
@@ -364,6 +367,7 @@ new_reader_slot (void)
   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;
@@ -651,6 +655,7 @@ open_ct_reader (int port)
   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;
@@ -2093,6 +2098,88 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
   return sw;
 }
+
+
+#define PIN_MODIFY_STRUCTURE_SIZE 28
+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;
+  unsigned long 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] = 0x03;  /* 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] = 0x04; /* 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] */
+
+  sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
+                     pin_modify, len, result, &resultlen);
+  xfree (pin_modify);
+  if (sw || resultlen < 2)
+    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  return sw;
+}
 
 #ifdef HAVE_LIBUSB
 /*
@@ -2304,6 +2391,7 @@ open_ccid_reader (const char *portstr)
   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_verify;
+  reader_table[slot].keypad_modify = NULL;
   /* Our CCID reader code does not support T=0 at all, thus reset the
      flag.  */
   reader_table[slot].is_t0 = 0;
@@ -2597,6 +2685,7 @@ open_rapdu_reader (int portno,
   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);
@@ -3233,6 +3322,28 @@ apdu_keypad_verify (int slot, int class, int ins, int p0, int p1, int pin_mode,
 }
 
 
+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 5a48f3a..94d7449 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -117,6 +117,9 @@ int 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);
+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-openpgp.c b/scd/app-openpgp.c
index 28ceeac..950968c 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -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,6 +1966,11 @@ 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. */
@@ -1973,12 +1984,12 @@ 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)
             {
+              minlen = 8;
               rc = build_enter_admin_pin_prompt (app, &promptbuf);
               if (rc)
                 goto leave;
@@ -1986,7 +1997,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
             }
           else
             prompt = _("||Please enter the PIN");
-          rc = pincb (pincb_arg, prompt, &oldpinvalue);
+          rc = pincb (pincb_arg, prompt, use_keypad ? NULL : &oldpinvalue);
           xfree (promptbuf);
           promptbuf = NULL;
           if (rc)
@@ -1996,7 +2007,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
               goto leave;
             }
 
-          if (strlen (oldpinvalue) < minlen)
+          if (!use_keypad && strlen (oldpinvalue) < minlen)
             {
               log_info (_("PIN for CHV%d is too short;"
                           " minimum length is %d\n"), chvno, minlen);
@@ -2012,8 +2023,8 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           unsigned char *value;
           size_t valuelen;
           int remaining;
-          int minlen = 8;
 
+          minlen = 8;
           relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
           if (!relptr || valuelen < 7)
             {
@@ -2060,17 +2071,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 +2144,18 @@ 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 = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno,
+                                                 &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/iso7816.c b/scd/iso7816.c
index 33382f2..38ce79a 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -306,16 +306,29 @@ iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
 }
 
 /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+   verification vector CHVNO.  With PININFO non-NULL the keypad of the
+   reader will be used.  */
+gpg_error_t
+iso7816_change_reference_data_kp (int slot, int chvno,
+                                  iso7816_pininfo_t *pininfo)
+{
+  int sw;
+
+  sw = apdu_keypad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, 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
    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.  */
+   length 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)
+iso7816_change_reference_data (int slot, int chvno,
+                               const char *oldchv, size_t oldchvlen,
+                               const char *newchv, size_t newchvlen)
 {
   int sw;
   char *buf;
@@ -332,35 +345,13 @@ iso7816_change_reference_data_kp (int slot, int chvno,
     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);
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
+                         oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
   xfree (buf);
   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.  */
-gpg_error_t
-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,
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 6af4701..6d52702 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -68,9 +68,7 @@ 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);
+                                              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,

commit 6dc187f2dbf3b5e7322fa40c7e085da12ad373d2
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Mon Nov 28 16:16:38 2011 +0900

    scd: PC/SC pinpad support.  (Backported from master.)
    
    * iso7816.h (iso7816_verify_kp): Remove arguments of CHV and CHVLEN.
    
    * iso7816.c (iso7816_verify_kp): Call apdu_keypad_verify. Only
    handle thecase with PININFO.
    (iso7816_verify): Call apdu_send_simple.
    
    * app-openpgp.c (verify_a_chv, verify_chv3): Follow the change of
    iso7816_verify_kp.
    
    * app-nks.c (verify_pin): Likewise.
    
    * app-dinsig.c (verify_pin): Likewise.
    
    * apdu.c: Include "iso7816.h".
    (struct reader_table_s): New memeber function keypad_verify.
    Add fields verify_ioctl and modify_ioctl in pcsc.
    (CM_IOCTL_GET_FEATURE_REQUEST, FEATURE_VERIFY_PIN_DIRECT)
    (FEATURE_MODIFY_PIN_DIRECT): New.
    (pcsc_control): New.
    (control_pcsc_direct, control_pcsc_wrapped, control_pcsc)
    (check_pcsc_keypad, pcsc_keypad_verify): New.
    (ccid_keypad_verify, apdu_keypad_verify): New.
    (new_reader_slot): Initialize with check_pcsc_keypad,
    pcsc_keypad_verify, verify_ioctl and modify_ioctl.
    (open_ct_reader): Initialize keypad_verify with NULL.
    (open_ccid_reader): Initialize keypad_verify.
    (open_rapdu_reader): Initialize keypad_verify with NULL.
    (apdu_open_reader): Initialize pcsc_control.
    
    * pcsc-wrapper.c (load_pcsc_driver): Initialize pcsc_control.
    (handle_control): New.
    (main): Handle the case 6 of handle_control.

diff --git a/scd/apdu.c b/scd/apdu.c
index 541dd05..a124d1c 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,7 @@ 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 *);
 
   struct {
     ccid_driver_t handle;
@@ -117,6 +119,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 +222,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 +295,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 +313,10 @@ 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);
 
 
 
@@ -340,9 +360,10 @@ 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].used = 1;
   reader_table[reader].any_status = 0;
@@ -353,6 +374,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 +650,7 @@ 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;
 
   dump_reader_status (reader);
   return reader;
@@ -1154,6 +1178,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 +1961,138 @@ 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 23
+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;
+  unsigned long 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] = 0x04; /* 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] */
+
+  sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
+                     pin_verify, len, result, &resultlen);
+  xfree (pin_verify);
+  if (sw || resultlen < 2)
+    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  return sw;
+}
 
 #ifdef HAVE_LIBUSB
 /*
@@ -1930,6 +2230,35 @@ check_ccid_keypad (int slot, int command, int pin_mode,
 }
 
 
+static int
+ccid_keypad_verify (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 +2303,7 @@ 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_verify;
   /* Our CCID reader code does not support T=0 at all, thus reset the
      flag.  */
   reader_table[slot].is_t0 = 0;
@@ -2266,6 +2596,7 @@ 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;
 
   dump_reader_status (slot);
   rapdu_msg_release (msg);
@@ -2433,6 +2764,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 +2777,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 +2795,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;
         }
@@ -2877,6 +3211,28 @@ 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;
+}
+
+
 /* 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 d79f8b4..5a48f3a 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -114,6 +114,9 @@ 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_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 46e9a6a..697cdf6 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 076b913..b51e1fc 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 8a71caf..28ceeac 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);
         }
diff --git a/scd/iso7816.c b/scd/iso7816.c
index e3f2c1b..33382f2 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,7 +299,10 @@ 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
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 8519712..6af4701 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -63,9 +63,7 @@ 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);
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index ee974ac..73b25f4 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);

-----------------------------------------------------------------------

Summary of changes:
 scd/apdu.c         |  497 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 scd/apdu.h         |    6 +
 scd/app-dinsig.c   |    2 +-
 scd/app-nks.c      |    4 +-
 scd/app-openpgp.c  |  133 +++++++++-----
 scd/app.c          |    8 +-
 scd/ccid-driver.c  |  134 +++++++++++---
 scd/iso7816.c      |  107 ++++-------
 scd/iso7816.h      |   13 +-
 scd/pcsc-wrapper.c |   50 +++++-
 10 files changed, 786 insertions(+), 168 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list