scd: do_decipher change for OpenPGPcard v3.0.
[gnupg.git] / scd / app-openpgp.c
index b1599fb..0e751e0 100644 (file)
@@ -67,6 +67,7 @@
 #include "iso7816.h"
 #include "app-common.h"
 #include "tlv.h"
+#include "host2net.h"
 
 
 /* A table describing the DOs of the card.  */
@@ -120,8 +121,8 @@ static struct {
 /* Type of keys.  */
 typedef enum
   {
-    KEY_TYPE_ECDH,
-    KEY_TYPE_ECDSA,
+    KEY_TYPE_ECC,
+    KEY_TYPE_EDDSA,
     KEY_TYPE_RSA,
   }
 key_type_t;
@@ -146,7 +147,8 @@ enum
     CURVE_NIST_P384,
     CURVE_NIST_P521,
     CURVE_SEC_P256K1,
-    CURVE_UNKOWN,
+    CURVE_ED25519,
+    CURVE_UNKNOWN,
   };
 
 
@@ -234,12 +236,10 @@ struct app_local_s {
       } rsa;
       struct {
         int curve;
-      } ecdsa;
+      } ecc;
       struct {
         int curve;
-        int hashalgo;
-        int cipheralgo;
-      } ecdh;
+      } eddsa;
     };
    } keyattr[3];
 };
@@ -740,12 +740,14 @@ parse_login_data (app_t app)
 
 
 static unsigned char
-get_algo_byte (key_type_t key_type)
+get_algo_byte (int keynumber, key_type_t key_type)
 {
-  if (key_type == KEY_TYPE_ECDSA)
+  if (key_type == KEY_TYPE_ECC && keynumber != 1)
     return 19;
-  else if (key_type == KEY_TYPE_ECDH)
+  else if (key_type == KEY_TYPE_ECC && keynumber == 1)
     return 18;
+  else if (key_type == KEY_TYPE_EDDSA)
+    return 22;
   else
     return 1;  /* RSA */
 }
@@ -754,10 +756,8 @@ get_algo_byte (key_type_t key_type)
 
 /* 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, unsigned int card_version,
-           key_type_t key_type,
-           ...)
+store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
+           key_type_t key_type, ...)
 {
   unsigned int n, nbits;
   unsigned char *buffer, *p;
@@ -770,20 +770,20 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
   int i;
 
   n = 6;    /* key packet version, 4-byte timestamps, and algorithm */
-  if (key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECDSA)
-    argc = 2;
-  else if (key_type == KEY_TYPE_ECDH)
+  if (keynumber == 1 && key_type == KEY_TYPE_ECC)
     argc = 3;
   else
-    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    argc = 2;
 
   va_start (ap, key_type);
   for (i = 0; i < argc; i++)
     {
       m[i] = va_arg (ap, const unsigned char *);
       mlen[i] = va_arg (ap, size_t);
-      for (; mlen[i] && !*m[i]; mlen[i]--, m[i]++) /* strip leading zeroes */
-        ;
+      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)
         n += 2;
       n += mlen[i];
@@ -802,7 +802,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
   *p++ = timestamp >> 16;
   *p++ = timestamp >>  8;
   *p++ = timestamp;
-  *p++ = get_algo_byte (key_type);
+  *p++ = get_algo_byte (keynumber, key_type);
 
   for (i = 0; i < argc; i++)
     {
@@ -820,7 +820,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
 
   xfree (buffer);
 
-  tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
+  tag = (app->card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
   flush_cache_item (app, 0xC5);
   tag2 = 0xCE + keynumber;
   flush_cache_item (app, 0xCD);
@@ -829,7 +829,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
   if (rc)
     log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
 
-  if (!rc && card_version > 0x0100)
+  if (!rc && app->card_version > 0x0100)
     {
       unsigned char buf[4];
 
@@ -877,7 +877,7 @@ send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
   char numbuf1[50], numbuf2[50];
   unsigned long value;
 
-  value = (stamp[0] << 24) | (stamp[1]<<16) | (stamp[2]<<8) | stamp[3];
+  value = buf32_to_ulong (stamp);
   if (!value)
     return;
   sprintf (numbuf1, "%d", number);
@@ -935,11 +935,21 @@ get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid)
       *r_n_bits = 521;
       *r_curve_oid = "1.3.132.0.35";
     }
-  else
+  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
@@ -957,21 +967,19 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
               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_ECDSA)
