Use a unique capitalization for "Note:".
[gnupg.git] / scd / app-openpgp.c
index dd4a2d9..9b4ab22 100644 (file)
@@ -1,6 +1,6 @@
 /* app-openpgp.c - The OpenPGP card application.
  * Copyright (C) 2003, 2004, 2005, 2007, 2008,
- *               2009, 2013 Free Software Foundation, Inc.
+ *               2009, 2013, 2014 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -45,6 +45,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <assert.h>
 #include <time.h>
@@ -121,6 +122,7 @@ typedef enum
   {
     KEY_TYPE_ECDH,
     KEY_TYPE_ECDSA,
+    KEY_TYPE_EDDSA,
     KEY_TYPE_RSA,
   }
 key_type_t;
@@ -143,7 +145,10 @@ enum
   {
     CURVE_NIST_P256,
     CURVE_NIST_P384,
-    CURVE_NIST_P521
+    CURVE_NIST_P521,
+    CURVE_SEC_P256K1,
+    CURVE_ED25519,
+    CURVE_UNKNOWN,
   };
 
 
@@ -234,6 +239,9 @@ struct app_local_s {
       } ecdsa;
       struct {
         int curve;
+      } eddsa;
+      struct {
+        int curve;
         int hashalgo;
         int cipheralgo;
       } ecdh;
@@ -735,24 +743,61 @@ parse_login_data (app_t app)
   xfree (relptr);
 }
 
+
+static unsigned char
+get_algo_byte (key_type_t key_type)
+{
+  if (key_type == KEY_TYPE_ECDSA)
+    return 19;
+  else if (key_type == KEY_TYPE_ECDH)
+    return 18;
+  else if (key_type == KEY_TYPE_EDDSA)
+    return 105;                 /* (experimental) */
+  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,
-           const unsigned char *m, size_t mlen,
-           const unsigned char *e, size_t elen,
-           unsigned char *fpr, unsigned int card_version)
+           unsigned char *fpr, unsigned int card_version,
+           key_type_t key_type,
+           ...)
 {
   unsigned int n, nbits;
   unsigned char *buffer, *p;
   int tag, tag2;
   int rc;
+  const unsigned char *m[MAX_ARGS_STORE_FPR];
+  size_t mlen[MAX_ARGS_STORE_FPR];
+  va_list ap;
+  int argc;
+  int i;
 
-  for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
-    ;
-  for (; elen && !*e; elen--, e++) /* strip leading zeroes */
-    ;
+  n = 6;    /* key packet version, 4-byte timestamps, and algorithm */
+  if (key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECDSA
+      || key_type == KEY_TYPE_EDDSA)
+    argc = 2;
+  else if (key_type == KEY_TYPE_ECDH)
+    argc = 3;
+  else
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  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_RSA || i == 1)
+        n += 2;
+      n += mlen[i];
+    }
+  va_end (ap);
 
-  n = 6 + 2 + mlen + 2 + elen;
   p = buffer = xtrymalloc (3 + n);
   if (!buffer)
     return gpg_error_from_syserror ();
@@ -765,15 +810,19 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
   *p++ = timestamp >> 16;
   *p++ = timestamp >>  8;
   *p++ = timestamp;
-  *p++ = 1; /* RSA */
-  nbits = count_bits (m, mlen);
-  *p++ = nbits >> 8;
-  *p++ = nbits;
-  memcpy (p, m, mlen); p += mlen;
-  nbits = count_bits (e, elen);
-  *p++ = nbits >> 8;
-  *p++ = nbits;
-  memcpy (p, e, elen); p += elen;
+  *p++ = get_algo_byte (key_type);
+
+  for (i = 0; i < argc; i++)
+    {
+      if (key_type == KEY_TYPE_RSA || i == 1)
+        {
+          nbits = count_bits (m[i], mlen[i]);
+          *p++ = nbits >> 8;
+          *p++ = nbits;
+        }
+      memcpy (p, m[i], mlen[i]);
+      p += mlen[i];
+    }
 
   gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
 
@@ -889,11 +938,26 @@ get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid)
       *r_n_bits = 384;
       *r_curve_oid = "1.3.132.0.34";
     }
-  else
+  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
@@ -927,6 +991,13 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
                 app->app_local->keyattr[number].ecdh.hashalgo,
                 app->app_local->keyattr[number].ecdh.cipheralgo);
     }
+  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
+    {
+      get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
+                              &n_bits, &curve_oid);
+      snprintf (buffer, sizeof buffer, "%d 105 %u %s",
+                number+1, n_bits, curve_oid);
+    }
   else
     snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
 
