scd: Cleanup SERIALNO protocol.
[gnupg.git] / scd / app-openpgp.c
index 10bd64e..71c9e1b 100644 (file)
@@ -1,6 +1,6 @@
 /* app-openpgp.c - The OpenPGP card application.
  * Copyright (C) 2003, 2004, 2005, 2007, 2008,
- *               2009, 2013, 2014 Free Software Foundation, Inc.
+ *               2009, 2013, 2014, 2015 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -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 "options.h"
 #include "errors.h"
 #include "memory.h"
-#include "util.h"
 #include "cardglue.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
+#include "util.h"
 #include "i18n.h"
 #include "iso7816.h"
 #include "app-common.h"
 #include "tlv.h"
 #include "host2net.h"
+#include "openpgpdefs.h"
 
 
 /* A table describing the DOs of the card.  */
@@ -114,6 +115,9 @@ static struct {
   { 0x0103, 0,    0, 0, 0, 0, 0, 0, "Private DO 3"},
   { 0x0104, 0,    0, 0, 0, 0, 0, 0, "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"},
+  { 0x00D5, 0,    0, 1, 0, 0, 0, 0, "AES key data"},
   { 0 }
 };
 
@@ -122,7 +126,6 @@ static struct {
 typedef enum
   {
     KEY_TYPE_ECC,
-    KEY_TYPE_EDDSA,
     KEY_TYPE_RSA,
   }
 key_type_t;
@@ -140,18 +143,6 @@ typedef enum
 rsa_key_format_t;
 
 
-/* Elliptic Curves.  */
-enum
-  {
-    CURVE_NIST_P256,
-    CURVE_NIST_P384,
-    CURVE_NIST_P521,
-    CURVE_SEC_P256K1,
-    CURVE_ED25519,
-    CURVE_UNKNOWN,
-  };
-
-
 /* One cache item for DOs.  */
 struct cache_s {
   struct cache_s *next;
@@ -195,17 +186,17 @@ struct app_local_s {
   struct
   {
     unsigned int is_v2:1;              /* This is a v2.0 compatible card.  */
+    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 sm_supported:1;       /* Secure Messaging is supported.  */
-    unsigned int sm_aes128:1;          /* Use AES-128 for SM.  */
+    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 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.  */
   } extcap;
 
   /* Flags used to control the application.  */
@@ -235,15 +226,15 @@ struct app_local_s {
         rsa_key_format_t format;
       } rsa;
       struct {
-        int curve;
+        const char *curve;
+        int flags;
       } ecc;
-      struct {
-        int curve;
-      } eddsa;
     };
    } keyattr[3];
 };
 
+#define ECC_FLAG_DJB_TWEAK (1 << 0)
+#define ECC_FLAG_PUBKEY    (1 << 1)
 
 
 /***** Local prototypes  *****/
@@ -332,7 +323,7 @@ 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;
+    exmode = app->app_local->extcap.max_certlen_3;
   else
     exmode = 0;
 
@@ -462,10 +453,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)
         {
@@ -739,25 +727,12 @@ parse_login_data (app_t app)
 }
 
 
-static unsigned char
-get_algo_byte (int keynumber, key_type_t key_type)
-{
-  if (key_type == KEY_TYPE_ECC && keynumber != 1)
-    return 19;
-  else if (key_type == KEY_TYPE_ECC && keynumber == 1)
-    return 18;
-  else if (key_type == KEY_TYPE_EDDSA)
-    return 22;
-  else
-    return 1;  /* RSA */
-}
-
 #define MAX_ARGS_STORE_FPR 3
 
 /* Note, that FPR must be at least 20 bytes. */
 static gpg_error_t
 store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
-           key_type_t key_type, ...)
+           int algo, ...)
 {
   unsigned int n, nbits;
   unsigned char *buffer, *p;
@@ -770,21 +745,17 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
   int i;
 
   n = 6;    /* key packet version, 4-byte timestamps, and algorithm */
-  if (keynumber == 1 && key_type == KEY_TYPE_ECC)
+  if (algo == PUBKEY_ALGO_ECDH)
     argc = 3;
   else
     argc = 2;
 
-  va_start (ap, key_type);
+  va_start (ap, algo);
   for (i = 0; i < argc; i++)
     {
       m[i] = va_arg (ap, const unsigned char *);
       mlen[i] = va_arg (ap, size_t);
-      if (key_type != KEY_TYPE_EDDSA)
-        /* strip off leading zeroes */
-        for (; mlen[i] && !*m[i]; mlen[i]--, m[i]++)
-          ;
-      if (key_type == KEY_TYPE_RSA || i == 1)
+      if (algo == PUBKEY_ALGO_RSA || i == 1)
         n += 2;
       n += mlen[i];
     }
@@ -802,11 +773,11 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
   *p++ = timestamp >> 16;
   *p++ = timestamp >>  8;
   *p++ = timestamp;
-  *p++ = get_algo_byte (keynumber, key_type);
+  *p++ = algo;
 
   for (i = 0; i < argc; i++)
     {
-      if (key_type == KEY_TYPE_RSA || i == 1)
+      if (algo == PUBKEY_ALGO_RSA || i == 1)
         {
           nbits = count_bits (m[i], mlen[i]);
           *p++ = nbits >> 8;
@@ -918,76 +889,50 @@ send_key_data (ctrl_t ctrl, const char *name,
 
 
 static void
-get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid)
-{
-  if (curve == CURVE_NIST_P256)
-    {
-      *r_n_bits = 256;
-      *r_curve_oid = "1.2.840.10045.3.1.7";
-    }
-  else if (curve == CURVE_NIST_P384)
-    {
-      *r_n_bits = 384;
-      *r_curve_oid = "1.3.132.0.34";
-    }
-  else if (curve == CURVE_NIST_P521)
-    {
-      *r_n_bits = 521;
-      *r_curve_oid = "1.3.132.0.35";
-    }
-  else if (curve == CURVE_SEC_P256K1)
-    {
-      *r_n_bits = 256;
-      *r_curve_oid = "1.3.132.0.10";
-    }
-  else if (curve == CURVE_ED25519)
-    {
-      *r_n_bits = 255;
-      *r_curve_oid = "1.3.6.1.4.1.11591.15.1";
-    }
-  else
-    {
-      *r_n_bits = 0;
-      *r_curve_oid = "1.3.6.1.4.1.11591.2.12242973"; /* gnu.gnupg.badoid */
-    }
-}
-
-static void
-send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
+send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
 {
   char buffer[200];
-  int n_bits;
-  const char *curve_oid;
 
-  assert (number >=0 && number < DIM(app->app_local->keyattr));
+  assert (keyno >=0 && keyno < DIM(app->app_local->keyattr));
 
-  if (app->app_local->keyattr[number].key_type == KEY_TYPE_RSA)
-    snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
-              number+1,
-              app->app_local->keyattr[number].rsa.n_bits,
-              app->app_local->keyattr[number].rsa.e_bits,
-              app->app_local->keyattr[number].rsa.format);
-  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECC)
-    {
-      get_ecc_key_parameters (app->app_local->keyattr[number].ecc.curve,
-                              &n_bits, &curve_oid);
-      snprintf (buffer, sizeof buffer, "%d %d %u %s",
-                number+1, number==1? 18: 19, n_bits, curve_oid);
-    }
-  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
+  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+    snprintf (buffer, sizeof buffer, "%d 1 rsa%u %u %d",
+              keyno+1,
+              app->app_local->keyattr[keyno].rsa.n_bits,
+              app->app_local->keyattr[keyno].rsa.e_bits,
+              app->app_local->keyattr[keyno].rsa.format);
+  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
     {
-      get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
-                              &n_bits, &curve_oid);
-      snprintf (buffer, sizeof buffer, "%d 22 %u %s",
-                number+1, n_bits, curve_oid);
+      snprintf (buffer, sizeof buffer, "%d %d %s",
+                keyno+1,
+                keyno==1? PUBKEY_ALGO_ECDH :
+                (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+                PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA,
+                app->app_local->keyattr[keyno].ecc.curve);
     }
   else
-    snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
+    snprintf (buffer, sizeof buffer, "%d 0 0 UNKNOWN", keyno+1);
 
   send_status_direct (ctrl, keyword, buffer);
 }
 
 
+#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/8
+    + 2 /* tag+len */ + app->app_local->keyattr[keyno].rsa.e_bits/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
@@ -1033,21 +978,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;
@@ -1057,7 +994,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
       char tmp[110];
 
       snprintf (tmp, sizeof tmp,
-                "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d si=%u",
+                "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d "
+                "sm=%d si=%u dec=%d bt=%d",
                 app->app_local->extcap.get_challenge,
                 app->app_local->extcap.key_import,
                 app->app_local->extcap.change_force_chv,
@@ -1065,9 +1003,13 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
                 app->app_local->extcap.max_certlen_3,
                 app->app_local->extcap.algo_attr_change,
                 (app->app_local->extcap.sm_supported
-                 ? (app->app_local->extcap.sm_aes128? 7 : 2)
+                 ? (app->app_local->extcap.sm_algo == 0? CIPHER_ALGO_3DES :
+                    (app->app_local->extcap.sm_algo == 1?
+                     CIPHER_ALGO_AES : CIPHER_ALGO_AES256))
                  : 0),
-                app->app_local->status_indicator);
+                app->app_local->status_indicator,
+                app->app_local->extcap.has_decrypt,
+                app->app_local->extcap.has_button);
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
@@ -1079,10 +1021,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)
             {
@@ -1176,8 +1117,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. */
@@ -1207,10 +1148,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);
@@ -1234,7 +1175,7 @@ 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.  */
@@ -1282,29 +1223,279 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
 #endif /*GNUPG_MAJOR_VERSION > 1*/
 
 