+  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECC)
     {
-      get_ecc_key_parameters (app->app_local->keyattr[number].ecdsa.curve,
+      get_ecc_key_parameters (app->app_local->keyattr[number].ecc.curve,
                               &n_bits, &curve_oid);
-      snprintf (buffer, sizeof buffer, "%d 19 %u %s",
-                number+1, 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_ECDH)
+  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
     {
-      get_ecc_key_parameters (app->app_local->keyattr[number].ecdh.curve,
+      get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
                               &n_bits, &curve_oid);
-      snprintf (buffer, sizeof buffer, "%d 18 %u %s %d %d",
-                number+1, n_bits, curve_oid,
-                app->app_local->keyattr[number].ecdh.hashalgo,
-                app->app_local->keyattr[number].ecdh.cipheralgo);
+      snprintf (buffer, sizeof buffer, "%d 22 %u %s",
+                number+1, n_bits, curve_oid);
     }
   else
     snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
@@ -1046,10 +1054,10 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     }
   if (table[idx].special == -2)
     {
-      char tmp[100];
+      char tmp[110];
 
       snprintf (tmp, sizeof tmp,
-                "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d",
+                "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d si=%u",
                 app->app_local->extcap.get_challenge,
                 app->app_local->extcap.key_import,
                 app->app_local->extcap.change_force_chv,
@@ -1058,7 +1066,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
                 app->app_local->extcap.algo_attr_change,
                 (app->app_local->extcap.sm_supported
                  ? (app->app_local->extcap.sm_aes128? 7 : 2)
-                 : 0));
+                 : 0),
+                app->app_local->status_indicator);
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
@@ -1187,7 +1196,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
   for (;;)
     {
       char *p;
-      char *fields[6];
+      char *fields[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
       int nfields;
       size_t max_length;
       gcry_mpi_t mpi;
@@ -1282,8 +1291,12 @@ get_curve_name (int curve)
     return "NIST P-384";
   else if (curve == CURVE_NIST_P521)
     return "NIST P-521";
-  else
+  else if (curve == CURVE_SEC_P256K1)
     return "secp256k1";
+  else if (curve == CURVE_ED25519)
+    return "Ed25519";
+  else
+    return "unknown";
 }
 
 
@@ -1419,9 +1432,8 @@ get_public_key (app_t app, int keyno)
        }
       hexkeyid = fpr + 24;
 
-      ret = estream_asprintf (&command,
-                             "gpg --list-keys --with-colons --with-key-data '%s'",
-                             fpr);
+      ret = gpgrt_asprintf
+        (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr);
       if (ret < 0)
        {
          err = gpg_error_from_syserror ();
@@ -1438,7 +1450,7 @@ get_public_key (app_t app, int keyno)
        }
 
       err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
-      fclose (fp);
+      pclose (fp);
       if (err)
        {
          log_error ("error while retrieving key material through pipe: %s\n",
@@ -1455,7 +1467,8 @@ get_public_key (app_t app, int keyno)
       goto leave;
     }
   /* Prepend numbers with a 0 if needed.  */
-  if (mlen && (*m & 0x80))
+  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_EDDSA
+      && mlen && (*m & 0x80))
     {
       *mbuf = 0;
       memcpy (mbuf+1, m, mlen);
@@ -1483,7 +1496,7 @@ get_public_key (app_t app, int keyno)
   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)))",
-                             mlen, mbuf, elen, ebuf);
+                             (int)mlen, mbuf, (int)elen, ebuf);
       if (err)
         goto leave;
 
