scd: Factor out a function to check keyidstr.
[gnupg.git] / scd / app-openpgp.c
index 2d53b84..25ec834 100644 (file)
@@ -76,16 +76,16 @@ static struct {
   int tag;
   int constructed;
   int get_from;  /* Constructed DO with this DO or 0 for direct access. */
-  int binary:1;
-  int dont_cache:1;
-  int flush_on_error:1;
-  int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
+  unsigned int binary:1;
+  unsigned int dont_cache:1;
+  unsigned int flush_on_error:1;
+  unsigned int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
                                  this data object if it is used in 1.1
                                  and later versions of the card.  This
                                  does not work with composite DO and
                                  is currently only useful for the CHV
                                  status bytes. */
-  int try_extlen:2;           /* Large object; try to use an extended
+  unsigned int try_extlen:2;           /* Large object; try to use an extended
                                  length APDU when !=0.  The size is
                                  determined by extcap.max_certlen_3
                                  when == 1, and by extcap.max_special_do
@@ -98,7 +98,7 @@ static struct {
   { 0x0065, 1,    0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
   { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
   { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
-  { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" },
+  { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Salutation" },
   { 0x006E, 1,    0, 1, 0, 0, 0, 0, "Application Related Data" },
   { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
   { 0x0073, 1,    0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
@@ -119,8 +119,12 @@ static struct {
   { 0x0104, 0,    0, 0, 0, 0, 0, 2, "Private DO 4"},
   { 0x7F21, 1,    0, 1, 0, 0, 0, 1, "Cardholder certificate"},
   /* V3.0 */
-  { 0x7F74, 0,    0, 1, 0, 0, 0, 0, "General Feature Management"},
+  { 0x7F74, 0, 0x6E, 1, 0, 0, 0, 0, "General Feature Management"},
   { 0x00D5, 0,    0, 1, 0, 0, 0, 0, "AES key data"},
+  { 0x00D6, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Signature"},
+  { 0x00D7, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Decryption"},
+  { 0x00D8, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Authentication"},
+  { 0x00F9, 0,    0, 1, 0, 0, 0, 0, "KDF data object"},
   { 0 }
 };
 
@@ -199,7 +203,7 @@ struct app_local_s {
     unsigned int private_dos:1;
     unsigned int algo_attr_change:1;   /* Algorithm attributes changeable.  */
     unsigned int has_decrypt:1;        /* Support symmetric decryption.     */
-    unsigned int kdf_do:1;                /* Support KDF DOs.               */
+    unsigned int kdf_do:1;                /* Support KDF DO               */
 
     unsigned int sm_algo:2;            /* Symmetric crypto algo for SM.     */
     unsigned int pin_blk2:1;           /* PIN block 2 format supported.     */
@@ -347,7 +351,8 @@ get_cached_data (app_t app, int tag,
   err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
   if (err)
     return err;
-  *result = p;
+  if (len)
+    *result = p;
   *resultlen = len;
 
   /* Check whether we should cache this object. */
@@ -369,7 +374,10 @@ get_cached_data (app_t app, int tag,
   c = xtrymalloc (sizeof *c + len);
   if (c)
     {
-      memcpy (c->data, p, len);
+      if (len)
+        memcpy (c->data, p, len);
+      else
+        xfree (p);
       c->length = len;
       c->tag = tag;
       c->next = app->app_local->cache;
@@ -468,7 +476,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
   for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
     ;
 
-  if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
+  if (app->appversion > 0x0100 && data_objects[i].get_immediate_in_v11)
     {
       exmode = 0;
       rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
@@ -558,7 +566,7 @@ dump_all_do (int slot)
           if (data_objects[i].binary)
             {
               log_info ("DO '%s': ", data_objects[i].desc);
-              log_printhex ("", buffer, buflen);
+              log_printhex (buffer, buflen, "");
             }
           else
             log_info ("DO '%s': '%.*s'\n",
@@ -588,7 +596,7 @@ dump_all_do (int slot)
                           if (valuelen > 200)
                             log_info ("[%u]\n", (unsigned int)valuelen);
                           else
-                            log_printhex ("", value, valuelen);
+                            log_printhex (value, valuelen, "");
                         }
                       else
                         log_info ("DO '%s': '%.*s'\n",
@@ -635,7 +643,7 @@ count_bits (const unsigned char *a, size_t len)
     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 0 = CHV1 and CHV2 are not synchronized
       Bit 1 = CHV2 has been set to the default PIN of "123456"
               (this implies that bit 0 is also set).
 
@@ -808,7 +816,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
 
   xfree (buffer);
 
-  tag = (app->card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
+  tag = (app->appversion > 0x0007? 0xC7 : 0xC6) + keynumber;
   flush_cache_item (app, 0xC5);
   tag2 = 0xCE + keynumber;
   flush_cache_item (app, 0xCD);
@@ -817,7 +825,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
   if (rc)
     log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
 
-  if (!rc && app->card_version > 0x0100)
+  if (!rc && app->appversion > 0x0100)
     {
       unsigned char buf[4];
 
@@ -943,8 +951,8 @@ determine_rsa_response (app_t app, int keyno)
   int size;
 
   size = 2 + 3 /* header */
-    + 4 /* tag+len */ + app->app_local->keyattr[keyno].rsa.n_bits/8
-    + 2 /* tag+len */ + app->app_local->keyattr[keyno].rsa.e_bits/8;
+    + 4 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.n_bits+7)/8
+    + 2 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.e_bits+7)/8;
 
   return size;
 }
@@ -979,7 +987,13 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     { "PRIVATE-DO-3", 0x0103 },
     { "PRIVATE-DO-4", 0x0104 },
     { "$AUTHKEYID",   0x0000, -3 },
+    { "$ENCRKEYID",   0x0000, -6 },
+    { "$SIGNKEYID",   0x0000, -7 },
     { "$DISPSERIALNO",0x0000, -4 },
+    { "UIF-1",        0x00D6, 0 },
+    { "UIF-2",        0x00D7, 0 },
+    { "UIF-3",        0x00D8, 0 },
+    { "KDF",          0x00F9 },
     { NULL, 0 }
   };
   int idx, i, rc;
@@ -1012,7 +1026,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
 
       snprintf (tmp, sizeof tmp,
                 "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d "
-                "sm=%d si=%u dec=%d bt=%d",
+                "sm=%d si=%u dec=%d bt=%d kdf=%d",
                 app->app_local->extcap.get_challenge,
                 app->app_local->extcap.key_import,
                 app->app_local->extcap.change_force_chv,
@@ -1026,7 +1040,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
                  : 0),
                 app->app_local->status_indicator,
                 app->app_local->extcap.has_decrypt,
-                app->app_local->extcap.has_button);
+                app->app_local->extcap.has_button,
+                app->app_local->extcap.kdf_do);
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
@@ -1058,6 +1073,18 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
         send_key_attr (ctrl, app, table[idx].name, i);
       return 0;
     }
+  if (table[idx].special == -6)
+    {
+      char const tmp[] = "OPENPGP.2";
+      send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+      return 0;
+    }
+  if (table[idx].special == -7)
+    {
+      char const tmp[] = "OPENPGP.1";
+      send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+      return 0;
+    }
 
   relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
   if (relptr)
@@ -1435,13 +1462,13 @@ ecdh_params (const char *curve)
   /* See RFC-6637 for those constants.
          0x03: Number of bytes
          0x01: Version for this parameter format
-         KDF algo
-        KEK algo
+         KEK digest algorithm
+         KEK cipher algorithm
   */
   if (nbits <= 256)
     return (const unsigned char*)"\x03\x01\x08\x07";
   else if (nbits <= 384)
-    return (const unsigned char*)"\x03\x01\x09\x08";
+    return (const unsigned char*)"\x03\x01\x09\x09";
   else
     return (const unsigned char*)"\x03\x01\x0a\x09";
 }
@@ -1642,7 +1669,7 @@ get_public_key (app_t app, int keyno)
 
   m = e = NULL; /* (avoid cc warning) */
 
-  if (app->card_version > 0x0100)
+  if (app->appversion > 0x0100)
     {
       int exmode, le_value;
 
@@ -1772,6 +1799,7 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key)
   unsigned char grip[20];
   char gripstr[41];
   char idbuf[50];
+  const char *usage;
 
   err = get_public_key (app, keyno);
   if (err)
@@ -1789,10 +1817,19 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key)
 
   bin2hex (grip, 20, gripstr);
 
+  switch (keyno)
+    {
+    case 0: usage = "sc"; break;
+    case 1: usage = "e";  break;
+    case 2: usage = "sa"; break;
+    default: usage = "";  break;
+    }
+
   sprintf (idbuf, "OPENPGP.%d", keyno+1);
   send_status_info (ctrl, "KEYPAIRINFO",
                     gripstr, 40,
                     idbuf, strlen (idbuf),
+                    usage, strlen (usage),
                     NULL, (size_t)0);
 
  leave:
@@ -1815,11 +1852,19 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
   do_getattr (app, ctrl, "PUBKEY-URL");
   do_getattr (app, ctrl, "LOGIN-DATA");
   do_getattr (app, ctrl, "KEY-FPR");
-  if (app->card_version > 0x0100)
+  if (app->appversion > 0x0100)
     do_getattr (app, ctrl, "KEY-TIME");
   do_getattr (app, ctrl, "CA-FPR");
   do_getattr (app, ctrl, "CHV-STATUS");
   do_getattr (app, ctrl, "SIG-COUNTER");
+  if (app->app_local->extcap.kdf_do)
+    do_getattr (app, ctrl, "KDF");
+  if (app->app_local->extcap.has_button)
+    {
+      do_getattr (app, ctrl, "UIF-1");
+      do_getattr (app, ctrl, "UIF-2");
+      do_getattr (app, ctrl, "UIF-3");
+    }
   if (app->app_local->extcap.private_dos)
     {
       do_getattr (app, ctrl, "PRIVATE-DO-1");
@@ -1844,10 +1889,9 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
    buffer. On error PK and PKLEN are not changed and an error code is
    returned.  */
 static gpg_error_t
-do_readkey (app_t app, int advanced, const char *keyid,
+do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
             unsigned char **pk, size_t *pklen)
 {
-#if GNUPG_MAJOR_VERSION > 1
   gpg_error_t err;
   int keyno;
   unsigned char *buf;
@@ -1869,29 +1913,14 @@ do_readkey (app_t app, int advanced, const char *keyid,
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
 
-  if (advanced)
+  if ((flags & APP_READKEY_FLAG_INFO))
     {
-      gcry_sexp_t s_key;
-
-      err = gcry_sexp_new (&s_key, buf, app->app_local->pk[keyno].keylen, 0);
+      err = send_keypair_info (app, ctrl, keyno+1);
       if (err)
         return err;
-
-      *pklen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, NULL, 0);
-      *pk = xtrymalloc (*pklen);
-      if (!*pk)
-        {
-          err = gpg_error_from_syserror ();
-          *pklen = 0;
-          return err;
-        }
-
-      gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, *pk, *pklen);
-      gcry_sexp_release (s_key);
-      /* Decrement for trailing '\0' */
-      *pklen = *pklen - 1;
     }
-  else
+
+  if (pk && pklen)
     {
       *pklen = app->app_local->pk[keyno].keylen;
       *pk = xtrymalloc (*pklen);
@@ -1905,9 +1934,6 @@ do_readkey (app_t app, int advanced, const char *keyid,
     }
 
   return 0;
-#else
-  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-#endif
 }
 
 /* Read the standard certificate of an OpenPGP v2 card.  It is
@@ -2054,6 +2080,63 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
   return result;
 }
 
+#define KDF_DATA_LENGTH_MIN  90
+#define KDF_DATA_LENGTH_MAX 110
+
+/* Compute hash if KDF-DO is available.  CHVNO must be 0 for reset
+   code, 1 or 2 for user pin and 3 for admin pin.
+ */
+static gpg_error_t
+pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
+{
+  gpg_error_t err = 0;
+  void *relptr = NULL;
+  unsigned char *buffer;
+  size_t buflen;
+
+  if (app->app_local->extcap.kdf_do
+      && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL))
+      && buflen >= KDF_DATA_LENGTH_MIN && (buffer[2] == 0x03))
+    {
+      const char *salt;
+      unsigned long s2k_count;
+      char dek[32];
+      int salt_index;
+
+      s2k_count = (((unsigned int)buffer[8] << 24)
+                   | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
+
+      if (buflen == KDF_DATA_LENGTH_MIN)
+        salt_index =14;
+      else if (buflen == KDF_DATA_LENGTH_MAX)
+        salt_index = (chvno==3 ? 34 : (chvno==0 ? 24 : 14));
+      else
+        {
+          err = gpg_error (GPG_ERR_INV_DATA);
+          goto leave;
+        }
+
+      salt = &buffer[salt_index];
+      err = gcry_kdf_derive (pinvalue, strlen (pinvalue),
+                             GCRY_KDF_ITERSALTED_S2K,
+                             DIGEST_ALGO_SHA256, salt, 8,
+                             s2k_count, sizeof (dek), dek);
+      if (!err)
+        {
+          /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK.  */
+          *r_pinlen = 32;
+          memcpy (pinvalue, dek, *r_pinlen);
+          wipememory (dek, *r_pinlen);
+        }
+   }
+  else
+    *r_pinlen = strlen (pinvalue);
+
+ leave:
+  xfree (relptr);
+  return err;
+}
+
 
 /* Verify a CHV either using the pinentry or if possible by
    using a pinpad.  PINCB and PINCB_ARG describe the usual callback
@@ -2068,8 +2151,8 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
 static gpg_error_t
 verify_a_chv (app_t app,
               gpg_error_t (*pincb)(void*, const char *, char **),
-              void *pincb_arg,
-              int chvno, unsigned long sigcount, char **pinvalue)
+              void *pincb_arg, int chvno, unsigned long sigcount,
+              char **pinvalue, int *pinlen)
 {
   int rc = 0;
   char *prompt_buffer = NULL;
@@ -2081,6 +2164,7 @@ verify_a_chv (app_t app,
   log_assert (chvno == 1 || chvno == 2);
 
   *pinvalue = NULL;
+  *pinlen = 0;
 
   remaining = get_remaining_tries (app, 0);
   if (remaining == -1)
@@ -2169,8 +2253,9 @@ verify_a_chv (app_t app,
           return gpg_error (GPG_ERR_BAD_PIN);
         }
 
-      rc = iso7816_verify (app->slot, 0x80+chvno,
-                           *pinvalue, strlen (*pinvalue));
+      rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen);
+      if (!rc)
+        rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen);
     }
 
   if (rc)
@@ -2194,11 +2279,12 @@ verify_chv2 (app_t app,
 {
   int rc;
   char *pinvalue;
+  int pinlen;
 
   if (app->did_chv2)
     return 0;  /* We already verified CHV2.  */
 
-  rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
+  rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen);
   if (rc)
     return rc;
   app->did_chv2 = 1;
@@ -2209,7 +2295,7 @@ verify_chv2 (app_t app,
          the card is not configured to require a verification before
          each CHV1 controlled operation (force_chv1) and if we are not
          using the pinpad (PINVALUE == NULL). */
-      rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+      rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen);
       if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
         rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
       if (rc)
@@ -2319,6 +2405,7 @@ verify_chv3 (app_t app,
       else
         {
           char *pinvalue;
+          int pinlen;
 
           rc = pincb (pincb_arg, prompt, &pinvalue);
           xfree (prompt);
@@ -2338,7 +2425,9 @@ verify_chv3 (app_t app,
               return gpg_error (GPG_ERR_BAD_PIN);
             }
 
-          rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+          rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen);
+          if (!rc)
+            rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen);
           xfree (pinvalue);
         }
 
