SCD: handle keypad request on the card.
authorNIIBE Yutaka <gniibe@fsij.org>
Thu, 10 Jan 2013 06:58:43 +0000 (15:58 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 5 Feb 2013 05:12:58 +0000 (14:12 +0900)
* scd/app-openpgp.c: Add 2013.
(struct app_local_s): Add keypad structure.
(parse_login_data): Add parsing keypad request on the card.
(check_keypad_request): New.
(verify_a_chv, verify_chv3, do_change_pin): Call check_keypad_request
to determine use of keypad.

scd/app-openpgp.c

index d3c309e..5929473 100644 (file)
@@ -1,6 +1,6 @@
 /* app-openpgp.c - The OpenPGP card application.
  * Copyright (C) 2003, 2004, 2005, 2007, 2008,
 /* app-openpgp.c - The OpenPGP card application.
  * Copyright (C) 2003, 2004, 2005, 2007, 2008,
- *               2009 Free Software Foundation, Inc.
+ *               2009, 2013 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  *
  * This file is part of GnuPG.
  *
@@ -191,6 +191,15 @@ struct app_local_s {
     unsigned int def_chv2:1;  /* Use 123456 for CHV2.  */
   } flags;
 
     unsigned int def_chv2:1;  /* Use 123456 for CHV2.  */
   } flags;
 
+  /* Keypad request specified on card.  */
+  struct
+  {
+    unsigned int specified:1;
+    unsigned int varlen:1;
+    int fixedlen_user;
+    int fixedlen_admin;
+  } keypad;
+
   struct
   {
     unsigned int n_bits;     /* Size of the modulus in bits.  The rest
   struct
   {
     unsigned int n_bits;     /* Size of the modulus in bits.  The rest
@@ -581,17 +590,23 @@ count_bits (const unsigned char *a, size_t len)
    Everything up to a LF is considered a mailbox or account name.  If
    the first LF is followed by DC4 (0x14) control sequence are
    expected up to the next LF.  Control sequences are separated by FS
    Everything up to a LF is considered a mailbox or account name.  If
    the first LF is followed by DC4 (0x14) control sequence are
    expected up to the next LF.  Control sequences are separated by FS
-   (0x18) and consist of key=value pairs.  There is one key defined:
+   (0x18) and consist of key=value pairs.  There are two keys defined:
 
     F=<flags>
 
 
     F=<flags>
 
-    Were FLAGS is a plain hexadecimal number representing flag values.
+    Where FLAGS is a plain hexadecimal number representing flag values.
     The lsb is here the rightmost bit.  Defined flags bits are:
 
       Bit 0 = CHV1 and CHV2 are not syncronized
       Bit 1 = CHV2 has been been set to the default PIN of "123456"
               (this implies that bit 0 is also set).
 
     The lsb is here the rightmost bit.  Defined flags bits are:
 
       Bit 0 = CHV1 and CHV2 are not syncronized
       Bit 1 = CHV2 has been been set to the default PIN of "123456"
               (this implies that bit 0 is also set).
 
+    P=<keypad-request>
+
+    Where KEYPAD_REQUEST is 0 or a pair of two integers: <n>,<m>.
+    0 means use keypad with variable length input.  <n>,<m> means use
+    keypad with fixed length input.  N for user PIN, M for admin PIN.
+
 */
 static void
 parse_login_data (app_t app)
 */
 static void
 parse_login_data (app_t app)
@@ -603,6 +618,10 @@ parse_login_data (app_t app)
   /* Set defaults.  */
   app->app_local->flags.no_sync = 0;
   app->app_local->flags.def_chv2 = 0;
   /* Set defaults.  */
   app->app_local->flags.no_sync = 0;
   app->app_local->flags.def_chv2 = 0;
+  app->app_local->keypad.specified = 0;
+  app->app_local->keypad.varlen = 0;
+  app->app_local->keypad.fixedlen_user = 6;
+  app->app_local->keypad.fixedlen_admin = 8;
 
   /* Read the DO.  */
   relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
 
   /* Read the DO.  */
   relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
@@ -628,11 +647,56 @@ parse_login_data (app_t app)
              any leading digits but bail out on invalid characters. */
           for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
             lastdig = xtoi_1 (p);
              any leading digits but bail out on invalid characters. */
           for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
             lastdig = xtoi_1 (p);
+          buffer = p;
+          buflen = len;
           if (len && !(*p == '\n' || *p == '\x18'))
             goto next;  /* Invalid characters in field.  */
           app->app_local->flags.no_sync = !!(lastdig & 1);
           app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
         }
           if (len && !(*p == '\n' || *p == '\x18'))
             goto next;  /* Invalid characters in field.  */
           app->app_local->flags.no_sync = !!(lastdig & 1);
           app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
         }