@@ -1234,8 +1305,14 @@ get_curve_name (int curve)
     return "NIST P-256";
   else if (curve == CURVE_NIST_P384)
     return "NIST P-384";
-  else
+  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";
+  else
+    return "unknown";
 }
 
 
@@ -1371,9 +1448,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 ();
@@ -1407,7 +1483,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);
@@ -1456,7 +1533,30 @@ get_public_key (app_t app, int keyno)
         = get_curve_name (app->app_local->keyattr[keyno].ecdsa.curve);
 
       err = gcry_sexp_build (&s_pkey, NULL,
-                             "(public-key(ecdsa(curve%s)(q%b)))",
+                             "(public-key(ecc(curve%s)(q%b)))",
+                             curve_name, 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, mlen, mbuf);
       if (err)
         goto leave;
@@ -2500,18 +2600,19 @@ add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
 }
 
 
-/* Build the private key template as specified in the OpenPGP specs
-   v2.0 section 4.3.3.7.  */
 static gpg_error_t
 build_privkey_template (app_t app, int keyno,
                         const unsigned char *rsa_n, size_t rsa_n_len,
                         const unsigned char *rsa_e, size_t rsa_e_len,
                         const unsigned char *rsa_p, size_t rsa_p_len,
                         const unsigned char *rsa_q, size_t rsa_q_len,
+                        const unsigned char *rsa_u, size_t rsa_u_len,
+                        const unsigned char *rsa_dp, size_t rsa_dp_len,
+                        const unsigned char *rsa_dq, size_t rsa_dq_len,
                         unsigned char **result, size_t *resultlen)
 {
   size_t rsa_e_reqlen;
-  unsigned char privkey[7*(1+3)];
+  unsigned char privkey[7*(1+3+3)];
   size_t privkey_len;
   unsigned char exthdr[2+2+3];
   size_t exthdr_len;
@@ -2529,17 +2630,16 @@ build_privkey_template (app_t app, int keyno,
     {
     case RSA_STD:
     case RSA_STD_N:
-      break;
     case RSA_CRT:
     case RSA_CRT_N:
-      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+      break;
 
     default:
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
-  /* Get the required length for E.  */
-  rsa_e_reqlen = app->app_local->keyattr[keyno].rsa.e_bits/8;
+  /* Get the required length for E. Rounded up to the nearest byte  */
+  rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8;
   assert (rsa_e_len <= rsa_e_reqlen);
 
   /* Build the 7f48 cardholder private key template.  */
@@ -2555,6 +2655,17 @@ build_privkey_template (app_t app, int keyno,
   tp += add_tlv (tp, 0x93, rsa_q_len);
   datalen += rsa_q_len;
 
+  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
+      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+    {
+      tp += add_tlv (tp, 0x94, rsa_u_len);
+      datalen += rsa_u_len;
+      tp += add_tlv (tp, 0x95, rsa_dp_len);
+      datalen += rsa_dp_len;
+      tp += add_tlv (tp, 0x96, rsa_dq_len);
+      datalen += rsa_dq_len;
+    }
+
   if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
       || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
     {
@@ -2608,6 +2719,17 @@ build_privkey_template (app_t app, int keyno,
   memcpy (tp, rsa_q, rsa_q_len);
   tp += rsa_q_len;
 
+  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
+      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+    {
+      memcpy (tp, rsa_u, rsa_u_len);
+      tp += rsa_u_len;
+      memcpy (tp, rsa_dp, rsa_dp_len);
+      tp += rsa_dp_len;
+      memcpy (tp, rsa_dq, rsa_dq_len);
+      tp += rsa_dq_len;
+    }
+
   if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
       || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
     {
@@ -2624,6 +2746,76 @@ build_privkey_template (app_t app, int keyno,
   return 0;
 }
 
+static gpg_error_t
+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;
+  unsigned char exthdr[2+2+1];
+  size_t exthdr_len;
+  unsigned char suffix[2+1];
+  size_t suffix_len;
+  unsigned char *tp;
+  size_t datalen;
+  unsigned char *template;
+  size_t template_size;
+
+  (void)app;
+
+  *result = NULL;
+  *resultlen = 0;
+
+  /* Build the 7f48 cardholder private key template.  */
+  datalen = 0;
+  tp = privkey;
+
+  tp += add_tlv (tp, 0x91, ecc_d_len); /* Tag 0x91??? */
+  datalen += ecc_d_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;
+  *tp++ = 0;
+  tp += add_tlv (tp, 0x7f48, privkey_len);
+  exthdr_len = tp - exthdr;
+
+  /* Build the 5f48 suffix of the data.  */
+  tp = suffix;
+  tp += add_tlv (tp, 0x5f48, datalen);
+  suffix_len = tp - suffix;
+
+  /* Now concatenate everything.  */
+  template_size = (1 + 1   /* 0x4d and len. */
+                   + exthdr_len
+                   + privkey_len
+                   + suffix_len
+                   + datalen);
+  tp = template = xtrymalloc_secure (template_size);
+  if (!template)
+    return gpg_error_from_syserror ();
+
+  tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+  memcpy (tp, exthdr, exthdr_len);
+  tp += exthdr_len;
+  memcpy (tp, privkey, privkey_len);
+  tp += privkey_len;
+  memcpy (tp, suffix, suffix_len);
+  tp += suffix_len;
+
+  memcpy (tp, ecc_d, ecc_d_len);
+  tp += ecc_d_len;
+
+  assert (tp - template == template_size);
+
+  *result = template;
+  *resultlen = tp - template;
+  return 0;
+}
+
 
 /* Helper for do_writekley to change the size of a key.  Not ethat
    this deletes the entire key without asking.  */
@@ -2725,26 +2917,15 @@ change_keyattr_from_string (app_t app,
 }
 
 
-/* Handle the WRITEKEY command for OpenPGP.  This function expects a
-   canonical encoded S-expression with the secret key in KEYDATA and
-   its length (for assertions) in KEYDATALEN.  KEYID needs to be the
-   usual keyid which for OpenPGP is the string "OPENPGP.n" with
-   n=1,2,3.  Bit 0 of FLAGS indicates whether an existing key shall
-   get overwritten.  PINCB and PINCB_ARG are the usual arguments for
-   the pinentry callback.  */
 static gpg_error_t
-do_writekey (app_t app, ctrl_t ctrl,
-             const char *keyid, unsigned int flags,
-             gpg_error_t (*pincb)(void*, const char *, char **),
-             void *pincb_arg,
-             const unsigned char *keydata, size_t keydatalen)
+rsa_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;
-  int force = (flags & 1);
-  int keyno;
-  const unsigned char *buf, *tok;
-  size_t buflen, toklen;
-  int depth, last_depth1, last_depth2;
+  const unsigned char *tok;
+  size_t toklen;
+  int last_depth1, last_depth2;
   const unsigned char *rsa_n = NULL;
   const unsigned char *rsa_e = NULL;
   const unsigned char *rsa_p = NULL;
@@ -2758,52 +2939,6 @@ do_writekey (app_t app, ctrl_t ctrl,
   unsigned char fprbuf[20];
   u32 created_at = 0;
 
-  (void)ctrl;
-
-  if (!strcmp (keyid, "OPENPGP.1"))
-    keyno = 0;
-  else if (!strcmp (keyid, "OPENPGP.2"))
-    keyno = 1;
-  else if (!strcmp (keyid, "OPENPGP.3"))
-    keyno = 2;
-  else
-    return gpg_error (GPG_ERR_INV_ID);
-
-  err = does_key_exist (app, keyno, 0, force);
-  if (err)
-    return err;
-
-
-  /*
-     Parse the S-expression
-   */
-  buf = keydata;
-  buflen = keydatalen;
-  depth = 0;
-  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
-    goto leave;
-  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
-    goto leave;
-  if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
-    {
-      if (!tok)
-        ;
-      else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
-        log_info ("protected-private-key passed to writekey\n");
-      else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
-        log_info ("shadowed-private-key passed to writekey\n");
-      err = gpg_error (GPG_ERR_BAD_SECKEY);
-      goto leave;
-    }
-  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
-    goto leave;
-  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
-    goto leave;
-  if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
-    {
-      err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
-      goto leave;
-    }
   last_depth1 = depth;
   while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
          && depth && depth >= last_depth1)
@@ -2954,16 +3089,53 @@ do_writekey (app_t app, ctrl_t ctrl,
 
   if (app->app_local->extcap.is_v2)
     {
-      /* Build the private key template as described in section 4.3.3.7 of
-         the OpenPGP card specs version 2.0.  */
+      unsigned char *rsa_u, *rsa_dp, *rsa_dq;
+      size_t rsa_u_len, rsa_dp_len, rsa_dq_len;
+      gcry_mpi_t mpi_e, mpi_p, mpi_q;
+      gcry_mpi_t mpi_u = gcry_mpi_snew (0);
+      gcry_mpi_t mpi_dp = gcry_mpi_snew (0);
+      gcry_mpi_t mpi_dq = gcry_mpi_snew (0);
+      gcry_mpi_t mpi_tmp = gcry_mpi_snew (0);
       int exmode;
 
+      /* Calculate the u, dp and dq components needed by RSA_CRT cards */
+      gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL);
+      gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL);
+      gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL);
+
+      gcry_mpi_invm (mpi_u, mpi_q, mpi_p);
+      gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1);
+      gcry_mpi_invm (mpi_dp, mpi_e, mpi_tmp);
+      gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1);
+      gcry_mpi_invm (mpi_dq, mpi_e, mpi_tmp);
+
+      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_u, &rsa_u_len, mpi_u);
+      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dp, &rsa_dp_len, mpi_dp);
+      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dq, &rsa_dq_len, mpi_dq);
+
+      gcry_mpi_release (mpi_e);
+      gcry_mpi_release (mpi_p);
+      gcry_mpi_release (mpi_q);
+      gcry_mpi_release (mpi_u);
+      gcry_mpi_release (mpi_dp);
+      gcry_mpi_release (mpi_dq);
+      gcry_mpi_release (mpi_tmp);
+
+      /* Build the private key template as described in section 4.3.3.7 of
+         the OpenPGP card specs version 2.0.  */
       err = build_privkey_template (app, keyno,
                                     rsa_n, rsa_n_len,
                                     rsa_e, rsa_e_len,
                                     rsa_p, rsa_p_len,
                                     rsa_q, rsa_q_len,
+                                    rsa_u, rsa_u_len,
+                                    rsa_dp, rsa_dp_len,
+                                    rsa_dq, rsa_dq_len,
                                     &template, &template_len);