@@ -2367,28 +2456,33 @@ do_setattr (app_t app, const char *name,
   static struct {
     const char *name;
     int tag;
+    int flush_tag;  /* The tag which needs to be flushed or 0. */
     int need_chv;
     int special;
     unsigned int need_v2:1;
   } table[] = {
-    { "DISP-NAME",    0x005B, 3 },
-    { "LOGIN-DATA",   0x005E, 3, 2 },
-    { "DISP-LANG",    0x5F2D, 3 },
-    { "DISP-SEX",     0x5F35, 3 },
-    { "PUBKEY-URL",   0x5F50, 3 },
-    { "CHV-STATUS-1", 0x00C4, 3, 1 },
-    { "CA-FPR-1",     0x00CA, 3 },
-    { "CA-FPR-2",     0x00CB, 3 },
-    { "CA-FPR-3",     0x00CC, 3 },
-    { "PRIVATE-DO-1", 0x0101, 2 },
-    { "PRIVATE-DO-2", 0x0102, 3 },
-    { "PRIVATE-DO-3", 0x0103, 2 },
-    { "PRIVATE-DO-4", 0x0104, 3 },
-    { "CERT-3",       0x7F21, 3, 0, 1 },
-    { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
-    { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
-    { "KEY-ATTR",     0,      0, 3, 1 },
-    { "AESKEY",       0x00D5, 3, 0, 1 },
+    { "DISP-NAME",    0x005B, 0,      3 },
+    { "LOGIN-DATA",   0x005E, 0,      3, 2 },
+    { "DISP-LANG",    0x5F2D, 0,      3 },
+    { "DISP-SEX",     0x5F35, 0,      3 },
+    { "PUBKEY-URL",   0x5F50, 0,      3 },
+    { "CHV-STATUS-1", 0x00C4, 0,      3, 1 },
+    { "CA-FPR-1",     0x00CA, 0x00C6, 3 },
+    { "CA-FPR-2",     0x00CB, 0x00C6, 3 },
+    { "CA-FPR-3",     0x00CC, 0x00C6, 3 },
+    { "PRIVATE-DO-1", 0x0101, 0,      2 },
+    { "PRIVATE-DO-2", 0x0102, 0,      3 },
+    { "PRIVATE-DO-3", 0x0103, 0,      2 },
+    { "PRIVATE-DO-4", 0x0104, 0,      3 },
+    { "CERT-3",       0x7F21, 0,      3, 0, 1 },
+    { "SM-KEY-ENC",   0x00D1, 0,      3, 0, 1 },
+    { "SM-KEY-MAC",   0x00D2, 0,      3, 0, 1 },
+    { "KEY-ATTR",     0,      0,      0, 3, 1 },
+    { "AESKEY",       0x00D5, 0,      3, 0, 1 },
+    { "UIF-1",        0x00D6, 0,      3, 5, 1 },
+    { "UIF-2",        0x00D7, 0,      3, 5, 1 },
+    { "UIF-3",        0x00D8, 0,      3, 5, 1 },
+    { "KDF",          0x00F9, 0,      3, 4, 1 },
     { NULL, 0 }
   };
   int exmode;
@@ -2400,6 +2494,9 @@ do_setattr (app_t app, const char *name,
   if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported.  */
 
+  if (table[idx].special == 5 && app->app_local->extcap.has_button == 0)
+    return gpg_error (GPG_ERR_INV_OBJ);
+
   if (table[idx].special == 3)
     return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen);
 
@@ -2420,7 +2517,8 @@ do_setattr (app_t app, const char *name,
   /* Flush the cache before writing it, so that the next get operation
      will reread the data from the card and thus get synced in case of
      errors (e.g. data truncated by the card). */
-  flush_cache_item (app, table[idx].tag);
+  flush_cache_item (app, table[idx].flush_tag? table[idx].flush_tag
+                    /* */                    : table[idx].tag);
 
   if (app->app_local->cardcap.ext_lc_le && valuelen > 254)
     exmode = 1;    /* Use extended length w/o a limit.  */
@@ -2436,15 +2534,21 @@ do_setattr (app_t app, const char *name,
     app->force_chv1 = (valuelen && *value == 0);
   else if (table[idx].special == 2)
     parse_login_data (app);
+  else if (table[idx].special == 4)
+    {
+      app->did_chv1 = 0;
+      app->did_chv2 = 0;
+      app->did_chv3 = 0;
+    }
 
   return rc;
 }
 
 
-/* Handle the WRITECERT command for OpenPGP.  This rites the standard
  certifciate to the card; CERTID needs to be set to "OPENPGP.3".
  PINCB and PINCB_ARG are the usual arguments for the pinentry
  callback.  */
+/* Handle the WRITECERT command for OpenPGP.  This writes the standard
* certificate to the card; CERTID needs to be set to "OPENPGP.3".
* PINCB and PINCB_ARG are the usual arguments for the pinentry
* callback.  */
 static gpg_error_t
 do_writecert (app_t app, ctrl_t ctrl,
               const char *certidstr,
@@ -2469,6 +2573,42 @@ do_writecert (app_t app, ctrl_t ctrl,
 }
 
 
+static gpg_error_t
+clear_chv_status (app_t app, int chvno)
+{
+  unsigned char apdu[4];
+  gpg_error_t err;
+
+  if (!app->app_local->extcap.is_v2)
+    return GPG_ERR_UNSUPPORTED_OPERATION;
+
+  apdu[0] = 0x00;
+  apdu[1] = ISO7816_VERIFY;
+  apdu[2] = 0xff;
+  apdu[3] = 0x80+chvno;
+
+  err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL);
+  if (err)
+    {
+      if (gpg_err_code (err) == GPG_ERR_INV_VALUE)
+        err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+      return err;
+    }
+
+  if (chvno == 1)
+    {
+      apdu[3]++;
+      err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL);
+      app->did_chv1 = app->did_chv2 = 0;
+    }
+  else if (chvno == 2)
+    app->did_chv2 = 0;
+  else if (chvno == 3)
+    app->did_chv3 = 0;
+
+  return err;
+}
+
 
 /* Handle the PASSWD command.  The following combinations are
    possible:
@@ -2484,6 +2624,8 @@ do_writecert (app_t app, ctrl_t ctrl,
      -       2   1      Verify CHV2 and set a new CHV1 and CHV2.
      -       2   2      Verify Reset Code and set a new PW1.
      -       3   any    Verify CHV3/PW3 and set a new CHV3/PW3.
+
+   The CHVNO can be prefixed with "OPENPGP.".
  */
 static gpg_error_t
 do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
@@ -2492,7 +2634,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
                void *pincb_arg)
 {
   int rc = 0;
-  int chvno = atoi (chvnostr);
+  int chvno;
   char *resetcode = NULL;
   char *oldpinvalue = NULL;
   char *pinvalue = NULL;
@@ -2501,12 +2643,29 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
   pininfo_t pininfo;
   int use_pinpad = 0;
   int minlen = 6;
+  int pinlen0 = 0;
+  int pinlen = 0;
 
   (void)ctrl;
+
+  if (digitp (chvnostr))
+    chvno = atoi (chvnostr);
+  else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1"))
+    chvno = 1;
+  else if (!ascii_strcasecmp (chvnostr, "OPENPGP.2"))
+    chvno = 2;
+  else if (!ascii_strcasecmp (chvnostr, "OPENPGP.3"))
+    chvno = 3;
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+
   memset (&pininfo, 0, sizeof pininfo);
   pininfo.fixedlen = -1;
   pininfo.minlen = minlen;
 
+  if ((flags & APP_CHANGE_FLAG_CLEAR))
+    return clear_chv_status (app, chvno);
+
   if (reset_mode && chvno == 3)
     {
       rc = gpg_error (GPG_ERR_INV_ID);
@@ -2685,10 +2844,17 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
         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));
+          strcpy (buffer, resetcode);
+          rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0);
+          if (!rc)
+            {
+              strcpy (buffer+pinlen0, pinvalue);
+              rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen);
+            }
+          if (!rc)
+            rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+                                                      buffer, pinlen0+pinlen);
+          wipememory (buffer, pinlen0 + pinlen);
           xfree (buffer);
         }
     }