-static const char *
-get_curve_name (int curve)
+static gpg_error_t
+rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at,  int keyno,
+                 const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+{
+  gpg_error_t err;
+  const unsigned char *m, *e;
+  size_t mlen, elen;
+  unsigned char *mbuf = NULL, *ebuf = NULL;
+
+  m = find_tlv (data, datalen, 0x0081, &mlen);
+  if (!m)
+    {
+      log_error (_("response does not contain the RSA modulus\n"));
+      return gpg_error (GPG_ERR_CARD);
+    }
+
+  e = find_tlv (data, datalen, 0x0082, &elen);
+  if (!e)
+    {
+      log_error (_("response does not contain the RSA public exponent\n"));
+      return gpg_error (GPG_ERR_CARD);
+    }
+
+  if (ctrl)
+    {
+      send_key_data (ctrl, "n", m, mlen);
+      send_key_data (ctrl, "e", e, elen);
+    }
+
+  for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+    ;
+  for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+    ;
+
+  if (ctrl)
+    {
+      unsigned char fprbuf[20];
+
+      err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+                       m, mlen, e, elen);
+      if (err)
+        return err;
+
+      send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+    }
+
+  mbuf = xtrymalloc (mlen + 1);
+  if (!mbuf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  /* Prepend numbers with a 0 if needed.  */
+  if (mlen && (*m & 0x80))
+    {
+      *mbuf = 0;
+      memcpy (mbuf+1, m, mlen);
+      mlen++;
+    }
+  else
+    memcpy (mbuf, m, mlen);
+
+  ebuf = xtrymalloc (elen + 1);
+  if (!ebuf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  /* Prepend numbers with a 0 if needed.  */
+  if (elen && (*e & 0x80))
+    {
+      *ebuf = 0;
+      memcpy (ebuf+1, e, elen);
+      elen++;
+    }
+  else
+    memcpy (ebuf, e, elen);
+
+  err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))",
+                         (int)mlen, mbuf, (int)elen, ebuf);
+ leave:
+  xfree (mbuf);
+  xfree (ebuf);
+  return err;
+}
+
+
+/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE.  */
+static const unsigned char*
+ecdh_params (const char *curve)
+{
+  unsigned int nbits;
+
+  openpgp_curve_to_oid (curve, &nbits);
+
+  /* See RFC-6637 for those constants.
+         0x03: Number of bytes
+         0x01: Version for this parameter format
+         KDF algo
+        KEK algo
+  */
+  if (nbits <= 256)
+    return (const unsigned char*)"\x03\x01\x08\x07";
+  else if (nbits <= 384)
+    return (const unsigned char*)"\x03\x01\x09\x08";
+  else
+    return (const unsigned char*)"\x03\x01\x0a\x09";
+}
+
+static gpg_error_t
+ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+                 const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+{
+  gpg_error_t err;
+  unsigned char *qbuf = NULL;
+  const unsigned char *ecc_q;
+  size_t ecc_q_len;
+  gcry_mpi_t oid = NULL;
+  int n;
+  const char *curve;
+  const char *oidstr;
+  const unsigned char *oidbuf;
+  size_t oid_len;
+  int algo;
+  const char *format;
+
+  ecc_q = find_tlv (data, datalen, 0x0086, &ecc_q_len);
+  if (!ecc_q)
+    {
+      log_error (_("response does not contain the EC public key\n"));
+      return gpg_error (GPG_ERR_CARD);
+    }
+
+  curve = app->app_local->keyattr[keyno].ecc.curve;
+  oidstr = openpgp_curve_to_oid (curve, NULL);
+  err = openpgp_oid_from_str (oidstr, &oid);
+  if (err)
+    return err;
+  oidbuf = gcry_mpi_get_opaque (oid, &n);
+  if (!oidbuf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  oid_len = (n+7)/8;
+
+  qbuf = xtrymalloc (ecc_q_len + 1);
+  if (!qbuf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+    {               /* Prepend 0x40 prefix.  */
+      *qbuf = 0x40;
+      memcpy (qbuf+1, ecc_q, ecc_q_len);
+      ecc_q_len++;
+    }
+  else
+    memcpy (qbuf, ecc_q, ecc_q_len);
+
+  if (ctrl)
+    {
+      send_key_data (ctrl, "q", qbuf, ecc_q_len);
+      send_key_data (ctrl, "curve", oidbuf, oid_len);
+    }
+
+  if (keyno == 1)
+    {
+      if (ctrl)
+        send_key_data (ctrl, "kdf/kek", ecdh_params (curve), (size_t)4);
+      algo = PUBKEY_ALGO_ECDH;
+    }
+  else
+    {
+      if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+        algo = PUBKEY_ALGO_EDDSA;
+      else
+        algo = PUBKEY_ALGO_ECDSA;
+    }
+
+  if (ctrl)
+    {
+      unsigned char fprbuf[20];
+
+      err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+                       qbuf, ecc_q_len, ecdh_params (curve), (size_t)4);
+      if (err)
+        goto leave;
+
+      send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+    }
+
+  if (!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+    format = "(public-key(ecc(curve%s)(q%b)))";
+  else if (keyno == 1)
+    format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))";
+  else
+    format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
+
+  err = gcry_sexp_build (r_sexp, NULL, format,
+                         app->app_local->keyattr[keyno].ecc.curve,
+                         (int)ecc_q_len, qbuf);
+ leave:
+  gcry_mpi_release (oid);
+  xfree (qbuf);
+  return err;
+}
+
+
+/* Parse tag-length-value data for public key in BUFFER of BUFLEN
+   length.  Key of KEYNO in APP is updated with an S-expression of
+   public key.  When CTRL is not NULL, fingerprint is computed with
+   CREATED_AT, and fingerprint is written to the card, and key data
+   and fingerprint are send back to the client side.
+ */
+static gpg_error_t
+read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+                 const unsigned char *buffer, size_t buflen)
 {
-  if (curve == CURVE_NIST_P256)
-    return "NIST P-256";
-  else if (curve == CURVE_NIST_P384)
-    return "NIST P-384";
-  else if (curve == CURVE_NIST_P521)
-    return "NIST P-521";
-  else if (curve == CURVE_SEC_P256K1)
-    return "secp256k1";
-  else if (curve == CURVE_ED25519)
-    return "Ed25519";
+  gpg_error_t err;
+  const unsigned char *data;
+  size_t datalen;
+  gcry_sexp_t s_pkey = NULL;
+
+  data = find_tlv (buffer, buflen, 0x7F49, &datalen);
+  if (!data)
+    {
+      log_error (_("response does not contain the public key data\n"));
+      return gpg_error (GPG_ERR_CARD);
+    }
+
+  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+    err = rsa_read_pubkey (app, ctrl, created_at, keyno,
+                           data, datalen, &s_pkey);
+  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+    err = ecc_read_pubkey (app, ctrl, created_at, keyno,
+                           data, datalen, &s_pkey);
   else
-    return "unknown";
+    err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  if (!err)
+    {
+      unsigned char *keybuf;
+      size_t len;
+
+      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+      keybuf = xtrymalloc (len);
+      if (!data)
+        {
+          err = gpg_error_from_syserror ();
+          gcry_sexp_release (s_pkey);
+          return err;
+        }
+
+      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+      gcry_sexp_release (s_pkey);
+
+      app->app_local->pk[keyno].key = keybuf;
+      /* Decrement for trailing '\0' */
+      app->app_local->pk[keyno].keylen = len - 1;
+    }
+
+  return err;
 }
 
 
 /* Get the public key for KEYNO and store it as an S-expresion 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 soley indicated by the
-   presence of the app->app_local->pk[KEYNO-1].key field.
+   not mean a key is available; this is solely indicated by the
+   presence of the app->app_local->pk[KEYNO].key field.
 
    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
@@ -1316,19 +1507,16 @@ get_public_key (app_t app, int keyno)
 {
   gpg_error_t err = 0;
   unsigned char *buffer;
-  const unsigned char *keydata, *m, *e;
-  size_t buflen, keydatalen;
+  const unsigned char *m, *e;
+  size_t buflen;
   size_t mlen = 0;
   size_t elen = 0;
-  unsigned char *mbuf = NULL;
-  unsigned char *ebuf = NULL;
   char *keybuf = NULL;
   gcry_sexp_t s_pkey;
   size_t len;
 
-  if (keyno < 1 || keyno > 3)
+  if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
-  keyno--;
 
   /* Already cached? */
   if (app->app_local->pk[keyno].read_done)