+      else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=')
+        {
+          /* Keypad request control sequence found.  */
+          buffer += 2;
+          buflen -= 2;
+
+          if (buflen)
+            {
+              if (*buffer == '0')
+                {
+                  buffer++;
+                  buflen--;
+                  if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
+                    goto next;
+                  app->app_local->keypad.specified = 1;
+                  app->app_local->keypad.varlen = 1;
+                }
+              else if (digitp (buffer))
+                {
+                  char *q;
+                  int n, m;
+
+                  n = strtol (buffer, &q, 10);
+                  if (*q++ != ',' || !digitp (q))
+                    goto next;
+                  m = strtol (q, &q, 10);
+                  buffer = q;
+                  if (buflen < ((unsigned char *)q - buffer))
+                    {
+                      buflen = 0;
+                      break;
+                    }
+                  else
+                    buflen -= ((unsigned char *)q - buffer);
+
+                  if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
+                    goto next;
+                  app->app_local->keypad.specified = 1;
+                  app->app_local->keypad.fixedlen_user = n;
+                  app->app_local->keypad.fixedlen_admin = m;
+                }
+            }
+        }
     next:
       for (; buflen && *buffer != '\x18'; buflen--, buffer++)
         if (*buffer == '\n')
     next:
       for (; buflen && *buffer != '\x18'; buflen--, buffer++)
         if (*buffer == '\n')
@@ -1470,6 +1534,37 @@ do_readcert (app_t app, const char *certid,
 }
 
 
 }
 
 
+/* Decide if we use keypad of reader for PIN input according to the
+   user preference on the card.  Returns 0 if we use keypad, 1 otherwise.  */
+static int
+check_keypad_request (app_t app, pininfo_t *pininfo, int admin_pin)
+{
+  /* User specifies no preference on card, then, use pinentry.  */
+  if (app->app_local->keypad.specified == 0)
+    return 1;
+
+  if (app->app_local->keypad.varlen)
+    if (pininfo->fixedlen == 0)
+      return 0;
+    else
+      /* On card, user specifies varlen but reader doesn't have the feature.  */
+      return 1;
+  else
+    {
+      if (admin_pin)
+        pininfo->fixedlen = app->app_local->keypad.fixedlen_admin;
+      else
+        pininfo->fixedlen = app->app_local->keypad.fixedlen_user;
+
+      if (pininfo->fixedlen < pininfo->minlen
+          || pininfo->fixedlen > pininfo->maxlen)
+        return 1;
+
+      return 0;
+    }
+}
+
+
 /* Verify a CHV either using using the pinentry or if possibile by
    using a keypad.  PINCB and PINCB_ARG describe the usual callback
    for the pinentry.  CHVNO must be either 1 or 2. SIGCOUNT is only
 /* Verify a CHV either using using the pinentry or if possibile by
    using a keypad.  PINCB and PINCB_ARG describe the usual callback
    for the pinentry.  CHVNO must be either 1 or 2. SIGCOUNT is only
@@ -1537,7 +1632,8 @@ verify_a_chv (app_t app,
 
 
   if (!opt.disable_keypad
 
 
   if (!opt.disable_keypad
-      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo)
+      && !check_keypad_request (app, &pininfo, 0))
     {
       /* The reader supports the verify command through the keypad.
          Note that the pincb appends a text to the prompt telling the
     {
       /* The reader supports the verify command through the keypad.
          Note that the pincb appends a text to the prompt telling the
@@ -1720,7 +1816,8 @@ verify_chv3 (app_t app,
         return rc;
 
       if (!opt.disable_keypad
         return rc;
 
       if (!opt.disable_keypad
-          && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+          && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo)
+          && !check_keypad_request (app, &pininfo, 1))
         {
           /* The reader supports the verify command through the keypad. */
           rc = pincb (pincb_arg, prompt, NULL);
         {
           /* The reader supports the verify command through the keypad. */
           rc = pincb (pincb_arg, prompt, NULL);
@@ -1970,7 +2067,8 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
 
       if (!opt.disable_keypad
           && !iso7816_check_keypad (app->slot,
 
       if (!opt.disable_keypad
           && !iso7816_check_keypad (app->slot,
-                                    ISO7816_CHANGE_REFERENCE_DATA, &pininfo))
+                                    ISO7816_CHANGE_REFERENCE_DATA, &pininfo)
+          && !check_keypad_request (app, &pininfo, chvno == 3))
         use_keypad = 1;
 
       if (reset_mode)
         use_keypad = 1;
 
       if (reset_mode)