Fix comment of configure.
[gnupg.git] / scd / app-openpgp.c
index 4e042e7..6fcec3e 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 /* Some notes:
 #include "scdaemon.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
-#include "util.h"
-#include "i18n.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
 #include "iso7816.h"
 #include "app-common.h"
-#include "tlv.h"
-#include "host2net.h"
-#include "openpgpdefs.h"
+#include "../common/tlv.h"
+#include "../common/host2net.h"
+#include "../common/openpgpdefs.h"
 
 
 /* A table describing the DOs of the card.  */
@@ -76,21 +76,24 @@ 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:1;           /* Large object; try to use an extended
-                                 length APDU.  */
+  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
+                                 when == 2.  */
   char *desc;
 } data_objects[] = {
-  { 0x005E, 0,    0, 1, 0, 0, 0, 0, "Login Data" },
-  { 0x5F50, 0,    0, 0, 0, 0, 0, 0, "URL" },
+  { 0x005E, 0,    0, 1, 0, 0, 0, 2, "Login Data" },
+  { 0x5F50, 0,    0, 0, 0, 0, 0, 2, "URL" },
   { 0x5F52, 0,    0, 1, 0, 0, 0, 0, "Historical Bytes" },
   { 0x0065, 1,    0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
   { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
@@ -110,10 +113,10 @@ static struct {
   { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
   { 0x007A, 1,    0, 1, 0, 0, 0, 0, "Security Support Template" },
   { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
-  { 0x0101, 0,    0, 0, 0, 0, 0, 0, "Private DO 1"},
-  { 0x0102, 0,    0, 0, 0, 0, 0, 0, "Private DO 2"},
-  { 0x0103, 0,    0, 0, 0, 0, 0, 0, "Private DO 3"},
-  { 0x0104, 0,    0, 0, 0, 0, 0, 0, "Private DO 4"},
+  { 0x0101, 0,    0, 0, 0, 0, 0, 2, "Private DO 1"},
+  { 0x0102, 0,    0, 0, 0, 0, 0, 2, "Private DO 2"},
+  { 0x0103, 0,    0, 0, 0, 0, 0, 2, "Private DO 3"},
+  { 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"},
@@ -185,20 +188,25 @@ struct app_local_s {
   /* Keep track of extended card capabilities.  */
   struct
   {
-    unsigned int is_v2:1;              /* This is a v2.0 compatible card.  */
-    unsigned int sm_supported:1;       /* Secure Messaging is supported.  */
+    unsigned int is_v2:1;              /* Compatible to v2 or later.        */
+    unsigned int extcap_v3:1;          /* Extcap is in v3 format.           */
+    unsigned int has_button:1;         /* Has confirmation button or not.   */
+
+    unsigned int sm_supported:1;       /* Secure Messaging is supported.    */
     unsigned int get_challenge:1;
     unsigned int key_import:1;
     unsigned int change_force_chv:1;
     unsigned int private_dos:1;
     unsigned int algo_attr_change:1;   /* Algorithm attributes changeable.  */
-    unsigned int has_decrypt:1;        /* Support symmetric decryption.  */
-    unsigned int has_button:1;
-    unsigned int sm_algo:2;            /* Symmetric crypto algo for SM.  */
+    unsigned int has_decrypt:1;        /* Support symmetric decryption.     */
+    unsigned int kdf_do:1;                /* Support KDF DOs.               */
+
+    unsigned int sm_algo:2;            /* Symmetric crypto algo for SM.     */
+    unsigned int pin_blk2:1;           /* PIN block 2 format supported.     */
+    unsigned int mse:1;                /* MSE command supported.            */
     unsigned int max_certlen_3:16;
-    unsigned int max_get_challenge:16; /* Maximum size for get_challenge.  */
-    unsigned int max_cmd_data:16;      /* Maximum data size for a command.  */
-    unsigned int max_rsp_data:16;      /* Maximum size of a response.  */
+    unsigned int max_get_challenge:16; /* Maximum size for get_challenge.   */
+    unsigned int max_special_do:16;    /* Maximum size for special DOs.     */
   } extcap;
 
   /* Flags used to control the application.  */
@@ -325,7 +333,14 @@ get_cached_data (app_t app, int tag,
     }
 
   if (try_extlen && app->app_local->cardcap.ext_lc_le)
-    exmode = app->app_local->extcap.max_rsp_data;
+    {
+      if (try_extlen == 1)
+        exmode = app->app_local->extcap.max_certlen_3;
+      else if (try_extlen == 2 && app->app_local->extcap.extcap_v3)
+        exmode = app->app_local->extcap.max_special_do;
+      else
+        exmode = 0;
+    }
   else
     exmode = 0;
 
@@ -455,10 +470,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
 
   if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
     {
-      if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le)
-        exmode = app->app_local->extcap.max_rsp_data;
-      else
-        exmode = 0;
+      exmode = 0;
       rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
       if (rc)
         {
@@ -624,7 +636,7 @@ count_bits (const unsigned char *a, size_t len)
     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"
+      Bit 1 = CHV2 has been set to the default PIN of "123456"
               (this implies that bit 0 is also set).
 
     P=<pinpad-request>
@@ -922,6 +934,22 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
 }
 
 
+#define RSA_SMALL_SIZE_KEY 1952
+#define RSA_SMALL_SIZE_OP  2048
+
+static int
+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+7)/8
+    + 2 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.e_bits+7)/8;
+
+  return size;
+}
+
+
 /* Implement the GETATTR command.  This is similar to the LEARN
    command but returns just one value via the status interface. */
 static gpg_error_t
@@ -967,21 +995,13 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
   if (table[idx].special == -1)
     {
       /* The serial number is very special.  We could have used the
-         AID DO to retrieve it, but we have it already in the app
-         context and the stamp argument is required anyway which we
-         can't by other means. The AID DO is available anyway but not
-         hex formatted. */
-      char *serial;
-      time_t stamp;
-      char tmp[50];
-
-      if (!app_get_serial_and_stamp (app, &serial, &stamp))
+         AID DO to retrieve it.  The AID DO is available anyway but
+         not hex formatted. */
+      char *serial = app_get_serialno (app);
+
+      if (serial)
         {
-          sprintf (tmp, "%lu", (unsigned long)stamp);
-          send_status_info (ctrl, "SERIALNO",
-                            serial, strlen (serial),
-                            tmp, strlen (tmp),
-                            NULL, 0);
+          send_status_direct (ctrl, "SERIALNO", serial);
           xfree (serial);
         }
       return 0;
@@ -1018,10 +1038,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     }
   if (table[idx].special == -4)
     {
-      char *serial;
-      time_t stamp;
+      char *serial = app_get_serialno (app);
 
-      if (!app_get_serial_and_stamp (app, &serial, &stamp))
+      if (serial)
         {
           if (strlen (serial) > 16+12)
             {
@@ -1080,6 +1099,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
@@ -1115,8 +1232,8 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
 #if GNUPG_MAJOR_VERSION > 1
 static gpg_error_t
 retrieve_key_material (FILE *fp, const char *hexkeyid,
-                      const unsigned char **m, size_t *mlen,
-                      const unsigned char **e, size_t *elen)
+                       const unsigned char **m, size_t *mlen,
+                       const unsigned char **e, size_t *elen)
 {
   gcry_error_t err = 0;
   char *line = NULL;    /* read_line() buffer. */
@@ -1146,10 +1263,10 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
       if (!i)
         break; /* EOF. */
       if (i < 0)
-       {
-         err = gpg_error_from_syserror ();
-         goto leave; /* Error. */
-       }
+        {
+          err = gpg_error_from_syserror ();
+          goto leave; /* Error. */
+        }
       if (!max_length)
         {
           err = gpg_error (GPG_ERR_TRUNCATED);
@@ -1173,14 +1290,13 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
                && nfields > 4 && !strcmp (fields[4], hexkeyid))
             found_key = 1;
           continue;
-       }
+        }
 
       if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
         break; /* Next key - stop.  */
 
       if ( strcmp (fields[0], "pkd") )
         continue; /* Not a key data record.  */
-      i = 0; /* Avoid erroneous compiler warning. */
       if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
            || (!i && m_new) || (i && e_new))
         {
@@ -1489,7 +1605,7 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
 }
 
 
-/* Get the public key for KEYNO and store it as an S-expresion with
+/* Get the public key for KEYNO and store it as an S-expression with
    the APP handle.  On error that field gets cleared.  If we already
    know about the public key we will just return.  Note that this does
    not mean a key is available; this is solely indicated by the
@@ -1497,7 +1613,7 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
 
    Note that GnuPG 1.x does not need this and it would be too time
    consuming to send it just for the fun of it. However, given that we
-   use the same code in gpg 1.4, we can't use the gcry S-expresion
+   use the same code in gpg 1.4, we can't use the gcry S-expression
    here but need to open encode it. */
 #if GNUPG_MAJOR_VERSION > 1
 static gpg_error_t
@@ -1531,10 +1647,12 @@ get_public_key (app_t app, int keyno)
       int exmode, le_value;
 
       /* We may simply read the public key out of these cards.  */
-      if (app->app_local->cardcap.ext_lc_le)
+      if (app->app_local->cardcap.ext_lc_le
+          && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA
+          && app->app_local->keyattr[keyno].rsa.n_bits > RSA_SMALL_SIZE_KEY)
         {
           exmode = 1;    /* Use extended length.  */
-          le_value = app->app_local->extcap.max_rsp_data;
+          le_value = determine_rsa_response (app, keyno);
         }
       else
         {
@@ -1561,8 +1679,8 @@ get_public_key (app_t app, int keyno)
          Clearly that is not an option and thus we try to locate the
          key using an external helper.
 
-        The helper we use here is gpg itself, which should know about
-        the key in any case.  */
+         The helper we use here is gpg itself, which should know about
+         the key in any case.  */
 
       char fpr[41];
       char *hexkeyid;
@@ -1574,38 +1692,38 @@ get_public_key (app_t app, int keyno)
 
       err = retrieve_fpr_from_card (app, keyno, fpr);
       if (err)
-       {
-         log_error ("error while retrieving fpr from card: %s\n",
-                    gpg_strerror (err));
-         goto leave;
-       }
+        {
+          log_error ("error while retrieving fpr from card: %s\n",
+                     gpg_strerror (err));
+          goto leave;
+        }
       hexkeyid = fpr + 24;
 
       ret = gpgrt_asprintf
         (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr);
       if (ret < 0)
-       {
-         err = gpg_error_from_syserror ();
-         goto leave;
-       }
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
 
       fp = popen (command, "r");
       xfree (command);
       if (!fp)
-       {
-         err = gpg_error_from_syserror ();
-         log_error ("running gpg failed: %s\n", gpg_strerror (err));
-         goto leave;
-       }
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("running gpg failed: %s\n", gpg_strerror (err));
+          goto leave;
+        }
 
       err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
       pclose (fp);
       if (err)
-       {
-         log_error ("error while retrieving key material through pipe: %s\n",
+        {
+          log_error ("error while retrieving key material through pipe: %s\n",
                      gpg_strerror (err));
-         goto leave;
-       }
+          goto leave;
+        }
 
       err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
                              (int)mlen, m, (int)elen, e);
@@ -1726,7 +1844,8 @@ 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, const char *keyid, unsigned char **pk, size_t *pklen)
+do_readkey (app_t app, int advanced, const char *keyid,
+            unsigned char **pk, size_t *pklen)
 {
 #if GNUPG_MAJOR_VERSION > 1
   gpg_error_t err;
@@ -1749,15 +1868,42 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   buf = app->app_local->pk[keyno].key;
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
-  *pklen = app->app_local->pk[keyno].keylen;;
-  *pk = xtrymalloc (*pklen);
-  if (!*pk)
+
+  if (advanced)
     {
-      err = gpg_error_from_syserror ();
-      *pklen = 0;
-      return err;
+      gcry_sexp_t s_key;
+
+      err = gcry_sexp_new (&s_key, buf, app->app_local->pk[keyno].keylen, 0);
+      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
+    {
+      *pklen = app->app_local->pk[keyno].keylen;
+      *pk = xtrymalloc (*pklen);
+      if (!*pk)
+        {
+          err = gpg_error_from_syserror ();
+          *pklen = 0;
+          return err;
+        }
+      memcpy (*pk, buf, *pklen);
     }
-  memcpy (*pk, buf, *pklen);
+
   return 0;
 #else
   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
@@ -1842,7 +1988,74 @@ check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
 }
 
 
-/* Verify a CHV either using using the pinentry or if possible by
+/* 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)
+    {
+      /* TRANSLATORS: Put a \x1f right before a colon.  This can be
+       * used by pinentry to nicely align the names and values.  Keep
+       * the %s at the start and end of the string.  */
+      result = xtryasprintf (_("%s"
+                               "Number\x1f: %s%%0A"
+                               "Holder\x1f: %s%%0A"
+                               "Counter\x1f: %lu"
+                               "%s"),
+                             "\x1e",
+                             serial,
+                             disp_name? disp_name:"",
+                             sigcount,
+                             "");
+    }
+  else
+    {
+      result = xtryasprintf (_("%s"
+                               "Number\x1f: %s%%0A"
+                               "Holder\x1f: %s"
+                               "%s"),
+                             "\x1e",
+                             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
    used with CHV1.  PINVALUE is the address of a pointer which will
@@ -1863,11 +2076,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. */
@@ -1891,22 +2109,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)
@@ -1929,7 +2144,7 @@ verify_a_chv (app_t app,
       /* Dismiss the prompt. */
       pincb (pincb_arg, NULL, NULL);
 
-      assert (!*pinvalue);
+      log_assert (!*pinvalue);
     }
   else
     {
@@ -2017,29 +2232,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",
@@ -2047,16 +2253,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 ();
 
@@ -2366,7 +2569,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
         }
       else if (chvno == 1 || chvno == 3)
         {
-         if (!use_pinpad)
+          if (!use_pinpad)
             {
               char *promptbuf = NULL;
               const char *prompt;
@@ -2465,7 +2668,7 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
       rc = pincb (pincb_arg, set_resetcode? _("|RN|New Reset Code") :
                   chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
                   &pinvalue);
-      if (rc)
+      if (rc || pinvalue == NULL)
         {
           log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
           goto leave;
@@ -3165,8 +3368,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
   nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
   if (opt.verbose)
-    log_info ("RSA modulus size is %u bits (%u bytes)\n",
-              nbits, (unsigned int)rsa_n_len);
+    log_info ("RSA modulus size is %u bits\n", nbits);
   if (nbits && nbits != maxbits
       && app->app_local->extcap.algo_attr_change)
     {
@@ -3413,7 +3615,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
           memcpy (curve_name, tok, toklen);
           curve_name[toklen] = 0;
-          curve = openpgp_is_curve_supported (curve_name, NULL);
+          curve = openpgp_is_curve_supported (curve_name, NULL, NULL);
           xfree (curve_name);
         }
       else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
@@ -3539,11 +3741,23 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
     {
       if (app->app_local->extcap.algo_attr_change)
         {
-          unsigned char keyattr[oid_len];
+          unsigned char *keyattr;
 
+          if (!oid_len)
+            {
+              err = gpg_error (GPG_ERR_INTERNAL);
+              goto leave;
+            }
+          keyattr = xtrymalloc (oid_len);
+          if (!keyattr)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
           keyattr[0] = algo;
           memcpy (keyattr+1, oidbuf+1, oid_len-1);
           err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg);
+          xfree (keyattr);
           if (err)
             goto leave;
         }
@@ -3741,12 +3955,11 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       if (keybits > 4096)
         return gpg_error (GPG_ERR_TOO_LARGE);
 
-      /* Test whether we will need extended length mode.  (1900 is an
-         arbitrary length which for sure fits into a short apdu.)  */
-      if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+      if (app->app_local->cardcap.ext_lc_le && keybits > RSA_SMALL_SIZE_KEY
+          && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
         {
           exmode = 1;    /* Use extended length w/o a limit.  */
-          le_value = app->app_local->extcap.max_rsp_data;
+          le_value = determine_rsa_response (app, keyno);
           /* No need to check le_value because it comes from a 16 bit
              value and thus can't create an overflow on a 32 bit
              system.  */
@@ -3990,23 +4203,23 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   else
     {
       for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-       ;
+        ;
       if (n != 32)
-       return gpg_error (GPG_ERR_INV_ID);
+        return gpg_error (GPG_ERR_INV_ID);
       else if (!*s)
-       ; /* no fingerprint given: we allow this for now. */
+        ; /* no fingerprint given: we allow this for now. */
       else if (*s == '/')
-       fpr = s + 1;
+        fpr = s + 1;
       else
-       return gpg_error (GPG_ERR_INV_ID);
+        return gpg_error (GPG_ERR_INV_ID);
 
       for (s=keyidstr, n=0; n < 16; s += 2, n++)
-       tmp_sn[n] = xtoi_2 (s);
+        tmp_sn[n] = xtoi_2 (s);
 
       if (app->serialnolen != 16)
-       return gpg_error (GPG_ERR_INV_CARD);
+        return gpg_error (GPG_ERR_INV_CARD);
       if (memcmp (app->serialno, tmp_sn, 16))
-       return gpg_error (GPG_ERR_WRONG_CARD);
+        return gpg_error (GPG_ERR_WRONG_CARD);
     }
 
   /* If a fingerprint has been specified check it against the one on
@@ -4094,10 +4307,12 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     }
 
 
-  if (app->app_local->cardcap.ext_lc_le)
+  if (app->app_local->cardcap.ext_lc_le
+      && app->app_local->keyattr[0].key_type == KEY_TYPE_RSA
+      && app->app_local->keyattr[0].rsa.n_bits > RSA_SMALL_SIZE_OP)
     {
       exmode = 1;    /* Use extended length.  */
-      le_value = app->app_local->extcap.max_rsp_data;
+      le_value = app->app_local->keyattr[0].rsa.n_bits / 8;
     }
   else
     {
@@ -4197,10 +4412,12 @@ do_auth (app_t app, const char *keyidstr,
     {
       int exmode, le_value;
 
-      if (app->app_local->cardcap.ext_lc_le)
+      if (app->app_local->cardcap.ext_lc_le
+          && app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+          && app->app_local->keyattr[2].rsa.n_bits > RSA_SMALL_SIZE_OP)
         {
           exmode = 1;    /* Use extended length.  */
-          le_value = app->app_local->extcap.max_rsp_data;
+          le_value = app->app_local->keyattr[2].rsa.n_bits / 8;
         }
       else
         {
@@ -4244,23 +4461,23 @@ do_decipher (app_t app, const char *keyidstr,
   else
     {
       for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
-       ;
+        ;
       if (n != 32)
-       return gpg_error (GPG_ERR_INV_ID);
+        return gpg_error (GPG_ERR_INV_ID);
       else if (!*s)
-       ; /* no fingerprint given: we allow this for now. */
+        ; /* no fingerprint given: we allow this for now. */
       else if (*s == '/')
-       fpr = s + 1;
+        fpr = s + 1;
       else
-       return gpg_error (GPG_ERR_INV_ID);
+        return gpg_error (GPG_ERR_INV_ID);
 
       for (s=keyidstr, n=0; n < 16; s += 2, n++)
-       tmp_sn[n] = xtoi_2 (s);
+        tmp_sn[n] = xtoi_2 (s);
 
       if (app->serialnolen != 16)
-       return gpg_error (GPG_ERR_INV_CARD);
+        return gpg_error (GPG_ERR_INV_CARD);
       if (memcmp (app->serialno, tmp_sn, 16))
-       return gpg_error (GPG_ERR_WRONG_CARD);
+        return gpg_error (GPG_ERR_WRONG_CARD);
     }
 
   /* If a fingerprint has been specified check it against the one on
@@ -4358,19 +4575,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);
@@ -4389,10 +4630,13 @@ do_decipher (app_t app, const char *keyidstr,
   else
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+  if (app->app_local->cardcap.ext_lc_le
+      && (indatalen > 254
+          || (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA
+              && app->app_local->keyattr[1].rsa.n_bits > RSA_SMALL_SIZE_OP)))
     {
       exmode = 1;    /* Extended length w/o a limit.  */
-      le_value = app->app_local->extcap.max_rsp_data;
+      le_value = app->app_local->keyattr[1].rsa.n_bits / 8;
     }
   else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
     {
@@ -4406,20 +4650,29 @@ 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
-      && (app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK))
-    { /* Add the prefix 0x40 */
-      fixbuf = xtrymalloc (*outdatalen + 1);
-      if (!fixbuf)
-        {
+  if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+    {
+      unsigned char prefix = 0;
+
+      if (app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK)
+        prefix = 0x40;
+      else if ((*outdatalen % 2) == 0) /* No 0x04 -> x-coordinate only */
+        prefix = 0x41;
+
+      if (prefix)
+        { /* Add the prefix */
+          fixbuf = xtrymalloc (*outdatalen + 1);
+          if (!fixbuf)
+            {
+              xfree (*outdata);
+              return gpg_error_from_syserror ();
+            }
+          fixbuf[0] = prefix;
+          memcpy (fixbuf+1, *outdata, *outdatalen);
           xfree (*outdata);
-          return gpg_error_from_syserror ();
+          *outdata = fixbuf;
+          *outdatalen = *outdatalen + 1;
         }
-      fixbuf[0] = 0x40;
-      memcpy (fixbuf+1, *outdata, *outdatalen);
-      xfree (*outdata);
-      *outdata = fixbuf;
-      *outdatalen = *outdatalen + 1;
     }
 
   if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
@@ -4528,7 +4781,14 @@ do_check_pin (app_t app, const char *keyidstr,
 static void
 show_caps (struct app_local_s *s)
 {
-  log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no");
+  log_info ("Version-2+ .....: %s\n", s->extcap.is_v2? "yes":"no");
+  log_info ("Extcap-v3 ......: %s\n", s->extcap.extcap_v3? "yes":"no");
+  log_info ("Button .........: %s\n", s->extcap.has_button? "yes":"no");
+
+  log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
+  if (s->extcap.sm_supported)
+    log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
+                (s->extcap.sm_algo==2? "AES-128" : "AES-256"));
   log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
   if (s->extcap.get_challenge)
     log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
@@ -4536,18 +4796,18 @@ show_caps (struct app_local_s *s)
   log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
   log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
   log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no");
-  log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
-  if (s->extcap.sm_supported)
-    log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
-                (s->extcap.sm_algo==2? "AES-128" : "AES-256"));
+  log_info ("Symmetric Crypto: %s\n", s->extcap.has_decrypt? "yes":"no");
+  log_info ("KDF-Support ....: %s\n", s->extcap.kdf_do? "yes":"no");
   log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
-  log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data);
-  log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data);
+  if (s->extcap.extcap_v3)
+    {
+      log_info ("PIN-Block-2 ....: %s\n", s->extcap.pin_blk2? "yes":"no");
+      log_info ("MSE-Support ....: %s\n", s->extcap.mse? "yes":"no");
+      log_info ("Max-Special-DOs : %u\n", s->extcap.max_special_do);
+    }
   log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
   log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
-  log_info ("Status Indicator: %02X\n", s->status_indicator);
-  log_info ("Symmetric crypto: %s\n", s->extcap.has_decrypt? "yes":"no");
-  log_info ("Button..........: %s\n", s->extcap.has_button? "yes":"no");
+  log_info ("Status-Indicator: %02X\n", s->status_indicator);
 
   log_info ("GnuPG-No-Sync ..: %s\n",  s->flags.no_sync? "yes":"no");
   log_info ("GnuPG-Def-PW2 ..: %s\n",  s->flags.def_chv2? "yes":"no");
@@ -4796,6 +5056,8 @@ app_select_openpgp (app_t app)
       if (app->card_version >= 0x0200)
         app->app_local->extcap.is_v2 = 1;
 
+      if (app->card_version >= 0x0300)
+        app->app_local->extcap.extcap_v3 = 1;
 
       /* Read the historical bytes.  */
       relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
@@ -4838,16 +5100,24 @@ app_select_openpgp (app_t app)
           app->app_local->extcap.private_dos      = !!(*buffer & 0x08);
           app->app_local->extcap.algo_attr_change = !!(*buffer & 0x04);
           app->app_local->extcap.has_decrypt      = !!(*buffer & 0x02);
+          app->app_local->extcap.kdf_do           = !!(*buffer & 0x01);
         }
       if (buflen >= 10)
         {
-          /* Available with v2 cards.  */
+          /* Available with cards of v2 or later.  */
           app->app_local->extcap.sm_algo = buffer[1];
           app->app_local->extcap.max_get_challenge
                                                = (buffer[2] << 8 | buffer[3]);
           app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
-          app->app_local->extcap.max_cmd_data  = (buffer[6] << 8 | buffer[7]);
-          app->app_local->extcap.max_rsp_data  = (buffer[8] << 8 | buffer[9]);
+
+          /* Interpretation is different between v2 and v3, unfortunately.  */
+          if (app->app_local->extcap.extcap_v3)
+            {
+              app->app_local->extcap.max_special_do
+                = (buffer[6] << 8 | buffer[7]);
+              app->app_local->extcap.pin_blk2 = !!(buffer[8] & 0x01);
+              app->app_local->extcap.mse= !!(buffer[9] & 0x01);
+            }
         }
       xfree (relptr);