@@ -1345,10 +1533,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
         {
@@ -1356,54 +1546,17 @@ get_public_key (app_t app, int keyno)
           le_value = 256; /* Use legacy value. */
         }
 
-      err = iso7816_read_public_key
-        (app->slot, exmode,
-         (const unsigned char*)(keyno == 0? "\xB6" :
-                                keyno == 1? "\xB8" : "\xA4"), 2,
-         le_value,
-         &buffer, &buflen);
+      err = iso7816_read_public_key (app->slot, exmode,
+                                     (keyno == 0? "\xB6" :
+                                      keyno == 1? "\xB8" : "\xA4"),
+                                     2, le_value, &buffer, &buflen);
       if (err)
         {
           log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
           goto leave;
         }
 
-      keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
-      if (!keydata)
-        {
-          err = gpg_error (GPG_ERR_CARD);
-          log_error (_("response does not contain the public key data\n"));
-          goto leave;
-        }
-
-      if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
-        {
-          m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
-          if (!m)
-            {
-              err = gpg_error (GPG_ERR_CARD);
-              log_error (_("response does not contain the RSA modulus\n"));
-              goto leave;
-            }
-
-          e = find_tlv (keydata, keydatalen, 0x0082, &elen);
-          if (!e)
-            {
-              err = gpg_error (GPG_ERR_CARD);
-              log_error (_("response does not contain the RSA public exponent\n"));
-              goto leave;
-            }
-        }
-      else
-        {
-          m = find_tlv (keydata, keydatalen, 0x0086, &mlen);
-          if (!m)
-            {
-              err = gpg_error (GPG_ERR_CARD);
-              log_error (_("response does not contain the EC public point\n"));
-              goto leave;
-            }
-        }
+      err = read_public_key (app, NULL, 0U, keyno, buffer, buflen);
     }
   else
     {
@@ -1412,8 +1565,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;
@@ -1425,123 +1578,41 @@ 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 = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
-      pclose (fp);
-      if (err)
-       {
-         log_error ("error while retrieving key material through pipe: %s\n",
-                     gpg_strerror (err));
-         goto leave;
-       }
-    }
-
-
-  mbuf = xtrymalloc ( mlen + 1);
-  if (!mbuf)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-  /* Prepend numbers with a 0 if needed.  */
-  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_EDDSA
-      && mlen && (*m & 0x80))
-    {
-      *mbuf = 0;
-      memcpy (mbuf+1, m, mlen);
-      mlen++;
-    }
-  else
-    memcpy (mbuf, m, mlen);
-
-  ebuf = xtrymalloc ( elen + 1);
-  if (!ebuf)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-  /* Prepend numbers with a 0 if needed.  */
-  if (elen && (*e & 0x80))
-    {
-      *ebuf = 0;
-      memcpy (ebuf+1, e, elen);
-      elen++;
-    }
-  else
-    memcpy (ebuf, e, elen);
-
-  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
-    {
-      err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
-                             (int)mlen, mbuf, (int)elen, ebuf);
-      if (err)
-        goto leave;
-
-      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
-      keybuf = xtrymalloc (len);
-      if (!keybuf)
         {
-          gcry_sexp_release (s_pkey);
           err = gpg_error_from_syserror ();
+          log_error ("running gpg failed: %s\n", gpg_strerror (err));
           goto leave;
         }
-      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
-      gcry_sexp_release (s_pkey);
-    }
-  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
-    {
-      const char *curve_name
-        = get_curve_name (app->app_local->keyattr[keyno].ecc.curve);
 
-      err = gcry_sexp_build (&s_pkey, NULL,
-                             "(public-key(ecc(curve%s)(q%b)))",
-                             curve_name, (int)mlen, mbuf);
+      err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
+      pclose (fp);
       if (err)
-        goto leave;
-
-      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
-
-      keybuf = xtrymalloc (len);
-      if (!keybuf)
         {
-          gcry_sexp_release (s_pkey);
-          err = gpg_error_from_syserror ();
+          log_error ("error while retrieving key material through pipe: %s\n",
+                     gpg_strerror (err));
           goto leave;
         }
-      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
-      gcry_sexp_release (s_pkey);
-    }
-  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_EDDSA)
-    {
-      const char *curve_name
-        = get_curve_name (app->app_local->keyattr[keyno].eddsa.curve);
 
-      err = gcry_sexp_build (&s_pkey, NULL,
-                             "(public-key(ecc(curve%s)(flags eddsa)(q%b)))",
-                             curve_name, (int)mlen, mbuf);
+      err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
+                             (int)mlen, m, (int)elen, e);
       if (err)
         goto leave;
 
