scd: Improve the prompts for OpenPGP cards.
authorWerner Koch <wk@gnupg.org>
Wed, 22 Feb 2017 12:03:52 +0000 (13:03 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 22 Feb 2017 14:48:33 +0000 (15:48 +0100)
* scd/app-openpgp.c (get_disp_name): New.
(get_disp_serialno): New.
(get_prompt_info): New.
(build_enter_admin_pin_prompt): Rework the prompt texts.  Factor some
code out to ...
(get_remaining_tries): New.
(verify_a_chv): Print a remaining counter also for the standard PIN.
Rework the prompt texts.

* agent/divert-scd.c (ask_for_card): Pretty format an OpenPGP serial
no.

Signed-off-by: Werner Koch <wk@gnupg.org>
agent/divert-scd.c
scd/app-openpgp.c

index 3164404..d9d734c 100644 (file)
@@ -39,22 +39,39 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
   char *serialno;
   int no_card = 0;
   char *desc;
-  char *want_sn, *want_kid;
-  int want_sn_displen;
+  char *want_sn, *want_kid, *want_sn_disp;
+  int len;
 
   *r_kid = NULL;
 
   rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL);
   if (rc)
     return rc;
+  want_sn_disp = xtrystrdup (want_sn);
+  if (!want_sn_disp)
+    {
+      rc = gpg_error_from_syserror ();
+      xfree (want_sn);
+      return rc;
+    }
 
-  /* We assume that a 20 byte serial number is a standard one which
-     has the property to have a zero in the last nibble (Due to BCD
-     representation).  We don't display this '0' because it may
-     confuse the user.  */
-  want_sn_displen = strlen (want_sn);
-  if (want_sn_displen == 20 && want_sn[19] == '0')
-    want_sn_displen--;
+  len = strlen (want_sn_disp);
+  if (len == 32 && !strncmp (want_sn_disp, "D27600012401", 12))
+    {
+      /* This is an OpenPGP card - reformat  */
+      memmove (want_sn_disp, want_sn_disp+16, 4);
+      want_sn_disp[4] = ' ';
+      memmove (want_sn_disp+5, want_sn_disp+20, 8);
+      want_sn_disp[13] = 0;
+    }
+  else if (len == 20 && want_sn_disp[19] == '0')
+    {
+      /* We assume that a 20 byte serial number is a standard one
+       * which has the property to have a zero in the last nibble (Due
+       * to BCD representation).  We don't display this '0' because it
+       * may confuse the user.  */
+      want_sn_disp[19] = 0;
+    }
 
   for (;;)
     {
@@ -93,12 +110,12 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
         {
           if (asprintf (&desc,
                     "%s:%%0A%%0A"
-                    "  \"%.*s\"",
+                    "  %s",
                         no_card
                         ? L_("Please insert the card with serial number")
                         : L_("Please remove the current card and "
                              "insert the one with serial number"),
-                    want_sn_displen, want_sn) < 0)
+                        want_sn_disp) < 0)
             {
               rc = out_of_core ();
             }
@@ -114,6 +131,7 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
         }
       if (rc)
         {
+          xfree (want_sn_disp);
           xfree (want_sn);
           xfree (want_kid);
           return rc;
@@ -312,7 +330,8 @@ getpin_cb (void *opaque, const char *desc_text, const char *info,
                              info, NULL);
         else
           desc2 = NULL;
-        rc = agent_askpin (ctrl, desc2, prompt, again_text, pi, NULL, 0);
+        rc = agent_askpin (ctrl, desc2? desc2 : info,
+                           prompt, again_text, pi, NULL, 0);
         xfree (desc2);
       }
       again_text = NULL;
@@ -401,6 +420,8 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
   size_t siglen;
   unsigned char *sigval = NULL;
 
+  (void)desc_text;
+
   rc = ask_for_card (ctrl, shadow_info, &kid);
   if (rc)
     return rc;
@@ -409,7 +430,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
     {
       int save = ctrl->use_auth_call;
       ctrl->use_auth_call = 1;
-      rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, desc_text,
+      rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
                               algo, digest, digestlen, &sigval, &siglen);
       ctrl->use_auth_call = save;
     }
@@ -421,7 +442,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
       rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata);
       if (!rc)
         {
-          rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, desc_text,
+          rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
                                   algo, data, ndata, &sigval, &siglen);
           xfree (data);
         }
@@ -458,6 +479,8 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
   char *plaintext;
   size_t plaintextlen;
 