@@ -1498,14 +1511,37 @@ get_public_key (app_t app, int keyno)
       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_ECDSA)
+  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
     {
       const char *curve_name
-        = get_curve_name (app->app_local->keyattr[keyno].ecdsa.curve);
+        = 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, mlen, mbuf);
+                             curve_name, (int)mlen, mbuf);
+      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 ();
+          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);
       if (err)
         goto leave;
 
@@ -2695,9 +2731,9 @@ build_privkey_template (app_t app, int keyno,
 }
 
 static gpg_error_t
-build_ecdsa_privkey_template (app_t app, int keyno,
-                              const unsigned char *ecc_d, size_t ecc_d_len,
-                              unsigned char **result, size_t *resultlen)
+build_ecc_privkey_template (app_t app, int keyno,
+                            const unsigned char *ecc_d, size_t ecc_d_len,
+                            unsigned char **result, size_t *resultlen)
 {
   unsigned char privkey[2];
   size_t privkey_len;
@@ -2719,7 +2755,7 @@ build_ecdsa_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;
 
   privkey_len = tp - privkey;
@@ -3159,8 +3195,8 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       goto leave;
     }
 
-  err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
-                   KEY_TYPE_RSA, rsa_n, rsa_n_len, rsa_e, rsa_e_len);
+  err = store_fpr (app, keyno, created_at, fprbuf, KEY_TYPE_RSA,
+                   rsa_n, rsa_n_len, rsa_e, rsa_e_len);
   if (err)
     goto leave;
 
@@ -3172,27 +3208,10 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 
 
 static gpg_error_t
-ecdh_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
               void *pincb_arg, int keyno,
               const unsigned char *buf, size_t buflen, int depth)
 {
-  (void)app;
-  (void)pincb;
-  (void)pincb_arg;
-  (void)keyno;
-  (void)buf;
-  (void)buflen;
-  (void)depth;
-
-  return GPG_ERR_NOT_IMPLEMENTED;
-}
-
-
-static gpg_error_t
-ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
-                void *pincb_arg, int keyno,
-                const unsigned char *buf, size_t buflen, int depth)
-{
   gpg_error_t err;
   const unsigned char *tok;
   size_t toklen;
@@ -3204,10 +3223,14 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   size_t template_len;
   unsigned char fprbuf[20];
   u32 created_at = 0;
-  int curve = CURVE_UNKOWN;
-
-  /* (private-key(ecdsa(curve%s)(q%m)(d%m))): curve = "1.2.840.10045.3.1.7" */
-  /* (private-key(ecc(curve%s)(q%m)(d%m))): curve = "secp256k1" */
+  int curve = CURVE_UNKNOWN;
+
+  /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
+     curve = "NIST P-256" */
+  /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
+     curve = "secp256k1" */
+  /* (private-key(ecc(curve%s)(flags eddsa)(q%m)(d%m))(created-at%d)):
+      curve = "Ed25519" */
   last_depth1 = depth;
   while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
          && depth && depth >= last_depth1)
@@ -3225,37 +3248,44 @@ ecdsa_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 == 19 && !memcmp (tok, "1.2.840.10045.3.1.7", 19))
+          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
+            {
+              log_error (_("unsupported curve\n"));
+              err = gpg_error (GPG_ERR_INV_VALUE);
+              goto leave;
+            }
         }
       else if (tok && toklen == 1)
         {
-          const unsigned char **mpi;
-          size_t *mpi_len;
+          const unsigned char **buf2;
+          size_t *buf2len;
 
           switch (*tok)
             {
-            case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break;
-            case 'd': mpi = &ecc_d; mpi_len = &ecc_d_len; break;
-            default: mpi = NULL;  mpi_len = NULL; break;
+            case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
+            case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; break;
+            default: buf2 = NULL;  buf2len = NULL; break;
             }
-          if (mpi && *mpi)
+          if (buf2 && *buf2)
             {
               err = gpg_error (GPG_ERR_DUP_VALUE);
               goto leave;
             }
           if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
             goto leave;
-          if (tok && mpi)
-            {
-              /* Strip off leading zero bytes and save. */
-              for (;toklen && !*tok; toklen--, tok++)
-                ;
-              *mpi = tok;
-              *mpi_len = toklen;
-            }
+          if (tok && buf2 && curve != CURVE_ED25519)
+            /* It's MPI.  Strip off leading zero bytes and save. */
+            for (;toklen && !*tok; toklen--, tok++)
+              ;
+
+          *buf2 = tok;
+          *buf2len = toklen;
         }
       /* Skip until end of list. */
       last_depth2 = depth;