@@ -1550,40 +1621,36 @@ get_public_key (app_t app, int keyno)
       keybuf = xtrymalloc (len);
       if (!keybuf)
         {
-          gcry_sexp_release (s_pkey);
           err = gpg_error_from_syserror ();
+          gcry_sexp_release (s_pkey);
           goto leave;
         }
+
       gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
       gcry_sexp_release (s_pkey);
-    }
-  else
-    {
-      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-      goto leave;
-    }
 
-  app->app_local->pk[keyno].key = (unsigned char*)keybuf;
-  app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */
+      app->app_local->pk[keyno].key = (unsigned char*)keybuf;
+      /* Decrement for trailing '\0' */
+      app->app_local->pk[keyno].keylen = len - 1;
+    }
 
  leave:
   /* Set a flag to indicate that we tried to read the key.  */
   app->app_local->pk[keyno].read_done = 1;
 
   xfree (buffer);
-  xfree (mbuf);
-  xfree (ebuf);
-  return 0;
+  return err;
 }
 #endif /* GNUPG_MAJOR_VERSION > 1 */
 
 
 
-/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3].
+/* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3].
    This is used by the LEARN command. */
 static gpg_error_t
-send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
+send_keypair_info (app_t app, ctrl_t ctrl, int key)
 {
+  int keyno = key - 1;
   gpg_error_t err = 0;
   /* 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. */
@@ -1596,19 +1663,19 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
   if (err)
     goto leave;
 
-  assert (keyno >= 1 && keyno <= 3);
-  if (!app->app_local->pk[keyno-1].key)
+  assert (keyno >= 0 && keyno <= 2);
+  if (!app->app_local->pk[keyno].key)
     goto leave; /* No such key - ignore. */
 
-  err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key,
-                                 app->app_local->pk[keyno-1].keylen,
+  err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key,
+                                 app->app_local->pk[keyno].keylen,
                                  grip);
   if (err)
     goto leave;
 
   bin2hex (grip, 20, gripstr);
 
-  sprintf (idbuf, "OPENPGP.%d", keyno);
+  sprintf (idbuf, "OPENPGP.%d", keyno+1);
   send_status_info (ctrl, "KEYPAIRINFO",
                     gripstr, 40,
                     idbuf, strlen (idbuf),
@@ -1652,7 +1719,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
   send_keypair_info (app, ctrl, 2);
   send_keypair_info (app, ctrl, 3);
   /* Note: We do not send the Cardholder Certificate, because that is
-     relativly long and for OpenPGP applications not really needed.  */
+     relatively long and for OpenPGP applications not really needed.  */
   return 0;
 }
 
@@ -1663,7 +1730,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;
@@ -1671,11 +1739,11 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   unsigned char *buf;
 
   if (!strcmp (keyid, "OPENPGP.1"))
-    keyno = 1;
+    keyno = 0;
   else if (!strcmp (keyid, "OPENPGP.2"))
-    keyno = 2;
+    keyno = 1;
   else if (!strcmp (keyid, "OPENPGP.3"))
-    keyno = 3;
+    keyno = 2;
   else
     return gpg_error (GPG_ERR_INV_ID);
 
@@ -1683,18 +1751,45 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   if (err)
     return err;
 
-  buf = app->app_local->pk[keyno-1].key;
+  buf = app->app_local->pk[keyno].key;
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
-  *pklen = app->app_local->pk[keyno-1].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);
@@ -1779,7 +1874,7 @@ check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
 }
 
 
-/* Verify a CHV either using using the pinentry or if possibile by
+/* Verify a CHV either using 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
@@ -1837,7 +1932,7 @@ verify_a_chv (app_t app,
       prompt_buffer = xtrymalloc (promptsize);
       if (!prompt_buffer)
         return gpg_error_from_syserror ();
-      snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
+      snprintf (prompt_buffer, promptsize, PROMPTSTRING, sigcount);
       prompt = prompt_buffer;
 #undef PROMPTSTRING
     }
@@ -1978,8 +2073,11 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
   remaining = value[6];
   xfree (relptr);
 
-  log_info(_("%d Admin PIN attempts remaining before card"
-             " is permanently locked\n"), remaining);
+  log_info (ngettext("%d Admin PIN attempt remaining before card"
+                     " is permanently locked\n",
+                     "%d Admin PIN attempts remaining before card"
+                     " is permanently locked\n",
+                     remaining), remaining);
 
   if (remaining < 3)
     {
@@ -2119,6 +2217,7 @@ do_setattr (app_t app, const char *name,
     { "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 },
     { NULL, 0 }
   };
   int exmode;
@@ -2299,7 +2398,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;
@@ -2733,9 +2832,10 @@ build_privkey_template (app_t app, int keyno,
 static gpg_error_t
 build_ecc_privkey_template (app_t app, int keyno,
                             const unsigned char *ecc_d, size_t ecc_d_len,
+                            const unsigned char *ecc_q, size_t ecc_q_len,
                             unsigned char **result, size_t *resultlen)
 {
-  unsigned char privkey[2];
+  unsigned char privkey[2+2];
   size_t privkey_len;
   unsigned char exthdr[2+2+1];
   size_t exthdr_len;
@@ -2745,8 +2845,10 @@ build_ecc_privkey_template (app_t app, int keyno,
   size_t datalen;
   unsigned char *template;
   size_t template_size;
+  int pubkey_required;
 
-  (void)app;
+  pubkey_required = !!(app->app_local->keyattr[keyno].ecc.flags
+                       & ECC_FLAG_PUBKEY);
 
   *result = NULL;
   *resultlen = 0;
@@ -2755,11 +2857,18 @@ build_ecc_privkey_template (app_t app, int keyno,
   datalen = 0;
   tp = privkey;
 
-  tp += add_tlv (tp, 0x91, ecc_d_len); /* Tag 0x91??? */
+  tp += add_tlv (tp, 0x92, ecc_d_len);
   datalen += ecc_d_len;
 
+  if (pubkey_required)
+    {
+      tp += add_tlv (tp, 0x99, ecc_q_len);
+      datalen += ecc_q_len;
+    }
+
   privkey_len = tp - privkey;
 
+
   /* Build the extended header list without the private key template.  */
   tp = exthdr;
   *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
@@ -2778,6 +2887,8 @@ build_ecc_privkey_template (app_t app, int keyno,
                    + privkey_len
                    + suffix_len
                    + datalen);
+  if (exthdr_len + privkey_len + suffix_len + datalen >= 128)
+    template_size++;
   tp = template = xtrymalloc_secure (template_size);
   if (!template)
     return gpg_error_from_syserror ();