@@ -2700,16 +2866,19 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           rc = gpg_error (GPG_ERR_BAD_PIN);
         }
       else
-        rc = iso7816_put_data (app->slot, 0, 0xD3,
-                               pinvalue, strlen (pinvalue));
+        {
+          rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen);
+          if (!rc)
+            rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen);
+        }
     }
   else if (reset_mode)
     {
-      rc = iso7816_reset_retry_counter (app->slot, 0x81,
-                                        pinvalue, strlen (pinvalue));
+      rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen);
+      if (!rc)
+        rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen);
       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, 0x82, pinvalue, pinlen);
     }
   else if (!app->app_local->extcap.is_v2)
     {
@@ -2750,14 +2919,20 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
           pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
         }
       else
-        rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
-                                            oldpinvalue, strlen (oldpinvalue),
-                                            pinvalue, strlen (pinvalue));
+        {
+          rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0);
+          if (!rc)
+            rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen);
+          if (!rc)
+            rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+                                                oldpinvalue, pinlen0,
+                                                pinvalue, pinlen);
+        }
     }
 
   if (pinvalue)
     {
-      wipememory (pinvalue, strlen (pinvalue));
+      wipememory (pinvalue, pinlen);
       xfree (pinvalue);
     }
   if (rc)
@@ -2771,7 +2946,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
     }
   if (oldpinvalue)
     {
-      wipememory (oldpinvalue, strlen (oldpinvalue));
+      wipememory (oldpinvalue, pinlen0);
       xfree (oldpinvalue);
     }
   return rc;
