prompt change.
[gnupg.git] / scd / app-openpgp.c
index 5cfec04..cfc51ce 100644 (file)
@@ -1,5 +1,5 @@
 /* app-openpgp.c - The OpenPGP card application.
- * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -85,6 +85,7 @@ static struct {
   { 0x0102, 0,    0, 0, 0, 0, 0, "Private DO 2"},
   { 0x0103, 0,    0, 0, 0, 0, 0, "Private DO 3"},
   { 0x0104, 0,    0, 0, 0, 0, 0, "Private DO 4"},
+  { 0x7F21, 1,    0, 1, 0, 0, 0, "Cardholder certificate"},
   { 0 }
 };
 
@@ -120,10 +121,12 @@ struct app_local_s {
   /* Keep track of card capabilities.  */
   struct 
   {
+    unsigned int is_v2:1;  /* This is a v2.0 compatible card.  */
     unsigned int get_challenge:1;
     unsigned int key_import:1;
     unsigned int change_force_chv:1;
     unsigned int private_dos:1;
+    unsigned int max_certlen_3:16;
   } extcap;
 
   /* Flags used to control the application.  */
@@ -481,7 +484,7 @@ count_bits (const unsigned char *a, size_t len)
   return n;
 }
 
-/* GnuPG makes special use of the login-data DO, this fucntion parses
+/* GnuPG makes special use of the login-data DO, this function parses
    the login data to store the flags for later use.  It may be called
    at any time and should be called after changing the login-data DO.
 
@@ -740,11 +743,12 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     {
       char tmp[50];
 
-      sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d", 
+      sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d mcl3=%u", 
                app->app_local->extcap.get_challenge,
                app->app_local->extcap.key_import,
                app->app_local->extcap.change_force_chv,
-               app->app_local->extcap.private_dos);
+               app->app_local->extcap.private_dos,
+               app->app_local->extcap.max_certlen_3);
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
@@ -1274,11 +1278,52 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
 #endif
 }
 
+/* Read the statdard certificate of an OpenPGP v2 card.  It is
+   returned in a freshly allocated buffer with that address stored at
+   CERT and the length of the certificate stored at CERTLEN.  CERTID
+   needs to be set to "OpenPGP.3".  */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+             unsigned char **cert, size_t *certlen)
+{
+#if GNUPG_MAJOR_VERSION > 1
+  gpg_error_t err;
+  unsigned char *buffer;
+  size_t buflen;
+  void *relptr;
+
+  *cert = NULL;
+  *certlen = 0;
+  if (strcmp (certid, "OPENPGP.3"))
+    return gpg_error (GPG_ERR_INV_ID);
+  if (app->app_local->extcap.is_v2)
+    return gpg_error (GPG_ERR_NOT_FOUND);
+
+  relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
+  if (!relptr)
+    return gpg_error (GPG_ERR_NOT_FOUND);
+
+  *cert = xtrymalloc (buflen);
+  if (!*cert)
+    err = gpg_error_from_syserror ();
+  else
+    {
+      memcpy (*cert, buffer, buflen);
+      *certlen = buflen;
+      err  = 0;
+    }
+  xfree (relptr);
+  return err;
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
 
 /* 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
-   ised with CHV1.  PINVALUE is the address of a pointer which will
+   used with CHV1.  PINVALUE is the address of a pointer which will
    receive a newly allocated block with the actual PIN (this is useful
    in case that PIN shall be used for another verifiy operation).  The
    caller needs to free this value.  If the function returns with
@@ -1300,6 +1345,25 @@ verify_a_chv (app_t app,
 
   *pinvalue = NULL;
 
+  if (chvno == 2 && app->app_local->flags.def_chv2)
+    {
+      /* Special case for def_chv2 mechanism. */
+      if (opt.verbose)
+        log_info (_("using default PIN as %s\n"), "CHV2");
+      rc = iso7816_verify (app->slot, 0x82, "123456", 6);
+      if (rc)
+        {
+          /* Verification of CHV2 with the default PIN failed,
+             although the card pretends to have the default PIN set as
+             CHV2.  We better disable the def_chv2 flag now. */
+          log_info (_("failed to use default PIN as %s: %s"
+                      " - disabling further default use\n"),
+                    "CHV2", gpg_strerror (rc));
+          app->app_local->flags.def_chv2 = 0;
+        }
+      return rc;
+    }
+
   memset (&pininfo, 0, sizeof pininfo);
   pininfo.mode = 1;
   pininfo.minlen = minlen;