+  (void)desc_text;
+
   *r_padding = -1;
 
   s = cipher;
@@ -523,7 +546,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
   if (rc)
     return rc;
 
-  rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, desc_text,
+  rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, NULL,
                              ciphertext, ciphertextlen,
                              &plaintext, &plaintextlen, r_padding);
   if (!rc)
index 608e3cc..90c2661 100644 (file)
@@ -1082,6 +1082,104 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
   return rc;
 }
 
+
+/* Return the DISP-NAME without any padding characters.  Caller must
+ * free the result.  If not found or empty NULL is returned.  */
+static char *
+get_disp_name (app_t app)
+{
+  int rc;
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+  char *string;
+  char *p, *given;
+  char *result;
+
+  relptr = get_one_do (app, 0x005B, &value, &valuelen, &rc);
+  if (!relptr)
+    return NULL;
+
+  string = xtrymalloc (valuelen + 1);
+  if (!string)
+    {
+      xfree (relptr);
+      return NULL;
+    }
+  memcpy (string, value, valuelen);
+  string[valuelen] = 0;
+  xfree (relptr);
+
+  /* Swap surname and given name.  */
+  given = strstr (string, "<<");
+  for (p = string; *p; p++)
+    if (*p == '<')
+      *p = ' ';
+
+  if (given && given[2])
+    {
+      *given = 0;
+      given += 2;
+      result = strconcat (given, " ", string, NULL);
+    }
+  else
+    {
+      result = string;
+      string = NULL;
+    }
+
+  xfree (string);
+  return result;
+}
+
+
+/* Return the pretty formatted serialnumber.  On error NULL is
+ * returned.  */
+static char *
+get_disp_serialno (app_t app)
+{
+  char *serial = app_get_serialno (app);
+
+  /* For our OpenPGP cards we do not want to show the entire serial
+   * number but a nicely reformatted actual serial number.  */
+  if (serial && strlen (serial) > 16+12)
+    {
+      memmove (serial, serial+16, 4);
+      serial[4] = ' ';
+      /* memmove (serial+5, serial+20, 4); */
+      /* serial[9] = ' '; */
+      /* memmove (serial+10, serial+24, 4); */
+      /* serial[14] = 0; */
+      memmove (serial+5, serial+20, 8);
+      serial[13] = 0;
+    }
+  return serial;
+}
+
+
+/* Return the number of remaining tries for the standard or the admin
+ * pw.  Returns -1 on card error.  */
+static int
+get_remaining_tries (app_t app, int adminpw)
+{
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+  int remaining;
+
+  relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+  if (!relptr || valuelen < 7)
+    {
+      log_error (_("error retrieving CHV status from card\n"));
+      xfree (relptr);
+      return -1;
+    }
+  remaining = value[adminpw? 6 : 4];
+  xfree (relptr);
+  return remaining;
+}
+
+
 /* Retrieve the fingerprint from the card inserted in SLOT and write
    the according hex representation to FPR.  Caller must have provide
    a buffer at FPR of least 41 bytes.  Returns 0 on success or an
@@ -1874,6 +1972,62 @@ check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
 }
 
 
+/* Return a string with information about the card for use in a
+ * prompt.  Returns NULL on memory failure.  */
+static char *
+get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
+{
+  char *serial, *disp_name, *rembuf, *tmpbuf, *result;
+
+  serial = get_disp_serialno (app);
+  if (!serial)
+    return NULL;
+
+  disp_name = get_disp_name (app);
+  if (chvno == 1)
+    {
+      result = xtryasprintf (_("Card number:\t%s%%0A"
+                               "Signatures:\t%lu%%0A"
+                               "Cardholder:\t%s"),
+                             serial,
+                             sigcount,
+                             disp_name? disp_name:"");
+    }
+  else
+    {
+      result = xtryasprintf (_("Card number:\t%s%%0A"
+                               "Cardholder:\t%s"),
+                             serial,
+                             disp_name? disp_name:"");
+    }
+  xfree (disp_name);
+  xfree (serial);
+
+  if (remaining != -1)
+    {
+      /* TRANSLATORS: This is the number of remaining attempts to
+       * enter a PIN.  Use %%0A (double-percent,0A) for a linefeed. */
+      rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
+      if (!rembuf)
+        {
+          xfree (result);
+          return NULL;
+        }
+      tmpbuf = strconcat (result, "%0A%0A", rembuf, NULL);
+      xfree (rembuf);
+      if (!tmpbuf)
+        {
+          xfree (result);
+          return NULL;
+        }
+      xfree (result);
+      result = tmpbuf;
+    }
+
+  return result;
+}
+
+
 /* Verify a CHV either using the pinentry or if possible by
    using a pinpad.  PINCB and PINCB_ARG describe the usual callback
    for the pinentry.  CHVNO must be either 1 or 2. SIGCOUNT is only
@@ -1895,11 +2049,16 @@ verify_a_chv (app_t app,
   const char *prompt;
   pininfo_t pininfo;
   int minlen = 6;
+  int remaining;
 
-  assert (chvno == 1 || chvno == 2);
+  log_assert (chvno == 1 || chvno == 2);
 
   *pinvalue = NULL;
 
+  remaining = get_remaining_tries (app, 0);
+  if (remaining == -1)
+    return gpg_error (GPG_ERR_CARD);
+
   if (chvno == 2 && app->app_local->flags.def_chv2)
     {
       /* Special case for def_chv2 mechanism. */