@@ -3322,9 +3352,9 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
          the OpenPGP card specs version 2.0.  */
       int exmode;
 
-      err = build_ecdsa_privkey_template (app, keyno,
-                                          ecc_d, ecc_d_len,
-                                          &template, &template_len);
+      err = build_ecc_privkey_template (app, keyno,
+                                        ecc_d, ecc_d_len,
+                                        &template, &template_len);
       if (err)
         goto leave;
 
@@ -3352,13 +3382,16 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       goto leave;
     }
 
-  err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
-                   KEY_TYPE_ECDSA,
-                   curve == CURVE_NIST_P256?
+  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"
-                   : "\05\x2b\x81\x04\x00\x0a",
-                   curve == CURVE_NIST_P256? 9 : 6,
-                   ecc_q, ecc_q_len);
+                   : "\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;
 
@@ -3431,15 +3464,12 @@ do_writekey (app_t app, ctrl_t ctrl,
   if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
     goto leave;
   if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0)
-    rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
-  else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
-            && (keyno == 0 || keyno == 2))
-           || (tok && toklen == 5 && memcmp ("ecdsa", tok, toklen) == 0))
-    ecdsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
-  else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
-            && keyno == 1)
-           || (tok && toklen == 4 && memcmp ("ecdh", tok, toklen) == 0))
-    ecdh_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
+    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)))
+    err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
   else
     {
       err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
@@ -3573,8 +3603,8 @@ 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);
 
-  rc = store_fpr (app, keyno, (u32)created_at, fprbuf, app->card_version,
-                  KEY_TYPE_RSA, m, mlen, e, elen);
+  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);
@@ -3922,16 +3952,23 @@ do_auth (app_t app, const char *keyidstr,
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen > 101) /* For a 2048 bit key. */
+  if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+      && indatalen > 101) /* For a 2048 bit key. */
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECDSA
+  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)
+    {
+      const char *p = (const char *)indata + 15;
+      indata = p;
+      indatalen -= 15;
+    }
 
   /* Check whether an OpenPGP card of any version has been requested. */
   if (!strcmp (keyidstr, "OPENPGP.3"))
@@ -4007,6 +4044,9 @@ do_decipher (app_t app, const char *keyidstr,
   int n;
   const char *fpr = NULL;
   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);