@@ -2793,6 +2904,12 @@ build_ecc_privkey_template (app_t app, int keyno,
   memcpy (tp, ecc_d, ecc_d_len);
   tp += ecc_d_len;
 
+  if (pubkey_required)
+    {
+      memcpy (tp, ecc_q, ecc_q_len);
+      tp += ecc_q_len;
+    }
+
   assert (tp - template == template_size);
 
   *result = template;
@@ -2804,54 +2921,25 @@ build_ecc_privkey_template (app_t app, int keyno,
 /* Helper for do_writekley to change the size of a key.  Not ethat
    this deletes the entire key without asking.  */
 static gpg_error_t
-change_keyattr (app_t app, int keyno, unsigned int nbits,
+change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
                 gpg_error_t (*pincb)(void*, const char *, char **),
                 void *pincb_arg)
 {
   gpg_error_t err;
-  unsigned char *buffer;
-  size_t buflen;
-  void *relptr;
 
   assert (keyno >=0 && keyno <= 2);
 
-  if (nbits > 4096)
-    return gpg_error (GPG_ERR_TOO_LARGE);
-
-  /* Read the current attributes into a buffer.  */
-  relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
-  if (!relptr)
-    return gpg_error (GPG_ERR_CARD);
-  if (buflen < 6 || buffer[0] != 1)
-    {
-      /* Attriutes too short or not an RSA key.  */
-      xfree (relptr);
-      return gpg_error (GPG_ERR_CARD);
-    }
-
-  /* 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;
-  buffer[1] = (nbits >> 8);
-  buffer[2] = nbits;
-
   /* Prepare for storing the key.  */
   err = verify_chv3 (app, pincb, pincb_arg);
   if (err)
-    {
-      xfree (relptr);
-      return err;
-    }
+    return err;
 
   /* Change the attribute.  */
-  err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buffer, buflen);
-  xfree (relptr);
+  err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen);
   if (err)
-    log_error ("error changing size of key %d to %u bits\n", keyno+1, nbits);
+    log_error ("error changing key attribute (key=%d)\n", keyno+1);
   else
-    log_info ("size of key %d changed to %u bits\n", keyno+1, nbits);
+    log_info ("key attribute changed (key=%d)\n", keyno+1);
   flush_cache (app);
   parse_algorithm_attribute (app, keyno);
   app->did_chv1 = 0;
@@ -2861,18 +2949,58 @@ change_keyattr (app_t app, int keyno, unsigned int nbits,
 }
 
 
-/* Helper to process an setattr command for name KEY-ATTR.  It expects
-   a string "--force <keyno> <algo> <nbits>" in (VALUE,VALUELEN).  */
+static gpg_error_t
+change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
+                    gpg_error_t (*pincb)(void*, const char *, char **),
+                    void *pincb_arg)
+{
+  gpg_error_t err = 0;
+  unsigned char *buf;
+  size_t buflen;
+  void *relptr;
+
+  /* Read the current attributes into a buffer.  */
+  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)
+    {
+      /* Attriutes too short or not an RSA key.  */
+      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.  */
+      nbits = ((nbits + 31) / 32) * 32;
+      buf[1] = (nbits >> 8);
+      buf[2] = nbits;
+      err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg);
+      xfree (relptr);
+    }
+
+  return err;
+}
+
+
+/* Helper to process an setattr command for name KEY-ATTR.
+   In (VALUE,VALUELEN), it expects following string:
+        RSA: "--force <key> <algo> rsa<nbits>"
+        ECC: "--force <key> <algo> <curvename>"
+  */
 static gpg_error_t
 change_keyattr_from_string (app_t app,
                             gpg_error_t (*pincb)(void*, const char *, char **),
                             void *pincb_arg,
                             const void *value, size_t valuelen)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   char *string;
-  int keyno, algo;
-  unsigned int nbits;
+  int key, keyno, algo;
+  int n = 0;
 
   /* VALUE is expected to be a string but not guaranteed to be
      terminated.  Thus copy it to an allocated buffer first. */
@@ -2885,17 +3013,63 @@ change_keyattr_from_string (app_t app,
   /* Because this function deletes the key we require the string
      "--force" in the data to make clear that something serious might
      happen.  */
-  if (sscanf (string, " --force %d %d %u", &keyno, &algo, &nbits) != 3)
-    err = gpg_error (GPG_ERR_INV_DATA);
-  else if (keyno < 1 || keyno > 3)
+  sscanf (string, "--force %d %d %n", &key, &algo, &n);
+  if (n < 12)
+    {
+      err = gpg_error (GPG_ERR_INV_DATA);
+      goto leave;
+    }
+
+  keyno = key - 1;
+  if (keyno < 0 || keyno > 2)
     err = gpg_error (GPG_ERR_INV_ID);
-  else if (algo != 1)
-    err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Not RSA.  */
-  else if (nbits < 1024)
-    err = gpg_error (GPG_ERR_TOO_SHORT);
+  else if (algo == PUBKEY_ALGO_RSA)
+    {
+      unsigned int nbits;
+
+      errno = 0;
+      nbits = strtoul (string+n+3, NULL, 10);
+      if (errno)
+        err = gpg_error (GPG_ERR_INV_DATA);
+      else if (nbits < 1024)
+        err = gpg_error (GPG_ERR_TOO_SHORT);
+      else if (nbits > 4096)
+        err = gpg_error (GPG_ERR_TOO_LARGE);
+      else
+        err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
+    }
+  else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
+           || algo == PUBKEY_ALGO_EDDSA)
+    {
+      const char *oidstr;
+      gcry_mpi_t oid;
+      const unsigned char *oidbuf;
+      size_t oid_len;
+
+      oidstr = openpgp_curve_to_oid (string+n, NULL);
+      if (!oidstr)
+        {
+          err = gpg_error (GPG_ERR_INV_DATA);
+          goto leave;
+        }
+
+      err = openpgp_oid_from_str (oidstr, &oid);
+      if (err)
+        goto leave;
+
+      oidbuf = gcry_mpi_get_opaque (oid, &n);
+      oid_len = (n+7)/8;
+
+      /* We have enough room at STRING.  */
+      string[0] = algo;
+      memcpy (string+1, oidbuf+1, oid_len-1);
+      err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
+      gcry_mpi_release (oid);
+    }
   else
-    err = change_keyattr (app, keyno-1, nbits, pincb, pincb_arg);
+    err = gpg_error (GPG_ERR_PUBKEY_ALGO);
 
+ leave:
   xfree (string);
   return err;
 }
@@ -2923,6 +3097,13 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   unsigned char fprbuf[20];
   u32 created_at = 0;
 
+  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_RSA)
+    {
+      log_error (_("unsupported algorithm: %s"), "RSA");
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+
   last_depth1 = depth;
   while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
          && depth && depth >= last_depth1)
@@ -3022,7 +3203,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       && app->app_local->extcap.algo_attr_change)
     {
       /* Try to switch the key to a new length.  */
-      err = change_keyattr (app, keyno, nbits, pincb, pincb_arg);
+      err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
       if (!err)
         maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
     }
@@ -3195,7 +3376,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       goto leave;
     }
 
-  err = store_fpr (app, keyno, created_at, fprbuf, KEY_TYPE_RSA,
+  err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
                    rsa_n, rsa_n_len, rsa_e, rsa_e_len);
   if (err)
     goto leave;
@@ -3219,11 +3400,16 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   const unsigned char *ecc_q = NULL;
   const unsigned char *ecc_d = NULL;
   size_t ecc_q_len, ecc_d_len;
-  unsigned char *template = NULL;
-  size_t template_len;
-  unsigned char fprbuf[20];
+  const char *curve = NULL;
   u32 created_at = 0;
