gpgconf: Fix for --homedir.
[gnupg.git] / scd / app-openpgp.c
index 72f7640..563a045 100644 (file)
@@ -235,7 +235,8 @@ struct app_local_s {
    } keyattr[3];
 };
 
-#define ECC_FLAG_EDDSA (1 << 0)
+#define ECC_FLAG_DJB_TWEAK (1 << 0)
+#define ECC_FLAG_PUBKEY    (1 << 1)
 
 
 /***** Local prototypes  *****/
@@ -909,12 +910,13 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
     {
       snprintf (buffer, sizeof buffer, "%d %d %s",
                 keyno+1,
-                app->app_local->keyattr[keyno].ecc.flags? PUBKEY_ALGO_EDDSA:
-                (keyno==1? PUBKEY_ALGO_ECDH: PUBKEY_ALGO_ECDSA),
-                openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid));
+                keyno==1? PUBKEY_ALGO_ECDH :
+                (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+                PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA,
+                openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 0));
     }
   else
-    snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
+    snprintf (buffer, sizeof buffer, "%d 0 0 UNKNOWN", keyno+1);
 
   send_status_direct (ctrl, keyword, buffer);
 }
@@ -1222,8 +1224,8 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
 /* 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
@@ -1245,9 +1247,8 @@ get_public_key (app_t app, int keyno)
   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)
@@ -1378,88 +1379,85 @@ get_public_key (app_t app, int keyno)
        }
     }
 
-
-  mbuf = xtrymalloc ( mlen + 1);
+  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_RSA
        || (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC
-           && !app->app_local->keyattr[keyno].ecc.flags))
+           && !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
       && mlen && (*m & 0x80))
-    {
+    {               /* Prepend numbers with a 0 if needed for MPI.  */
       *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 if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC
+           && (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+    {               /* Prepend 0x40 prefix.  */
+      *mbuf = 0x40;
+      memcpy (mbuf+1, m, mlen);
+      mlen++;
     }
   else
-    memcpy (ebuf, e, elen);
+    memcpy (mbuf, m, mlen);
 
   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)
+      ebuf = xtrymalloc (elen + 1);
+      if (!ebuf)
         {
-          gcry_sexp_release (s_pkey);
           err = gpg_error_from_syserror ();
           goto leave;
         }
-      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
-      gcry_sexp_release (s_pkey);
+      /* 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 (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
+                             (int)mlen, mbuf, (int)elen, ebuf);
     }
   else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
     {
-      err = gcry_sexp_build (&s_pkey, NULL,
-                             "(public-key(ecc(curve%s)%s(q%b)))",
-                             openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid),
-                             app->app_local->keyattr[keyno].ecc.flags?
-                             "(flags eddsa)" : "",
-                             (int)mlen, mbuf);
-      if (err)
-        goto leave;
+      char *format;
 
-      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+      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)))";
 
-      keybuf = xtrymalloc (len);
-      if (!keybuf)
-        {
-          gcry_sexp_release (s_pkey);
-          err = gpg_error_from_syserror ();
-          goto leave;
-        }
-      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
-      gcry_sexp_release (s_pkey);
+      err = gcry_sexp_build (&s_pkey, NULL, format,
+                openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1),
+                             (int)mlen, mbuf);
     }
   else
+    err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  if (err)
+    goto leave;
+
+  len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+
+  keybuf = xtrymalloc (len);
+  if (!keybuf)
     {
-      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+      gcry_sexp_release (s_pkey);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
+  gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+  gcry_sexp_release (s_pkey);
 
   app->app_local->pk[keyno].key = (unsigned char*)keybuf;
   app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */
@@ -1471,17 +1469,18 @@ get_public_key (app_t app, int keyno)
   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. */
@@ -1494,19 +1493,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),
@@ -1550,7 +1549,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;
 }
 
@@ -1569,11 +1568,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);
 