@@ -3134,21 +3309,33 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
   relptr = get_one_do (app, 0xC1+keyno, &buf, &buflen, NULL);
   if (!relptr)
     err = gpg_error (GPG_ERR_CARD);
-  else if (buflen < 6 || buf[0] != PUBKEY_ALGO_RSA)
+  else if (buflen < 6)
     {
-      /* Attriutes too short or not an RSA key.  */
+      /* Attributes too short.  */
       xfree (relptr);
       err = gpg_error (GPG_ERR_CARD);
     }
   else
     {
-      /* We only change n_bits and don't touch anything else.  Before we
-         do so, we round up NBITS to a sensible way in the same way as
-         gpg's key generation does it.  This may help to sort out problems
-         with a few bits too short keys.  */
+      /* If key attribute was RSA, we only change n_bits and don't
+         touch anything else.  Before we do so, we round up NBITS to a
+         sensible way in the same way as gpg's key generation does it.
+         This may help to sort out problems with a few bits too short
+         keys.  */
       nbits = ((nbits + 31) / 32) * 32;
       buf[1] = (nbits >> 8);
       buf[2] = nbits;
+
+      /* If it was not RSA, we need to fill other parts.  */
+      if (buf[0] != PUBKEY_ALGO_RSA)
+        {
+          buf[0] = PUBKEY_ALGO_RSA;
+          buf[3] = 0;
+          buf[4] = 32;
+          buf[5] = 0;
+          buflen = 6;
+        }
+
       err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg);
       xfree (relptr);
     }