-  int curve = CURVE_UNKNOWN;
+  const char *oidstr;
+  int flag_djb_tweak = 0;
+  int algo;
+  gcry_mpi_t oid = NULL;
+  const unsigned char *oidbuf;
+  unsigned int n;
+  size_t oid_len;
+  unsigned char fprbuf[20];
 
   /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
      curve = "NIST P-256" */
@@ -3245,31 +3431,45 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
       if (tok && toklen == 5 && !memcmp (tok, "curve", 5))
         {
+          char *curve_name;
+
           if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
             goto leave;
 
-          if (tok && toklen == 10 && !memcmp (tok, "NIST P-256", 10))
-            curve = CURVE_NIST_P256;
-          else if (tok && toklen == 9 && !memcmp (tok, "secp256k1", 9))
-            curve = CURVE_SEC_P256K1;
-          else if (tok && toklen == 7 && !memcmp (tok, "Ed25519", 7))
-            curve = CURVE_ED25519;
-          else
+          curve_name = xtrymalloc (toklen+1);
+          if (!curve_name)
             {
-              log_error (_("unsupported curve\n"));
-              err = gpg_error (GPG_ERR_INV_VALUE);
+              err = gpg_error_from_syserror ();
               goto leave;
             }
+
+          memcpy (curve_name, tok, toklen);
+          curve_name[toklen] = 0;
+          curve = openpgp_is_curve_supported (curve_name, NULL, NULL);
+          xfree (curve_name);
+        }
+      else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
+        {
+          if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+            goto leave;
+
+          if (tok)
+            {
+              if ((toklen == 5 && !memcmp (tok, "eddsa", 5))
+                  || (toklen == 9 && !memcmp (tok, "djb-tweak", 9)))
+                flag_djb_tweak = 1;
+            }
         }
       else if (tok && toklen == 1)
         {
           const unsigned char **buf2;
           size_t *buf2len;
+          int native = flag_djb_tweak;
 
           switch (*tok)
             {
             case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
-            case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; break;
+            case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; native = 0; break;
             default: buf2 = NULL;  buf2len = NULL; break;
             }
           if (buf2 && *buf2)
@@ -3279,13 +3479,16 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
             }
           if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
             goto leave;
-          if (tok && buf2 && curve != CURVE_ED25519)
-            /* It's MPI.  Strip off leading zero bytes and save. */
-            for (;toklen && !*tok; toklen--, tok++)
-              ;
+          if (tok && buf2)
+            {
+              if (!native)
+                /* Strip off leading zero bytes and save. */
+                for (;toklen && !*tok; toklen--, tok++)
+                  ;
 
-          *buf2 = tok;
-          *buf2len = toklen;
+              *buf2 = tok;
+              *buf2len = toklen;
+            }
         }
       /* Skip until end of list. */
       last_depth2 = depth;
@@ -3330,12 +3533,71 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
   /* Check that we have all parameters and that they match the card
      description. */