@@ -1581,10 +1580,10 @@ 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;;
+  *pklen = app->app_local->pk[keyno].keylen;;
   *pk = xtrymalloc (*pklen);
   if (!*pk)
     {
@@ -1677,7 +1676,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
@@ -1876,8 +1875,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)
     {
@@ -2632,9 +2634,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;
@@ -2644,8 +2647,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;
@@ -2657,8 +2662,15 @@ build_ecc_privkey_template (app_t app, int keyno,
   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;
@@ -2692,6 +2704,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;
@@ -2731,10 +2749,47 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
 }
 
 
+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 <keyno> <algo> rsa<nbits>"
-        ECC: "--force <keyno> <algo> <curvename>"
+        RSA: "--force <key> <algo> rsa<nbits>"
+        ECC: "--force <key> <algo> <curvename>"
   */
 static gpg_error_t
 change_keyattr_from_string (app_t app,
@@ -2744,7 +2799,7 @@ change_keyattr_from_string (app_t app,
 {
   gpg_error_t err = 0;
   char *string;
-  int keyno, algo;
+  int key, keyno, algo;
   int n = 0;
 
   /* VALUE is expected to be a string but not guaranteed to be
@@ -2758,14 +2813,15 @@ 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.  */
-  sscanf (string, " --force %d %d %n", &keyno, &algo, &n);
-  if (n < 13)
+  sscanf (string, "--force %d %d %n", &key, &algo, &n);
+  if (n < 12)
     {
       err = gpg_error (GPG_ERR_INV_DATA);
       goto leave;
     }
 
-  if (keyno < 1 || keyno > 3)
+  keyno = key - 1;
+  if (keyno < 0 || keyno > 2)
     err = gpg_error (GPG_ERR_INV_ID);
   else if (algo == PUBKEY_ALGO_RSA)
     {
@@ -2780,64 +2836,35 @@ change_keyattr_from_string (app_t app,
       else if (nbits > 4096)
         err = gpg_error (GPG_ERR_TOO_LARGE);
       else
-        {
-          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);
-              goto leave;
-            }
-          if (buflen < 6 || buf[0] != PUBKEY_ALGO_RSA)
-            {
-              /* Attriutes too short or not an RSA key.  */
-              xfree (relptr);
-              err = gpg_error (GPG_ERR_CARD);
-              goto leave;
-            }
-
-          /* 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-1, buf, buflen, pincb, pincb_arg);
-          xfree (relptr);
-        }
+        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);
-      else
         {
-          gcry_mpi_t m;
-
-          err = openpgp_oid_from_str (oidstr, &m);
-          if (!err)
-            {
-              unsigned int len;
-              const unsigned char *buf = gcry_mpi_get_opaque (m, &len);
-
-              /* We have enough room at STRING.  */
-              len = buf[0];
-              string[0] = algo;
-              memcpy (string+1, buf+1, len++);
-              err = change_keyattr (app, keyno-1, string, len,
-                                    pincb, pincb_arg);
-              gcry_mpi_release (m);
-            }
+          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 = gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -2972,6 +2999,14 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   if (opt.verbose)
     log_info ("RSA modulus size is %u bits (%u bytes)\n",
               nbits, (unsigned int)rsa_n_len);
+  if (nbits && nbits != maxbits
+      && app->app_local->extcap.algo_attr_change)
+    {
+      /* Try to switch the key to a new length.  */
+      err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
+      if (!err)
+        maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
+    }
   if (nbits != maxbits)
     {
       log_error (_("RSA modulus missing or not of size %d bits\n"),
@@ -3167,8 +3202,13 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   size_t ecc_q_len, ecc_d_len;
   u32 created_at = 0;
   const char *oidstr = NULL;
-  int flag_eddsa = 0;
+  int flag_djb_tweak = 0;
   int algo;
+  gcry_mpi_t oid;
+  const unsigned char *oidbuf = NULL;
+  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" */
@@ -3212,18 +3252,23 @@ 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 && toklen == 5 && !memcmp (tok, "eddsa", 5))
-            flag_eddsa = 1;
+          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)
@@ -3233,13 +3278,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 && !flag_eddsa)
-            /* 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;
@@ -3296,20 +3344,46 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
-  if (flag_eddsa && keyno != 1)
+  if (flag_djb_tweak && keyno != 1)
     algo = PUBKEY_ALGO_EDDSA;
   else if (keyno == 1)
     algo = PUBKEY_ALGO_ECDH;
   else
     algo = PUBKEY_ALGO_ECDSA;
 
+  err = openpgp_oid_from_str (oidstr, &oid);
+  if (err)
+    goto leave;
+  oidbuf = gcry_mpi_get_opaque (oid, &n);
+  oid_len = (n+7)/8;
+  if (!oidbuf)
+    {
+      err = gpg_error_from_syserror ();
+      gcry_mpi_release (oid);
+      goto leave;
+    }
+
   if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
       || app->app_local->keyattr[keyno].ecc.oid != oidstr
-      || app->app_local->keyattr[keyno].ecc.flags != flag_eddsa)
+      || (flag_djb_tweak !=
+          (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
     {
-      log_error ("key attribute on card doesn't match\n");
-      err = gpg_error (GPG_ERR_INV_VALUE);
-      goto leave;
+      if (app->app_local->extcap.algo_attr_change)
+        {
+          unsigned char keyattr[oid_len];
+
+          keyattr[0] = algo;
+          memcpy (keyattr+1, oidbuf+1, oid_len-1);
+          err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg);
+          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)
@@ -3331,6 +3405,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
       err = build_ecc_privkey_template (app, keyno,
                                         ecc_d, ecc_d_len,
+                                        ecc_q, ecc_q_len,
                                         &template, &template_len);
       if (err)
         goto leave;
@@ -3362,31 +3437,13 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
       goto leave;
     }
-  else
-    {
-      gcry_mpi_t oid;
-      const unsigned char *oidbuf;
-      size_t oid_len;
-      unsigned char fprbuf[20];
-
-      err = openpgp_oid_from_str (oidstr, &oid);
-      if (err)
-        goto leave;
 
-      oidbuf = gcry_mpi_get_opaque (oid, &oid_len);
-      if (!oidbuf)
-        {
-          err = gpg_error_from_syserror ();
-          gcry_mpi_release (oid);
-          goto leave;
-        }
-      err = store_fpr (app, keyno, created_at, fprbuf, algo,
-                   oidbuf, oid_len, ecc_q, ecc_q_len,
-                   "\x03\x01\x08\x07", (size_t)4);
-      gcry_mpi_release (oid);
-    }
+  err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+                   ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
 
  leave:
+  if (oidbuf)
+    gcry_mpi_release (oid);
   return err;
 }
 
@@ -3482,16 +3539,15 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   unsigned char *buffer = NULL;
   size_t buflen, keydatalen, mlen, elen;
   time_t created_at;
-  int keyno = atoi (keynostr);
+  int keyno = atoi (keynostr) - 1;
   int force = (flags & 1);
   time_t start_at;
   int exmode;
   int le_value;
   unsigned int keybits;
 
-  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. */
@@ -3553,8 +3609,13 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       log_error (_("generating key failed\n"));
       goto leave;
     }
-  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)
@@ -3589,6 +3650,11 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   send_status_info (ctrl, "KEY-CREATED-AT",
                     numbuf, (size_t)strlen(numbuf), NULL, 0);
 
+  for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+    ;
+  for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+    ;
+
   rc = store_fpr (app, keyno, (u32)created_at, fprbuf, PUBKEY_ALGO_RSA,
                   m, mlen, e, elen);
   if (rc)
@@ -3641,7 +3707,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)
@@ -3656,7 +3722,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])
       {
@@ -3675,7 +3741,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;
@@ -3692,7 +3758,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);
 }
 
 