@@ -3537,7 +3724,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
       /* Store the key. */
       err = iso7816_put_data (app->slot, 0,
-                              (app->card_version > 0x0007? 0xE0:0xE9)+keyno,
+                              (app->appversion > 0x0007? 0xE0:0xE9)+keyno,
                               template, template_len);
     }
   if (err)
@@ -3909,8 +4096,8 @@ do_writekey (app_t app, ctrl_t ctrl,
 
 /* Handle the GENKEY command. */
 static gpg_error_t
-do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
-           time_t createtime,
+do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, const char *keytype,
+           unsigned int flags, time_t createtime,
            gpg_error_t (*pincb)(void*, const char *, char **),
            void *pincb_arg)
 {
@@ -3926,6 +4113,8 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   int exmode = 0;
   int le_value = 256; /* Use legacy value. */
 
+  (void)keytype;  /* Ignored for OpenPGP cards.  */
+
   if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
 
@@ -3974,7 +4163,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
 
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
-  err = iso7816_generate_keypair (app->slot, exmode,
+  err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0,
                                   (keyno == 0? "\xB6" :
                                    keyno == 1? "\xB8" : "\xA4"),
                                   2, le_value, &buffer, &buflen);
@@ -4105,6 +4294,50 @@ check_against_given_fingerprint (app_t app, const char *fpr, int key)
 }
 
 