+  if (!curve)
+    {
+      log_error (_("unsupported curve\n"));
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
   if (!created_at)
     {
       log_error (_("creation timestamp missing\n"));
       err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
+  if (flag_djb_tweak && keyno != 1)
+    algo = PUBKEY_ALGO_EDDSA;
+  else if (keyno == 1)
+    algo = PUBKEY_ALGO_ECDH;
+  else
+    algo = PUBKEY_ALGO_ECDSA;
+
+  oidstr = openpgp_curve_to_oid (curve, NULL);
+  err = openpgp_oid_from_str (oidstr, &oid);
+  if (err)
+    goto leave;
+  oidbuf = gcry_mpi_get_opaque (oid, &n);
+  if (!oidbuf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  oid_len = (n+7)/8;
+
+  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
+      || app->app_local->keyattr[keyno].ecc.curve != curve
+      || (flag_djb_tweak !=
+          (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
+    {
+      if (app->app_local->extcap.algo_attr_change)
+        {
+          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;
+        }
+      else
+        {
+          log_error ("key attribute on card doesn't match\n");
+          err = gpg_error (GPG_ERR_INV_VALUE);
+          goto leave;
+        }
+    }
 
   if (opt.verbose)
     log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len);
@@ -3350,10 +3612,13 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
     {
       /* Build the private key template as described in section 4.3.3.7 of
          the OpenPGP card specs version 2.0.  */
+      unsigned char *template;
+      size_t template_len;
       int exmode;
 
       err = build_ecc_privkey_template (app, keyno,
                                         ecc_d, ecc_d_len,
+                                        ecc_q, ecc_q_len,
                                         &template, &template_len);
       if (err)
         goto leave;
@@ -3361,7 +3626,10 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       /* Prepare for storing the key.  */
       err = verify_chv3 (app, pincb, pincb_arg);
       if (err)
-        goto leave;
+        {
+          xfree (template);
+          goto leave;
+        }
 
       /* Store the key. */
       if (app->app_local->cardcap.ext_lc_le && template_len > 254)
@@ -3372,9 +3640,10 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
         exmode = 0;
       err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
                                   template, template_len);
+      xfree (template);
     }
   else
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    err = gpg_error (GPG_ERR_NOT_SUPPORTED);
 
   if (err)
     {
@@ -3382,22 +3651,11 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       goto leave;
     }
 
-  err = store_fpr (app, keyno, created_at, fprbuf,
-                   curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECC,
-                   curve == CURVE_ED25519 ?
-                   "\x09\x2b\x06\x01\x04\x01\xda\x47\x0f\x01"
-                   : curve == CURVE_NIST_P256 ?
-                   "\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"
-                   : "\x05\x2b\x81\x04\x00\x0a",
-                   (size_t)(curve == CURVE_ED25519 ? 10
-                            : curve == CURVE_NIST_P256? 9 : 6),
-                   ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
-  if (err)
-    goto leave;
-
+  err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+                   ecc_q, ecc_q_len, ecdh_params (curve), (size_t)4);
 
  leave:
-  xfree (template);
+  gcry_mpi_release (oid);
   return err;
 }
 
@@ -3465,10 +3723,7 @@ do_writekey (app_t app, ctrl_t ctrl,
     goto leave;
   if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0)
     err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
-  else if (tok
-           && ((toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
-               || (toklen == 4 && memcmp ("ecdh", tok, toklen) == 0)
-               || (toklen == 5 && memcmp ("ecdsa", tok, toklen) == 0)))
+  else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
     err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
   else
     {
@@ -3489,23 +3744,20 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
            gpg_error_t (*pincb)(void*, const char *, char **),
            void *pincb_arg)
 {
-  int rc;
+  gpg_error_t err;
   char numbuf[30];
-  unsigned char fprbuf[20];
-  const unsigned char *keydata, *m, *e;
   unsigned char *buffer = NULL;
-  size_t buflen, keydatalen, mlen, elen;
-  time_t created_at;
-  int keyno = atoi (keynostr);
+  const unsigned char *keydata;
+  size_t buflen, keydatalen;
+  u32 created_at;
+  int keyno = atoi (keynostr) - 1;
   int force = (flags & 1);
   time_t start_at;
-  int exmode;
-  int le_value;
-  unsigned int keybits;
+  int exmode = 0;
+  int le_value = 256; /* Use legacy value. */
 
-  if (keyno < 1 || keyno > 3)
+  if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
-  keyno--;
 
   /* We flush the cache to increase the traffic before a key
      generation.  This _might_ help a card to gather more entropy. */
@@ -3518,101 +3770,74 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   app->app_local->pk[keyno].read_done = 0;
 
   /* Check whether a key already exists.  */
-  rc = does_key_exist (app, keyno, 1, force);
-  if (rc)
-    return rc;
+  err = does_key_exist (app, keyno, 1, force);
+  if (err)
+    return err;
 
-  /* Because we send the key parameter back via status lines we need
-     to put a limit on the max. allowed keysize.  2048 bit will
-     already lead to a 527 byte long status line and thus a 4096 bit
-     key would exceed the Assuan line length limit.  */
-  keybits = app->app_local->keyattr[keyno].rsa.n_bits;
-  if (keybits > 4096)
-    return gpg_error (GPG_ERR_TOO_LARGE);
+  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+    {
+      unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
 
-  /* Prepare for key generation by verifying the Admin PIN.  */
-  rc = verify_chv3 (app, pincb, pincb_arg);
-  if (rc)
-    goto leave;
+      /* Because we send the key parameter back via status lines we need
+         to put a limit on the max. allowed keysize.  2048 bit will
+         already lead to a 527 byte long status line and thus a 4096 bit
+         key would exceed the Assuan line length limit.  */
+      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)
-    {
-      exmode = 1;    /* Use extended length w/o a limit.  */
-      le_value = app->app_local->extcap.max_rsp_data;
-      /* 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.  */
-    }
-  else
-    {
-      exmode = 0;
-      le_value = 256; /* Use legacy value. */
+      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 = 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.  */
+        }
     }
 
+  /* Prepare for key generation by verifying the Admin PIN.  */
+  err = verify_chv3 (app, pincb, pincb_arg);
+  if (err)
+    return err;
+
+
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
-  rc = iso7816_generate_keypair
-/* # warning key generation temporary replaced by reading an existing key. */
-/*   rc = iso7816_read_public_key */
-    (app->slot, exmode,
-     (const unsigned char*)(keyno == 0? "\xB6" :
-                            keyno == 1? "\xB8" : "\xA4"), 2,
-     le_value,
-     &buffer, &buflen);
-  if (rc)
+  err = iso7816_generate_keypair (app->slot, exmode,
+                                  (keyno == 0? "\xB6" :
+                                   keyno == 1? "\xB8" : "\xA4"),
+                                  2, le_value, &buffer, &buflen);
+  if (err)
     {
-      rc = gpg_error (GPG_ERR_CARD);
       log_error (_("generating key failed\n"));
-      goto leave;
+      return gpg_error (GPG_ERR_CARD);
     }
-  log_info (_("key generation completed (%d seconds)\n"),
-            (int)(time (NULL) - start_at));
+
+  {
+    int nsecs = (int)(time (NULL) - start_at);
+    log_info (ngettext("key generation completed (%d second)\n",
+                       "key generation completed (%d seconds)\n",
+                       nsecs), nsecs);
+  }
 
   keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
-      rc = gpg_error (GPG_ERR_CARD);
+      err = gpg_error (GPG_ERR_CARD);
       log_error (_("response does not contain the public key data\n"));
       goto leave;
     }
 
-  m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
-  if (!m)
-    {
-      rc = gpg_error (GPG_ERR_CARD);
-      log_error (_("response does not contain the RSA modulus\n"));
-      goto leave;
-    }
-  /* log_printhex ("RSA n:", m, mlen); */
-  send_key_data (ctrl, "n", m, mlen);
-
-  e = find_tlv (keydata, keydatalen, 0x0082, &elen);
-  if (!e)
-    {
-      rc = gpg_error (GPG_ERR_CARD);
-      log_error (_("response does not contain the RSA public exponent\n"));
-      goto leave;
-    }
-  /* log_printhex ("RSA e:", e, elen); */
-  send_key_data (ctrl, "e", e, elen);
-
-  created_at = createtime? createtime : gnupg_get_time ();
-  sprintf (numbuf, "%lu", (unsigned long)created_at);
+  created_at = (u32)(createtime? createtime : gnupg_get_time ());
+  sprintf (numbuf, "%u", created_at);
   send_status_info (ctrl, "KEY-CREATED-AT",
                     numbuf, (size_t)strlen(numbuf), NULL, 0);
 
-  rc = store_fpr (app, keyno, (u32)created_at, fprbuf, KEY_TYPE_RSA,
-                  m, mlen, e, elen);
-  if (rc)
-    goto leave;
-  send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
-
-
+  err = read_public_key (app, ctrl, created_at, keyno, buffer, buflen);
  leave:
   xfree (buffer);
-  return rc;
+  return err;
 }
 
 
@@ -3655,7 +3880,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
   size_t buflen, n;
   int rc, i;
 
-  assert (keyno >= 1 && keyno <= 3);
+  assert (keyno >= 0 && keyno <= 2);
 
   rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
   if (rc)
@@ -3670,7 +3895,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
       log_error (_("error reading fingerprint DO\n"));
       return gpg_error (GPG_ERR_GENERAL);
     }
-  fpr += (keyno-1)*20;
+  fpr += keyno*20;
   for (i=0; i < 20; i++)
     if (sha1fpr[i] != fpr[i])
       {
@@ -3689,7 +3914,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
    gpg has not been updated.  If there is no fingerprint we assume
    that this is okay. */
 static gpg_error_t
-check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
+check_against_given_fingerprint (app_t app, const char *fpr, int key)
 {
   unsigned char tmp[20];
   const char *s;
@@ -3706,7 +3931,7 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
 
   for (s=fpr, n=0; n < 20; s += 2, n++)
         tmp[n] = xtoi_2 (s);
-  return compare_fingerprint (app, keyno, tmp);
+  return compare_fingerprint (app, key-1, tmp);
 }
 
 
@@ -3808,23 +4033,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
@@ -3912,10 +4137,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
     {
@@ -3956,18 +4183,21 @@ do_auth (app_t app, const char *keyidstr,
       && indatalen > 101) /* For a 2048 bit key. */
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC
-      && (indatalen == 51 || indatalen == 67 || indatalen == 83))
-    {
-      const char *p = (const char *)indata + 19;
-      indata = p;
-      indatalen -= 19;
-    }
-  else if (app->app_local->keyattr[2].key_type == KEY_TYPE_EDDSA)
+  if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
     {
-      const char *p = (const char *)indata + 15;
-      indata = p;
-      indatalen -= 15;
+      if (!(app->app_local->keyattr[2].ecc.flags & ECC_FLAG_DJB_TWEAK)
+          && (indatalen == 51 || indatalen == 67 || indatalen == 83))
+        {
+          const char *p = (const char *)indata + 19;
+          indata = p;
+          indatalen -= 19;
+        }
+      else
+        {
+          const char *p = (const char *)indata + 15;
+          indata = p;
+          indatalen -= 15;
+        }
     }
 
   /* Check whether an OpenPGP card of any version has been requested. */
@@ -4012,10 +4242,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
         {
@@ -4046,6 +4278,7 @@ do_decipher (app_t app, const char *keyidstr,
   int exmode, le_value;
   unsigned char *fixbuf = NULL;
   int padind = 0;
+  int fixuplen = 0;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -4058,23 +4291,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
@@ -4090,10 +4323,14 @@ do_decipher (app_t app, const char *keyidstr,
   if (rc)
     return rc;
 
-  if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
+  if ((indatalen == 16 + 1 || indatalen == 32 + 1)
+      && ((char *)indata)[0] == 0x02)
+    {
+      /* PSO:DECIPHER with symmetric key.  */
+      padind = -1;
+    }
+  else if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
     {
-      int fixuplen;
-
       /* We might encounter a couple of leading zeroes in the
          cryptogram.  Due to internal use of MPIs these leading zeroes
          are stripped.  However the OpenPGP card expects exactly 128
@@ -4146,14 +4383,66 @@ do_decipher (app_t app, const char *keyidstr,
         }
     }
   else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
-    padind = -1;
+    {
+      int old_format_len = 0;
+
+      if ((app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK))
+        {
+          if (indatalen > 32 && (indatalen % 2))
+            { /*
+               * Skip the prefix.  It may be 0x40 (in new format), or MPI
+               * head of 0x00 (in old format).
+               */
+              indata = (const char *)indata + 1;
+              indatalen--;
+            }
+          else if (indatalen < 32)
+            { /*
+               * Old format trancated by MPI handling.
+               */
+              old_format_len = indatalen;
+              indatalen = 32;
+            }
+        }
+
+      fixuplen = 7;
+      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;
+      if (old_format_len)
+        {
+          memset (fixbuf+fixuplen, 0, 32 - old_format_len);
+          memcpy (fixbuf+fixuplen + 32 - old_format_len,
+                  indata, old_format_len);
+        }
+      else
+        {
+          memcpy (fixbuf+fixuplen, indata, indatalen);
+        }
+      indata = fixbuf;
+      indatalen = fixuplen + indatalen;
+
+      padind = -1;
+    }
   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)
     {
@@ -4167,6 +4456,30 @@ 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)
+    {
+      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);
+          *outdata = fixbuf;
+          *outdatalen = *outdatalen + 1;
+        }
+    }
 
   if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
       && app->app_local->manufacturer == 5