@@ -3944,7 +4010,7 @@ do_auth (app_t app, const char *keyidstr,
 
   if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
     {
-      if (!app->app_local->keyattr[2].ecc.flags
+      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;
@@ -4080,9 +4146,12 @@ do_decipher (app_t app, const char *keyidstr,
   if (rc)
     return rc;
 
-  if (indatalen == 16 + 1 || indatalen == 32 + 1)
-    /* PSO:DECIPHER with symmetric key.  */
-    padind = -1;
+  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)
     {
       /* We might encounter a couple of leading zeroes in the
@@ -4138,6 +4207,27 @@ do_decipher (app_t app, const char *keyidstr,
     }
   else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
     {
+      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)
@@ -4151,7 +4241,16 @@ do_decipher (app_t app, const char *keyidstr,
       fixbuf[4] = (char)(indatalen+2);
       fixbuf[5] = '\x86';
       fixbuf[6] = (char)indatalen;
-      memcpy (fixbuf+fixuplen, indata, 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;
 
@@ -4177,6 +4276,21 @@ 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)
+        {
+          xfree (*outdata);
+          return gpg_error_from_syserror ();
+        }
+      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 */
       && app->app_local->manufacturer == 5
@@ -4360,17 +4474,33 @@ parse_historical (struct app_local_s *apploc,
 }
 
 
+/*
+ * Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
+ * and return the constant string in dotted decimal form.
+ * Return NULL if not available.
+ * The constant string is not allocated dynamically, never free it.
+ */
 static const char *
 ecc_oid (unsigned char *buf, size_t buflen)
 {
   gcry_mpi_t oid;
   char *oidstr;
   const char *result;
+  unsigned char *oidbuf;
 
-  oid = gcry_mpi_set_opaque (NULL, buf, buflen * 8);
-  if (!oid)
+  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;
+    }
+
   oidstr = openpgp_oid_to_str (oid);
   gcry_mpi_release (oid);
   if (!oidstr)
@@ -4439,13 +4569,37 @@ parse_algorithm_attribute (app_t app, int keyno)
   else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
            || *buffer == PUBKEY_ALGO_EDDSA)
     {
-      app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
-      app->app_local->keyattr[keyno].ecc.oid = ecc_oid (buffer + 1, buflen - 1);
-      app->app_local->keyattr[keyno].ecc.flags = (*buffer == PUBKEY_ALGO_EDDSA);
-      if (opt.verbose)
-        log_printf
-          ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.oid,
-           app->app_local->keyattr[keyno].ecc.flags ? " (eddsa)": "");
+      const char *oid;
+      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;
+        }
+
+      oid = ecc_oid (buffer + 1, oidlen);
+
+      if (!oid)
+        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.oid = oid;
+          if (*buffer == PUBKEY_ALGO_EDDSA
+              || (*buffer == PUBKEY_ALGO_ECDH
+                  && !strcmp (app->app_local->keyattr[keyno].ecc.oid,
+                              "1.3.6.1.4.1.3029.1.5.1")))
+            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.oid,
+               !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+               "": keyno==1? " (djb-tweak)": " (eddsa)");
+        }
     }
   else if (opt.verbose)
     log_printhex ("", buffer, buflen);
@@ -4568,7 +4722,7 @@ app_select_openpgp (app_t app)
         }
       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;