+static int
+check_keyidstr (app_t app, const char *keyidstr, int keyno)
+{
+  int rc;
+  const char *s;
+  int n;
+  const char *fpr = NULL;
+  unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */
+
+  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+    return gpg_error (GPG_ERR_INV_ID);
+  else
+    {
+      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+        ;
+      if (n != 32)
+        return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+        ; /* no fingerprint given: we allow this for now. */
+      else if (*s == '/')
+        fpr = s + 1;
+      else
+        return gpg_error (GPG_ERR_INV_ID);
+
+      for (s=keyidstr, n=0; n < 16; s += 2, n++)
+        tmp_sn[n] = xtoi_2 (s);
+
+      if (app->serialnolen != 16)
+        return gpg_error (GPG_ERR_INV_CARD);
+      if (memcmp (app->serialno, tmp_sn, 16))
+        return gpg_error (GPG_ERR_WRONG_CARD);
+    }
+
+  /* If a fingerprint has been specified check it against the one on
+     the card.  This is allows for a meaningful error message in case
+     the key on the card has been replaced but the shadow information
+     known to gpg was not updated.  If there is no fingerprint, gpg
+     will detect a bogus signature anyway due to the
+     verify-after-signing feature. */
+  rc = (fpr&&keyno)? check_against_given_fingerprint (app, fpr, keyno) : 0;
+
+  return rc;
+}
+
 
 /* Compute a digital signature on INDATA which is expected to be the
    raw message digest. For this application the KEYIDSTR consists of
@@ -4150,10 +4383,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   int rc;
   unsigned char data[19+64];
   size_t datalen;
-  unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */
-  const char *s;
-  int n;
-  const char *fpr = NULL;
   unsigned long sigcount;
   int use_auth = 0;
   int exmode, le_value;
@@ -4198,40 +4427,13 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     ;
   else if (!strcmp (keyidstr, "OPENPGP.3"))
     use_auth = 1;
-  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
   else
     {
-      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-        ;
-      if (n != 32)
-        return gpg_error (GPG_ERR_INV_ID);
-      else if (!*s)
-        ; /* no fingerprint given: we allow this for now. */
-      else if (*s == '/')
-        fpr = s + 1;
-      else
-        return gpg_error (GPG_ERR_INV_ID);
-
-      for (s=keyidstr, n=0; n < 16; s += 2, n++)
-        tmp_sn[n] = xtoi_2 (s);
-
-      if (app->serialnolen != 16)
-        return gpg_error (GPG_ERR_INV_CARD);
-      if (memcmp (app->serialno, tmp_sn, 16))
-        return gpg_error (GPG_ERR_WRONG_CARD);
+      rc = check_keyidstr (app, keyidstr, 1);
+      if (rc)
+        return rc;
     }
 
-  /* If a fingerprint has been specified check it against the one on
-     the card.  This is allows for a meaningful error message in case
-     the key on the card has been replaced but the shadow information
-     known to gpg was not updated.  If there is no fingerprint, gpg
-     will detect a bogus signature anyway due to the
-     verify-after-signing feature. */
-  rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0;
-  if (rc)
-    return rc;
-
   /* Concatenate prefix and digest.  */
 #define X(a,b,d) \
   if (hashalgo == GCRY_MD_ ## a && (d) )                      \
@@ -4274,11 +4476,12 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   log_info (_("signatures created so far: %lu\n"), sigcount);
 
   /* Check CHV if needed.  */
-  if (!app->did_chv1 || app->force_chv1 )
+  if (!app->did_chv1 || app->force_chv1)
     {
       char *pinvalue;
+      int pinlen;
 
-      rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue);
+      rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen);
       if (rc)
         return rc;
 