@@ -4284,13 +4597,14 @@ show_caps (struct app_local_s *s)
   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_aes128? "AES-128":"3DES");
+    log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
+                (s->extcap.sm_algo==2? "AES-128" : "AES-256"));
   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);
   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 ("GnuPG-No-Sync ..: %s\n",  s->flags.no_sync? "yes":"no");
   log_info ("GnuPG-Def-PW2 ..: %s\n",  s->flags.def_chv2? "yes":"no");
@@ -4347,25 +4661,40 @@ parse_historical (struct app_local_s *apploc,
 }
 
 
-static int
-parse_ecc_curve (const unsigned char *buffer, size_t buflen)
+/*
+ * Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
+ * and return the curve name.  Return NULL if not available.
+ * The constant string is not allocated dynamically, never free it.
+ */
+static const char *
+ecc_curve (unsigned char *buf, size_t buflen)
 {
-  int curve;
-
-  if (buflen == 5 && buffer[5] == 0x22)
-    curve = CURVE_NIST_P384;
-  else if (buflen == 5 && buffer[5] == 0x23)
-    curve = CURVE_NIST_P521;
-  else if (buflen == 8)
-    curve = CURVE_NIST_P256;
-  else if (buflen == 5 && buffer[5] == 0x0a)
-    curve = CURVE_SEC_P256K1;
-  else if (buflen == 9)
-    curve = CURVE_ED25519;
-  else
-    curve = CURVE_UNKNOWN;
+  gcry_mpi_t oid;
+  char *oidstr;
+  const char *result;
+  unsigned char *oidbuf;
+
+  oidbuf = xtrymalloc (buflen + 1);
+  if (!oidbuf)
+    return NULL;
+
+  memcpy (oidbuf+1, buf, buflen);
+  oidbuf[0] = buflen;
+  oid = gcry_mpi_set_opaque (NULL, oidbuf, (buflen+1) * 8);
+  if (!oid)
+    {
+      xfree (oidbuf);
+      return NULL;
+    }
 
-  return curve;
+  oidstr = openpgp_oid_to_str (oid);
+  gcry_mpi_release (oid);
+  if (!oidstr)
+    return NULL;
+
+  result = openpgp_oid_to_curve (oidstr, 1);
+  xfree (oidstr);
+  return result;
 }
 
 
@@ -4399,7 +4728,7 @@ parse_algorithm_attribute (app_t app, int keyno)
 
   if (opt.verbose)
     log_info ("Key-Attr-%s ..: ", desc[keyno]);
-  if (*buffer == 1 && (buflen == 5 || buflen == 6))
+  if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6))
     {
       app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
       app->app_local->keyattr[keyno].rsa.e_bits = (buffer[3]<<8 | buffer[4]);
@@ -4423,25 +4752,40 @@ parse_algorithm_attribute (app_t app, int keyno)
            app->app_local->keyattr[keyno].rsa.format == RSA_CRT?  "crt"  :
            app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N?"crt+n":"?");
     }
-  else if (*buffer == 18 || *buffer == 19) /* ECDH or ECDSA */
-    {
-      app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
-      app->app_local->keyattr[keyno].ecc.curve
-        = parse_ecc_curve (buffer + 1, buflen - 1);
-      if (opt.verbose)
-        log_printf
-          ("ECC, curve=%s\n",
-           get_curve_name (app->app_local->keyattr[keyno].ecc.curve));
-    }
-  else if (*buffer == 22) /* EdDSA */
+  else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
+           || *buffer == PUBKEY_ALGO_EDDSA)
     {
-      app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA;
-      app->app_local->keyattr[keyno].eddsa.curve
-        = parse_ecc_curve (buffer + 1, buflen - 1);
-      if (opt.verbose)
-        log_printf
-          ("EdDSA, curve=%s\n",
-           get_curve_name (app->app_local->keyattr[keyno].eddsa.curve));
+      const char *curve;
+      int oidlen = buflen - 1;
+
+      app->app_local->keyattr[keyno].ecc.flags = 0;
+
+      if (buffer[buflen-1] == 0x00 || buffer[buflen-1] == 0xff)
+        { /* Found "pubkey required"-byte for private key template.  */
+          oidlen--;
+          if (buffer[buflen-1] == 0xff)
+            app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_PUBKEY;
+        }
+
+      curve = ecc_curve (buffer + 1, oidlen);
+
+      if (!curve)
+        log_printhex ("Curve with OID not supported: ", buffer+1, buflen-1);
+      else
+        {
+          app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
+          app->app_local->keyattr[keyno].ecc.curve = curve;
+          if (*buffer == PUBKEY_ALGO_EDDSA
+              || (*buffer == PUBKEY_ALGO_ECDH
+                  && !strcmp (app->app_local->keyattr[keyno].ecc.curve,
+                              "Curve25519")))
+            app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_DJB_TWEAK;
+          if (opt.verbose)
+            log_printf
+              ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.curve,
+               !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+               "": keyno==1? " (djb-tweak)": " (eddsa)");
+        }
     }
   else if (opt.verbose)
     log_printhex ("", buffer, buflen);
@@ -4550,24 +4894,29 @@ app_select_openpgp (app_t app)
           app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
           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);
         }
       if (buflen >= 10)
         {
           /* Available with v2 cards.  */
-          app->app_local->extcap.sm_aes128     = (buffer[1] == 1);
+          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]);
         }
       xfree (relptr);
 
-      /* Some of the first cards accidently don't set the
+      /* 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)
         app->app_local->extcap.change_force_chv = 1;
 
+      /* Check optional DO of "General Feature Management" for button.  */
+      relptr = get_one_do (app, 0x7f74, &buffer, &buflen, NULL);
+      if (relptr)
+        /* It must be: 03 81 01 20 */
+        app->app_local->extcap.has_button = 1;
+
       parse_login_data (app);
 
       if (opt.verbose)