@@ -4048,12 +4088,11 @@ do_decipher (app_t app, const char *keyidstr,
     return rc;
 
   rc = verify_chv2 (app, pincb, pincb_arg);
-  if (!rc)
-    {
-      int fixuplen;
-      unsigned char *fixbuf = NULL;
-      int padind = 0;
+  if (rc)
+    return rc;
 
+  if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
+    {
       /* 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
@@ -4104,33 +4143,56 @@ do_decipher (app_t app, const char *keyidstr,
           /* We use the extra leading zero as the padding byte.  */
           padind = -1;
         }
+    }
+  else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+    {
+      fixuplen = 7;
+      fixbuf = xtrymalloc (fixuplen + indatalen);
+      if (!fixbuf)
+        return gpg_error_from_syserror ();
 
-      if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
-        {
-          exmode = 1;    /* Extended length w/o a limit.  */
-          le_value = app->app_local->extcap.max_rsp_data;
-        }
-      else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
-        {
-          exmode = -254; /* Command chaining with max. 254 bytes.  */
-          le_value = 0;
-        }
-      else
-        exmode = le_value = 0;
-
-      rc = iso7816_decipher (app->slot, exmode,
-                             indata, indatalen, le_value, padind,
-                             outdata, outdatalen);
-      xfree (fixbuf);
+      /* 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;
+      memcpy (fixbuf+fixuplen, indata, indatalen);
+      indata = fixbuf;
+      indatalen = fixuplen + indatalen;
 
-      if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
-          && app->app_local->manufacturer == 5
-          && app->card_version == 0x0200)
-        log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
-                  " do not work with encryption keys > 2048 bits\n");
+      padind = -1;
+    }
+  else
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-      *r_info |= APP_DECIPHER_INFO_NOPAD;
+  if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+    {
+      exmode = 1;    /* Extended length w/o a limit.  */
+      le_value = app->app_local->extcap.max_rsp_data;
+    }
+  else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+    {
+      exmode = -254; /* Command chaining with max. 254 bytes.  */
+      le_value = 0;
     }
+  else
+    exmode = le_value = 0;
+
+  rc = iso7816_decipher (app->slot, exmode,
+                         indata, indatalen, le_value, padind,
+                         outdata, outdatalen);
+  xfree (fixbuf);
+
+  if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
+      && app->app_local->manufacturer == 5
+      && app->card_version == 0x0200)
+    log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
+              " do not work with encryption keys > 2048 bits\n");
+
+  *r_info |= APP_DECIPHER_INFO_NOPAD;
 
   return rc;
 }
@@ -4211,7 +4273,7 @@ do_check_pin (app_t app, const char *keyidstr,
           log_info (_("card is permanently locked!\n"));
           return gpg_error (GPG_ERR_BAD_PIN);
         }
-      else if (value[6] < 3)
+      else if (count < 3)
         {
           log_info (_("verification of Admin PIN is currently prohibited "
                       "through this command\n"));
@@ -4308,14 +4370,18 @@ parse_ecc_curve (const unsigned char *buffer, size_t buflen)
 {
   int curve;
 
-  if (buflen == 6 && buffer[5] == 0x22)
+  if (buflen == 5 && buffer[5] == 0x22)
     curve = CURVE_NIST_P384;
-  else if (buflen == 6 && buffer[5] == 0x23)
+  else if (buflen == 5 && buffer[5] == 0x23)
     curve = CURVE_NIST_P521;
-  else if (buflen == 9)
+  else if (buflen == 8)
     curve = CURVE_NIST_P256;
-  else
+  else if (buflen == 5 && buffer[5] == 0x0a)
     curve = CURVE_SEC_P256K1;
+  else if (buflen == 9)
+    curve = CURVE_ED25519;
+  else
+    curve = CURVE_UNKNOWN;
 
   return curve;
 }
@@ -4375,19 +4441,25 @@ 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 == 19) /* ECDSA */
+  else if (*buffer == 18 || *buffer == 19) /* ECDH or ECDSA */
     {
-      app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDSA;
-      app->app_local->keyattr[keyno].ecdsa.curve
+      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 == 18 && buflen == 11) /* ECDH */
+  else if (*buffer == 22) /* EdDSA */
     {
-      app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDH;
-      app->app_local->keyattr[keyno].ecdh.curve
+      app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA;
+      app->app_local->keyattr[keyno].eddsa.curve
         = parse_ecc_curve (buffer + 1, buflen - 1);
-      app->app_local->keyattr[keyno].ecdh.hashalgo = buffer[1];
-      app->app_local->keyattr[keyno].ecdh.cipheralgo = buffer[2];
+      if (opt.verbose)
+        log_printf
+          ("EdDSA, curve=%s\n",
+           get_curve_name (app->app_local->keyattr[keyno].eddsa.curve));
     }
   else if (opt.verbose)
     log_printhex ("", buffer, buflen);