@@ -4291,7 +4494,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
          pinpad has been used. */
       if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
         {
-          rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+          rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen);
           if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
             rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
           if (rc)
@@ -4321,6 +4524,11 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     }
   rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
                            outdata, outdatalen);
+  if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
+    clear_chv_status (app, 1);
+  else if (!rc && app->force_chv1)
+    app->did_chv1 = 0;
+
   return rc;
 }
 
@@ -4342,10 +4550,6 @@ do_auth (app_t app, const char *keyidstr,
          unsigned char **outdata, size_t *outdatalen )
 {
   int rc;
-  unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */
-  const char *s;
-  int n;
-  const char *fpr = NULL;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -4373,40 +4577,13 @@ do_auth (app_t app, const char *keyidstr,
   /* Check whether an OpenPGP card of any version has been requested. */
   if (!strcmp (keyidstr, "OPENPGP.3"))
     ;
-  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
   else
     {
-      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-        ;
-      if (n != 32)
-        return gpg_error (GPG_ERR_INV_ID);
-      else if (!*s)
-        ; /* no fingerprint given: we allow this for now. */
-      else if (*s == '/')
-        fpr = s + 1;
-      else
-        return gpg_error (GPG_ERR_INV_ID);
-
-      for (s=keyidstr, n=0; n < 16; s += 2, n++)
-        tmp_sn[n] = xtoi_2 (s);
-
-      if (app->serialnolen != 16)
-        return gpg_error (GPG_ERR_INV_CARD);
-      if (memcmp (app->serialno, tmp_sn, 16))
-        return gpg_error (GPG_ERR_WRONG_CARD);
+      rc = check_keyidstr (app, keyidstr, 3);
+      if (rc)
+        return rc;
     }
 
-  /* If a fingerprint has been specified check it against the one on
-     the card.  This is allows for a meaningful error message in case
-     the key on the card has been replaced but the shadow information
-     known to gpg was not updated.  If there is no fingerprint, gpg
-     will detect a bogus signature anyway due to the
-     verify-after-signing feature. */
-  rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0;
-  if (rc)
-    return rc;
-
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (!rc)
     {
@@ -4427,6 +4604,8 @@ do_auth (app_t app, const char *keyidstr,
       rc = iso7816_internal_authenticate (app->slot, exmode,
                                           indata, indatalen, le_value,
                                           outdata, outdatalen);
+      if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
+        clear_chv_status (app, 1);
     }
   return rc;
 }
@@ -4440,11 +4619,8 @@ do_decipher (app_t app, const char *keyidstr,
              unsigned char **outdata, size_t *outdatalen,
              unsigned int *r_info)
 {
-  int rc;
-  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
-  const char *s;
   int n;
-  const char *fpr = NULL;
+  int rc;
   int exmode, le_value;
   unsigned char *fixbuf = NULL;
   int padind = 0;
@@ -4456,39 +4632,13 @@ do_decipher (app_t app, const char *keyidstr,
   /* Check whether an OpenPGP card of any version has been requested. */
   if (!strcmp (keyidstr, "OPENPGP.2"))
     ;
-  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
   else
     {
-      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-        ;
-      if (n != 32)
-        return gpg_error (GPG_ERR_INV_ID);
-      else if (!*s)
-        ; /* no fingerprint given: we allow this for now. */
-      else if (*s == '/')
-        fpr = s + 1;
-      else
-        return gpg_error (GPG_ERR_INV_ID);
-
-      for (s=keyidstr, n=0; n < 16; s += 2, n++)
-        tmp_sn[n] = xtoi_2 (s);
-
-      if (app->serialnolen != 16)
-        return gpg_error (GPG_ERR_INV_CARD);
-      if (memcmp (app->serialno, tmp_sn, 16))
-        return gpg_error (GPG_ERR_WRONG_CARD);
+      rc = check_keyidstr (app, keyidstr, 2);
+      if (rc)
+        return rc;
     }
 
-  /* If a fingerprint has been specified check it against the one on
-     the card.  This is allows for a meaningful error message in case
-     the key on the card has been replaced but the shadow information
-     known to gpg was not updated.  If there is no fingerprint, the
-     decryption won't produce the right plaintext anyway. */
-  rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
-  if (rc)
-    return rc;
-
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (rc)
     return rc;
@@ -4575,19 +4725,43 @@ do_decipher (app_t app, const char *keyidstr,
             }
         }
 
-      fixuplen = 7;
+      n = 0;
+      if (indatalen < 128)
+        fixuplen = 7;
+      else
+        fixuplen = 10;
+
       fixbuf = xtrymalloc (fixuplen + indatalen);
       if (!fixbuf)
         return gpg_error_from_syserror ();
 
       /* Build 'Cipher DO' */
-      fixbuf[0] = '\xa6';
-      fixbuf[1] = (char)(indatalen+5);
-      fixbuf[2] = '\x7f';
-      fixbuf[3] = '\x49';
-      fixbuf[4] = (char)(indatalen+2);
-      fixbuf[5] = '\x86';
-      fixbuf[6] = (char)indatalen;
+      fixbuf[n++] = '\xa6';
+      if (indatalen < 128)
+        fixbuf[n++] = (char)(indatalen+5);
+      else
+        {
+          fixbuf[n++] = 0x81;
+          fixbuf[n++] = (char)(indatalen+7);
+        }
+      fixbuf[n++] = '\x7f';
+      fixbuf[n++] = '\x49';
+      if (indatalen < 128)
+        fixbuf[n++] = (char)(indatalen+2);
+      else
+        {
+          fixbuf[n++] = 0x81;
+          fixbuf[n++] = (char)(indatalen+3);
+        }
+      fixbuf[n++] = '\x86';
+      if (indatalen < 128)
+        fixbuf[n++] = (char)indatalen;
+      else
+        {
+          fixbuf[n++] = 0x81;
+          fixbuf[n++] = (char)indatalen;
+        }
+
       if (old_format_len)
         {
           memset (fixbuf+fixuplen, 0, 32 - old_format_len);
@@ -4626,7 +4800,7 @@ do_decipher (app_t app, const char *keyidstr,
                          indata, indatalen, le_value, padind,
                          outdata, outdatalen);
   xfree (fixbuf);
-  if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+  if (!rc && app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
     {
       unsigned char prefix = 0;
 
@@ -4650,10 +4824,12 @@ do_decipher (app_t app, const char *keyidstr,
           *outdatalen = *outdatalen + 1;
         }
     }
+  if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
+    clear_chv_status (app, 1);
 
   if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
       && app->app_local->manufacturer == 5
-      && app->card_version == 0x0200)
+      && app->appversion == 0x0200)
     log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
               " do not work with encryption keys > 2048 bits\n");
 
@@ -4678,38 +4854,18 @@ do_check_pin (app_t app, const char *keyidstr,
               gpg_error_t (*pincb)(void*, const char *, char **),
               void *pincb_arg)
 {
-  unsigned char tmp_sn[20];
-  const char *s;
-  int n;
   int admin_pin = 0;
+  int rc;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  /* Check whether an OpenPGP card of any version has been requested. */
-  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
+  rc = check_keyidstr (app, keyidstr, 0);
+  if (rc)
+    return rc;
 
-  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-    ;
-  if (n != 32)
-    return gpg_error (GPG_ERR_INV_ID);
-  else if (!*s)
-    ; /* No fingerprint given: we allow this for now. */
-  else if (*s == '/')
-    ; /* We ignore a fingerprint. */
-  else if (!strcmp (s, "[CHV3]") )
+  if (strlen (keyidstr) >= 32+6 && !strcmp (keyidstr+32, "[CHV3]"))
     admin_pin = 1;
-  else
-    return gpg_error (GPG_ERR_INV_ID);
-
-  for (s=keyidstr, n=0; n < 16; s += 2, n++)
-    tmp_sn[n] = xtoi_2 (s);
-
-  if (app->serialnolen != 16)
-    return gpg_error (GPG_ERR_INV_CARD);
-  if (memcmp (app->serialno, tmp_sn, 16))
-    return gpg_error (GPG_ERR_WRONG_CARD);
 
   /* Yes, there is a race conditions: The user might pull the card
      right here and we won't notice that.  However this is not a
@@ -4949,7 +5105,7 @@ parse_algorithm_attribute (app_t app, int keyno)
       curve = ecc_curve (buffer + 1, oidlen);
 
       if (!curve)
-        log_printhex ("Curve with OID not supported: ", buffer+1, buflen-1);
+        log_printhex (buffer+1, buflen-1, "Curve with OID not supported: ");
       else
         {
           app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
@@ -4967,7 +5123,7 @@ parse_algorithm_attribute (app_t app, int keyno)
         }
     }
   else if (opt.verbose)
-    log_printhex ("", buffer, buflen);
+    log_printhex (buffer, buflen, "");
 
   xfree (relptr);
 }
@@ -5009,11 +5165,11 @@ app_select_openpgp (app_t app)
       if (opt.verbose)
         {
           log_info ("AID: ");
-          log_printhex ("", buffer, buflen);
+          log_printhex (buffer, buflen, "");
         }
 
-      app->card_version = buffer[6] << 8;
-      app->card_version |= buffer[7];
+      app->appversion = buffer[6] << 8;
+      app->appversion |= buffer[7];
       manufacturer = (buffer[8]<<8 | buffer[9]);
 
       xfree (app->serialno);
@@ -5029,10 +5185,10 @@ app_select_openpgp (app_t app)
 
       app->app_local->manufacturer = manufacturer;
 
-      if (app->card_version >= 0x0200)
+      if (app->appversion >= 0x0200)
         app->app_local->extcap.is_v2 = 1;
 
-      if (app->card_version >= 0x0300)
+      if (app->appversion >= 0x0300)
         app->app_local->extcap.extcap_v3 = 1;
 
       /* Read the historical bytes.  */
@@ -5042,7 +5198,7 @@ app_select_openpgp (app_t app)
           if (opt.verbose)
             {
               log_info ("Historical Bytes: ");
-              log_printhex ("", buffer, buflen);
+              log_printhex (buffer, buflen, "");
             }
           parse_historical (app->app_local, buffer, buflen);
           xfree (relptr);
@@ -5099,7 +5255,7 @@ app_select_openpgp (app_t app)
 
       /* Some of the first cards accidentally don't set the
          CHANGE_FORCE_CHV bit but allow it anyway. */
-      if (app->card_version <= 0x0100 && manufacturer == 1)
+      if (app->appversion <= 0x0100 && manufacturer == 1)
         app->app_local->extcap.change_force_chv = 1;
 
       /* Check optional DO of "General Feature Management" for button.  */