@@ -1348,7 +1412,7 @@ verify_a_chv (app_t app,
 #define PROMPTSTRING  _("||Please enter the PIN%%0A[sigs done: %lu]")
           size_t promptsize = strlen (PROMPTSTRING) + 50;
 
-          prompt = xmalloc (promptsize);
+          prompt = xtrymalloc (promptsize);
           if (!prompt)
             return gpg_error_from_syserror ();
           snprintf (prompt, promptsize-1, PROMPTSTRING, sigcount);
@@ -1357,7 +1421,7 @@ verify_a_chv (app_t app,
 #undef PROMPTSTRING
         }
       else
-        rc = pincb (pincb_arg, "PIN", pinvalue); 
+        rc = pincb (pincb_arg, _("||Please enter the PIN"), pinvalue); 
 
       if (rc)
         {
@@ -1451,10 +1515,16 @@ verify_chv3 (app_t app,
       
   if (!app->did_chv3) 
     {
-      char *pinvalue;
       void *relptr;
       unsigned char *value;
       size_t valuelen;
+      iso7816_pininfo_t pininfo;
+      int minlen = 8;
+      int remaining;
+
+      memset (&pininfo, 0, sizeof pininfo);
+      pininfo.mode = 1;
+      pininfo.minlen = minlen;
 
       relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
       if (!relptr || valuelen < 7)
@@ -1469,38 +1539,80 @@ verify_chv3 (app_t app,
           xfree (relptr);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
+      remaining = value[6];
+      xfree (relptr);
 
       log_info(_("%d Admin PIN attempts remaining before card"
-                 " is permanently locked\n"), value[6]);
-      xfree (relptr);
+                 " is permanently locked\n"), remaining);
 
-      /* TRANSLATORS: Do not translate the "|A|" prefix but
-         keep it at the start of the string.  We need this elsewhere
-         to get some infos on the string. */
-      rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue); 
-      if (rc)
+      if (!opt.disable_keypad
+          && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
         {
-          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
-          return rc;
-        }
+          /* The reader supports the verify command through the keypad. */
+          
+          if (remaining < 3)
+            {
+#define PROMPTSTRING  _("|A|Please enter the Admin PIN" \
+                        " at the reader's keypad%%0A"   \
+                        "[remaining attempts: %d]")
+              size_t promptsize = strlen (PROMPTSTRING) + 50;
+              char *prompt;
+              
+              prompt = xmalloc (promptsize);
+              if (!prompt)
+                return gpg_error_from_syserror ();
+              snprintf (prompt, promptsize-1, PROMPTSTRING, remaining);
+              rc = pincb (pincb_arg, prompt, NULL); 
+              xfree (prompt);
+#undef PROMPTSTRING
+            }
+          else
+            rc = pincb (pincb_arg, _("|A|Please enter the Admin PIN"
+                                     " at the reader's keypad"),   NULL);
 
-      if (strlen (pinvalue) < 8)
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              return rc;
+            }
+          rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo); 
+          /* Dismiss the prompt. */
+          pincb (pincb_arg, NULL, NULL);
+        }
+      else
         {
-          log_error (_("PIN for CHV%d is too short;"
-                       " minimum length is %d\n"), 3, 8);
+          char *pinvalue;
+
+          /* TRANSLATORS: Do not translate the "|A|" prefix but keep
+             it at the start of the string.  We need this elsewhere to
+             get some infos on the string. */
+          rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue); 
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              return rc;
+            }
+          
+          if (strlen (pinvalue) < minlen)
+            {
+              log_error (_("PIN for CHV%d is too short;"
+                           " minimum length is %d\n"), 3, minlen);
+              xfree (pinvalue);
+              return gpg_error (GPG_ERR_BAD_PIN);
+            }
+          
+          rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
           xfree (pinvalue);
-          return gpg_error (GPG_ERR_BAD_PIN);
         }
-
-      rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
-      xfree (pinvalue);
+      
       if (rc)
         {
           log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
           flush_cache_after_error (app);
           return rc;
         }
-      app->did_chv3 = 1;
     }
   return rc;
 }
@@ -1521,6 +1633,7 @@ do_setattr (app_t app, const char *name,
     int tag;
     int need_chv;
     int special;
+    unsigned int need_v2:1;
   } table[] = {
     { "DISP-NAME",    0x005B, 3 },
     { "LOGIN-DATA",   0x005E, 3, 2 },
@@ -1535,6 +1648,7 @@ do_setattr (app_t app, const char *name,
     { "PRIVATE-DO-2", 0x0102, 3 },
     { "PRIVATE-DO-3", 0x0103, 2 },
     { "PRIVATE-DO-4", 0x0104, 3 },
+    { "CERT-3",       0x7F21, 3, 0, 1 },
     { NULL, 0 }
   };
 
@@ -1543,6 +1657,8 @@ do_setattr (app_t app, const char *name,
     ;
   if (!table[idx].name)
     return gpg_error (GPG_ERR_INV_NAME); 
+  if (!table[idx].need_v2)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED); 
 
   switch (table[idx].need_chv)
     {
@@ -1577,13 +1693,15 @@ do_setattr (app_t app, const char *name,
 
 /* Handle the PASSWD command. */
 static gpg_error_t 
-do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
+do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, 
+               unsigned int flags,
                gpg_error_t (*pincb)(void*, const char *, char **),
                void *pincb_arg)
 {
   int rc = 0;
   int chvno = atoi (chvnostr);
   char *pinvalue;
+  int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
 
   if (reset_mode && chvno == 3)
     {
@@ -2461,7 +2579,7 @@ do_decipher (app_t app, const char *keyidstr,
 
       /* We might encounter a couple of leading zeroes in the
          cryptogram.  Due to internal use of MPIs thease leading
-         zeroes are stripped.  However the OpenPGp card expects
+         zeroes are stripped.  However the OpenPGP card expects
          exactly 128 bytes for the cryptogram (for a 1k key).  Thus we
          need to fix it up.  We do this for up to 16 leading zero
          bytes; a cryptogram with more than this is with a very high
@@ -2650,6 +2768,9 @@ app_select_openpgp (app_t app)
           goto leave;
         }
 
+      if (app->card_version >= 0x0200)
+        app->app_local->extcap.is_v2 = 1;
+
       relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
       if (!relptr)
         {
@@ -2674,6 +2795,11 @@ app_select_openpgp (app_t app)
           app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
           app->app_local->extcap.private_dos      = !!(*buffer & 0x08);
         }
+      if (buflen >= 10)
+        {
+          /* Available with v2 cards.  */
+          app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
+        }
       xfree (relptr);
       
       /* Some of the first cards accidently don't set the
@@ -2688,6 +2814,7 @@ app_select_openpgp (app_t app)
 
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
+      app->fnc.readcert = do_readcert;
       app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;