+      xfree(rsa_u);
+      xfree(rsa_dp);
+      xfree(rsa_dq);
+
       if (err)
         goto leave;
 
@@ -3039,9 +3211,214 @@ do_writekey (app_t app, ctrl_t ctrl,
       goto leave;
     }
 
-  err = store_fpr (app, keyno, created_at,
-                  rsa_n, rsa_n_len, rsa_e, rsa_e_len,
-                  fprbuf, app->card_version);
+  err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
+                   KEY_TYPE_RSA, rsa_n, rsa_n_len, rsa_e, rsa_e_len);
+  if (err)
+    goto leave;
+
+
+ leave:
+  xfree (template);
+  return err;
+}
+
+
+static gpg_error_t
+ecdh_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
+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)
+{
+  gpg_error_t err;
+  const unsigned char *tok;
+  size_t toklen;
+  int last_depth1, last_depth2;
+  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];
+  u32 created_at = 0;
+  int curve = CURVE_UNKNOWN;
+
+  /* (private-key(ecdsa(curve%s)(q%m)(d%m))(created-at%d)):
+     curve = "1.2.840.10045.3.1.7" */
+  /* (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)
+    {
+      if (tok)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+          goto leave;
+        }
+      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+        goto leave;
+
+      if (tok && toklen == 5 && !memcmp (tok, "curve", 5))
+        {
+          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))
+            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 if (tok && toklen == 1)
+        {
+          const unsigned char **buf2;
+          size_t *buf2len;
+
+          switch (*tok)
+            {
+            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 (buf2 && *buf2)
+            {
+              err = gpg_error (GPG_ERR_DUP_VALUE);
+              goto leave;
+            }
+          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++)
+              ;
+
+          *buf2 = tok;
+          *buf2len = toklen;
+        }
+      /* Skip until end of list. */
+      last_depth2 = depth;
+      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+             && depth && depth >= last_depth2)
+        ;
+      if (err)
+        goto leave;
+    }
+  /* Parse other attributes. */
+  last_depth1 = depth;
+  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+         && depth && depth >= last_depth1)
+    {
+      if (tok)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+          goto leave;
+        }
+      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+        goto leave;
+      if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+        {
+          if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+            goto leave;
+          if (tok)
+            {
+              for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+                   tok++, toklen--)
+                created_at = created_at*10 + (*tok - '0');
+            }
+        }
+      /* Skip until end of list. */
+      last_depth2 = depth;
+      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+             && depth && depth >= last_depth2)
+        ;
+      if (err)
+        goto leave;
+    }
+
+
+  /* Check that we have all parameters and that they match the card
+     description. */
+  if (!created_at)
+    {
+      log_error (_("creation timestamp missing\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);
+
+  /* We need to remove the cached public key.  */
+  xfree (app->app_local->pk[keyno].key);
+  app->app_local->pk[keyno].key = NULL;
+  app->app_local->pk[keyno].keylen = 0;
+  app->app_local->pk[keyno].read_done = 0;
+
+  if (app->app_local->extcap.is_v2)
+    {
+      /* Build the private key template as described in section 4.3.3.7 of
+         the OpenPGP card specs version 2.0.  */
+      int exmode;
+
+      err = build_ecc_privkey_template (app, keyno,
+                                        ecc_d, ecc_d_len,
+                                        &template, &template_len);
+      if (err)
+        goto leave;
+
+      /* Prepare for storing the key.  */
+      err = verify_chv3 (app, pincb, pincb_arg);
+      if (err)
+        goto leave;
+
+      /* Store the key. */
+      if (app->app_local->cardcap.ext_lc_le && template_len > 254)
+        exmode = 1;    /* Use extended length w/o a limit.  */
+      else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+        exmode = -254;
+      else
+        exmode = 0;
+      err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+                                  template, template_len);
+    }
+  else
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+  if (err)
+    {
+      log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
+                   curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECDSA,
+                   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_ED25519 ? 10
+                   : curve == CURVE_NIST_P256? 9 : 6,
+                   ecc_q, ecc_q_len);
   if (err)
     goto leave;
 
@@ -3051,6 +3428,89 @@ do_writekey (app_t app, ctrl_t ctrl,
   return err;
 }
 
+/* Handle the WRITEKEY command for OpenPGP.  This function expects a
+   canonical encoded S-expression with the secret key in KEYDATA and
+   its length (for assertions) in KEYDATALEN.  KEYID needs to be the
+   usual keyid which for OpenPGP is the string "OPENPGP.n" with
+   n=1,2,3.  Bit 0 of FLAGS indicates whether an existing key shall
+   get overwritten.  PINCB and PINCB_ARG are the usual arguments for
+   the pinentry callback.  */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+             const char *keyid, unsigned int flags,
+             gpg_error_t (*pincb)(void*, const char *, char **),
+             void *pincb_arg,
+             const unsigned char *keydata, size_t keydatalen)
+{
+  gpg_error_t err;
+  int force = (flags & 1);
+  int keyno;
+  const unsigned char *buf, *tok;
+  size_t buflen, toklen;
+  int depth;
+
+  (void)ctrl;
+
+  if (!strcmp (keyid, "OPENPGP.1"))
+    keyno = 0;
+  else if (!strcmp (keyid, "OPENPGP.2"))
+    keyno = 1;
+  else if (!strcmp (keyid, "OPENPGP.3"))
+    keyno = 2;
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+
+  err = does_key_exist (app, keyno, 0, force);
+  if (err)
+    return err;
+
+
+  /*
+     Parse the S-expression
+   */
+  buf = keydata;
+  buflen = keydatalen;
+  depth = 0;
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+    {
+      if (!tok)
+        ;
+      else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+        log_info ("protected-private-key passed to writekey\n");
+      else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+        log_info ("shadowed-private-key passed to writekey\n");
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  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))
+    ecc_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);
+  else
+    {
+      err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+      goto leave;
+    }
+
+ leave:
+  return err;
+}
+
+
 
 /* Handle the GENKEY command. */
 static gpg_error_t
@@ -3173,8 +3633,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,
-                  m, mlen, e, elen, fprbuf, app->card_version);
+  rc = store_fpr (app, keyno, (u32)created_at, fprbuf, app->card_version,
+                  KEY_TYPE_RSA, m, mlen, e, elen);
   if (rc)
     goto leave;
   send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
@@ -3522,7 +3982,8 @@ 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
@@ -3532,6 +3993,12 @@ do_auth (app_t app, const char *keyidstr,
       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"))
@@ -3908,12 +4375,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
+  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;
 
   return curve;
 }
@@ -3982,10 +4455,16 @@ parse_algorithm_attribute (app_t app, int keyno)
   else if (*buffer == 18 && buflen == 11) /* ECDH */
     {
       app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDH;
-      app->app_local->keyattr[keyno].ecdh.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];
+      app->app_local->keyattr[keyno].ecdh.curve
+        = parse_ecc_curve (buffer + 3, buflen - 3);
+    }
+  else if (*buffer == 105) /* EdDSA (experimental) */
+    {
+      app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA;
+      app->app_local->keyattr[keyno].eddsa.curve
+        = parse_ecc_curve (buffer + 1, buflen - 1);
     }
   else if (opt.verbose)
     log_printhex ("", buffer, buflen);