@@ -1923,22 +2082,19 @@ verify_a_chv (app_t app,
   pininfo.fixedlen = -1;
   pininfo.minlen = minlen;
 
+  {
+    const char *firstline = _("||Please unlock the card");
+    char *infoblock = get_prompt_info (app, chvno, sigcount,
+                                       remaining < 3? remaining : -1);
 
-  if (chvno == 1)
-    {
-#define PROMPTSTRING  _("||Please enter the PIN%%0A[sigs done: %lu]")
-      size_t promptsize = strlen (PROMPTSTRING) + 50;
-
-      prompt_buffer = xtrymalloc (promptsize);
-      if (!prompt_buffer)
-        return gpg_error_from_syserror ();
-      snprintf (prompt_buffer, promptsize, PROMPTSTRING, sigcount);
+    prompt_buffer = strconcat (firstline, "%0A%0A", infoblock, NULL);
+    if (prompt_buffer)
       prompt = prompt_buffer;
-#undef PROMPTSTRING
-    }
-  else
-    prompt = _("||Please enter the PIN");
+    else
+      prompt = firstline;  /* ENOMEM fallback.  */
 
+    xfree (infoblock);
+  }
 
   if (!opt.disable_pinpad
       && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
@@ -1961,7 +2117,7 @@ verify_a_chv (app_t app,
       /* Dismiss the prompt. */
       pincb (pincb_arg, NULL, NULL);
 
-      assert (!*pinvalue);
+      log_assert (!*pinvalue);
     }
   else
     {
@@ -2049,29 +2205,20 @@ verify_chv2 (app_t app,
 static gpg_error_t
 build_enter_admin_pin_prompt (app_t app, char **r_prompt)
 {
-  void *relptr;
-  unsigned char *value;
-  size_t valuelen;
   int remaining;
   char *prompt;
+  char *infoblock;
 
   *r_prompt = NULL;
 
-  relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
-  if (!relptr || valuelen < 7)
-    {
-      log_error (_("error retrieving CHV status from card\n"));
-      xfree (relptr);
-      return gpg_error (GPG_ERR_CARD);
-    }
-  if (value[6] == 0)
+  remaining = get_remaining_tries (app, 1);
+  if (remaining == -1)
+    return gpg_error (GPG_ERR_CARD);
+  if (!remaining)
     {
       log_info (_("card is permanently locked!\n"));
-      xfree (relptr);
       return gpg_error (GPG_ERR_BAD_PIN);
     }
-  remaining = value[6];
-  xfree (relptr);
 
   log_info (ngettext("%d Admin PIN attempt remaining before card"
                      " is permanently locked\n",
@@ -2079,16 +2226,13 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
                      " is permanently locked\n",
                      remaining), remaining);
 
-  if (remaining < 3)
-    {
-      /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
-         the start of the string.  Use %%0A to force a linefeed.  */
-      prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
-                               "[remaining attempts: %d]"), remaining);
-    }
-  else
-    prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
+  infoblock = get_prompt_info (app, 3, 0, remaining < 3? remaining : -1);
 
+  /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
+     the start of the string.  Use %0A (single percent) for a linefeed.  */
+  prompt = strconcat (_("|A|Please enter the Admin PIN"),
+                      "%0A%0A", infoblock, NULL);
+  xfree (infoblock);
   if (!prompt)
     return gpg_error_from_syserror ();