gpg: Finish experimental support for Ed25519.
authorWerner Koch <wk@gnupg.org>
Wed, 7 May 2014 11:16:32 +0000 (13:16 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 7 May 2014 11:27:43 +0000 (13:27 +0200)
* agent/cvt-openpgp.c (try_do_unprotect_arg_s): Add field "curve".
(get_keygrip): Add and use arg CURVE.
(convert_secret_key): Ditto.
(convert_transfer_key): Ditto.
(get_npkey_nskey): New.
(prepare_unprotect): Replace gcrypt functions by
get_npkey_nskey.  Allow opaque MPIs.
(do_unprotect): Use CURVE instead of parameters.
(convert_from_openpgp_main): Ditto.
(convert_to_openpgp):  Simplify.
* g10/import.c (one_mpi_from_pkey): Remove.
(transfer_secret_keys): Rewrite to use the curve instead of the
parameters.
* g10/parse-packet.c (parse_key): Mark protected MPIs with USER1 flag.

* common/openpgp-oid.c (openpgp_curve_to_oid): Allow the use of
 "NIST P-256" et al.
* g10/keygen.c (ask_curve): Add arg ALGO.
(generate_keypair): Rewrite the ECC key logic.

* tests/openpgp/ecc.test: Provide the "ecc" passphrase.

agent/cvt-openpgp.c
agent/keyformat.txt
common/openpgp-oid.c
g10/import.c
g10/keygen.c
g10/parse-packet.c
tests/openpgp/ecc.test

index ef34463..28f0380 100644 (file)
@@ -36,6 +36,7 @@ struct try_do_unprotect_arg_s
   int  is_v4;
   int  is_protected;
   int  pubkey_algo;
+  const char *curve;
   int  protect_algo;
   char *iv;
   int  ivlen;
@@ -54,7 +55,8 @@ struct try_do_unprotect_arg_s
 
 /* Compute the keygrip from the public key and store it at GRIP.  */
 static gpg_error_t
-get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
+get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
+             unsigned char *grip)
 {
   gpg_error_t err;
   gcry_sexp_t s_pkey = NULL;
@@ -80,9 +82,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
 
     case GCRY_PK_ECC:
       err = gcry_sexp_build (&s_pkey, NULL,
-                             "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))",
-                             pkey[0], pkey[1], pkey[2], pkey[3], pkey[4],
-                             pkey[5]);
+                             "(public-key(ecc(curve %s)(q%m)))",
+                             curve, pkey[0]);
       break;
 
     default:
@@ -102,7 +103,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
    parameters into our s-expression based format.  Note that
    PUBKEY_ALGO has an gcrypt algorithm number. */
 static gpg_error_t
-convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
+convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
+                    const char *curve)
 {
   gpg_error_t err;
   gcry_sexp_t s_skey = NULL;
@@ -135,11 +137,12 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
       break;
 
     case GCRY_PK_ECC:
-      err = gcry_sexp_build (&s_skey, NULL,
-                             "(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
-                             "(d%m)))",
-                             skey[0], skey[1], skey[2], skey[3], skey[4],
-                             skey[5], skey[6]);
+      if (!curve)
+        err = gpg_error (GPG_ERR_BAD_SECKEY);
+      else
+        err = gcry_sexp_build (&s_skey, NULL,
+                               "(private-key(ecc(curve%s)(q%m)(d%m)))",
+                               curve, skey[0], skey[1]);
       break;
 
     default:
@@ -160,7 +163,7 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
    mode.  Note that PUBKEY_ALGO has an gcrypt algorithm number. */
 static gpg_error_t
 convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
-                      gcry_sexp_t transfer_key)
+                      const char *curve, gcry_sexp_t transfer_key)
 {
   gpg_error_t err;
   gcry_sexp_t s_skey = NULL;
@@ -197,9 +200,9 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
     case GCRY_PK_ECC:
       err = gcry_sexp_build
         (&s_skey, NULL,
-         "(protected-private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
+         "(protected-private-key(ecc(curve%s)(q%m)"
          "(protected openpgp-native%S)))",
-         skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key);
+         curve, skey[0], transfer_key);
       break;
 
     default:
@@ -254,6 +257,22 @@ checksum (const unsigned char *p, unsigned int n)
 }
 
 
+/* Return the number of expected key parameters.  */
+static void
+get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey)
+{
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_RSA:   *npkey = 2; *nskey = 6; break;
+    case GCRY_PK_ELG:   *npkey = 3; *nskey = 4; break;
+    case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break;
+    case GCRY_PK_DSA:   *npkey = 4; *nskey = 5; break;
+    case GCRY_PK_ECC:   *npkey = 1; *nskey = 2; break;
+    default:            *npkey = 0; *nskey = 0; break;
+    }
+}
+
+
 /* Helper for do_unprotect.  PUBKEY_ALOGO is the gcrypt algo number.
    On success R_NPKEY and R_NSKEY receive the number or parameters for
    the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
@@ -264,7 +283,6 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
                    unsigned int *r_npkey, unsigned int *r_nskey,
                    unsigned int *r_skeylen)
 {
-  gpg_error_t err;
   size_t npkey, nskey, skeylen;
   int i;
 
@@ -293,12 +311,8 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
   /* Get properties of the public key algorithm and do some
      consistency checks.  Note that we need at least NPKEY+1 elements
      in the SKEY array. */
-  if ( (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY,
-                                 NULL, &npkey))
-       || (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY,
-                                    NULL, &nskey)))
-    return err;
-  if (!npkey || npkey >= nskey)
+  get_npkey_nskey (pubkey_algo, &npkey, &nskey);
+  if (!npkey || !nskey || npkey >= nskey)
     return gpg_error (GPG_ERR_INTERNAL);
   if (skeylen <= npkey)
     return gpg_error (GPG_ERR_MISSING_VALUE);
@@ -309,7 +323,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
      encrypted.  */
   for (i=0; i < npkey; i++)
     {
-      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
         return gpg_error (GPG_ERR_BAD_SECKEY);
     }
 
@@ -329,7 +343,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
 static int
 do_unprotect (const char *passphrase,
               int pkt_version, int pubkey_algo, int is_protected,
-              gcry_mpi_t *skey, size_t skeysize,
+              const char *curve, gcry_mpi_t *skey, size_t skeysize,
               int protect_algo, void *protect_iv, size_t protect_ivlen,
               int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
               u16 desired_csum, gcry_sexp_t *r_key)
@@ -353,23 +367,26 @@ do_unprotect (const char *passphrase,
      merely verify the checksum.  */
   if (!is_protected)
     {
-      unsigned char *buffer;
-
       actual_csum = 0;
       for (i=npkey; i < nskey; i++)
         {
-          if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+          if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
             return gpg_error (GPG_ERR_BAD_SECKEY);
 
-          err = gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, skey[i]);
-          if (!err)
+          if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
+            {
+              unsigned int nbits;
+              const unsigned char *buffer;
+              buffer = gcry_mpi_get_opaque (skey[i], &nbits);
+              nbytes = (nbits+7)/8;
+              actual_csum += checksum (buffer, nbytes);
+            }
+          else
             {
-              buffer = (gcry_is_secure (skey[i])?
-                        xtrymalloc_secure (nbytes) : xtrymalloc (nbytes));
-              if (!buffer)
-                return gpg_error_from_syserror ();
-              err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes,
-                                    NULL, skey[i]);
+              unsigned char *buffer;
+
+              err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes,
+                                     skey[i]);
               if (!err)
                 actual_csum += checksum (buffer, nbytes);
               xfree (buffer);
@@ -428,7 +445,8 @@ do_unprotect (const char *passphrase,
     {
       int ndata;
       unsigned int ndatabits;
-      unsigned char *p, *data;
+      const unsigned char *p;
+      unsigned char *data;
       u16 csum_pgp7 = 0;
 
       if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE ))
@@ -527,7 +545,7 @@ do_unprotect (const char *passphrase,
 
       for (i = npkey; i < nskey; i++)
         {
-          unsigned char *p;
+          const unsigned char *p;
           size_t ndata;
           unsigned int ndatabits;
 
@@ -580,7 +598,7 @@ do_unprotect (const char *passphrase,
   if (nskey != skeylen)
     err = gpg_error (GPG_ERR_BAD_SECKEY);
   else
-    err = convert_secret_key (r_key, pubkey_algo, skey);
+    err = convert_secret_key (r_key, pubkey_algo, skey, curve);
   if (err)
     return err;
 
@@ -608,6 +626,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
   err = do_unprotect (pi->pin,
                       arg->is_v4? 4:3,
                       arg->pubkey_algo, arg->is_protected,
+                      arg->curve,
                       arg->skey, arg->skeysize,
                       arg->protect_algo, arg->iv, arg->ivlen,
                       arg->s2k_mode, arg->s2k_algo,
@@ -651,6 +670,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   u32  s2k_count = 0;
   size_t npkey, nskey;
   gcry_mpi_t skey[10];  /* We support up to 9 parameters.  */
+  char *curve = NULL;
   u16 desired_csum;
   int skeyidx = 0;
   gcry_sexp_t s_skey = NULL;
@@ -695,8 +715,6 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       if (!string)
         goto bad_seckey;
       protect_algo = gcry_cipher_map_name (string);
-      if (!protect_algo && !!strcmp (string, "IDEA"))
-        protect_algo = GCRY_CIPHER_IDEA;
       xfree (string);
 
       value = gcry_sexp_nth_data (list, 3, &valuelen);
@@ -739,11 +757,21 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   pubkey_algo = gcry_pk_map_name (string);
   xfree (string);
 
-  if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
-      || gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
-      || !npkey || npkey >= nskey)
+  get_npkey_nskey (pubkey_algo, &npkey, &nskey);
+  if (!npkey || !nskey || npkey >= nskey)
     goto bad_seckey;
 
+  if (npkey == 1) /* This is ECC */
+    {
+      gcry_sexp_release (list);
+      list = gcry_sexp_find_token (top_list, "curve", 0);
+      if (!list)
+        goto bad_seckey;
+      curve = gcry_sexp_nth_string (list, 1);
+      if (!curve)
+        goto bad_seckey;
+    }
+
   gcry_sexp_release (list);
   list = gcry_sexp_find_token (top_list, "skey", 0);
   if (!list)
@@ -770,15 +798,15 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       value = gcry_sexp_nth_data (list, ++idx, &valuelen);
       if (!value || !valuelen)
         goto bad_seckey;
-      if (is_enc)
+      if (is_enc || curve)
         {
-          void *p = xtrymalloc (valuelen);
-          if (!p)
-            goto outofmem;
-          memcpy (p, value, valuelen);
-          skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
+          /* Encrypted parameters and ECC parameters need or can be
+             stored as opaque.  */
+          skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
           if (!skey[skeyidx])
             goto outofmem;
+          if (is_enc)
+            gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
         }
       else
         {
@@ -807,33 +835,24 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   gcry_sexp_release (list); list = NULL;
   gcry_sexp_release (top_list); top_list = NULL;
 
-  /* log_debug ("XXX is_v4=%d\n", is_v4); */
-  /* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */
-  /* log_debug ("XXX is_protected=%d\n", is_protected); */
-  /* log_debug ("XXX protect_algo=%d\n", protect_algo); */
-  /* log_printhex ("XXX iv", iv, ivlen); */
-  /* log_debug ("XXX ivlen=%d\n", ivlen); */
-  /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */
-  /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */
-  /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */
-  /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */
-  /* for (idx=0; skey[idx]; idx++) */
-  /*   { */
-  /*     int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */
-  /*     log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */
-  /*     if (is_enc) */
-  /*       { */
-  /*         void *p; */
-  /*         unsigned int nbits; */
-  /*         p = gcry_mpi_get_opaque (skey[idx], &nbits); */
-  /*         log_printhex (NULL, p, (nbits+7)/8); */
-  /*       } */
-  /*     else */
-  /*       gcry_mpi_dump (skey[idx]); */
-  /*     log_printf ("\n"); */
-  /*   } */
-
-  err = get_keygrip (pubkey_algo, skey, grip);
+#if 0
+  log_debug ("XXX is_v4=%d\n", is_v4);
+  log_debug ("XXX pubkey_algo=%d\n", pubkey_algo);
+  log_debug ("XXX is_protected=%d\n", is_protected);
+  log_debug ("XXX protect_algo=%d\n", protect_algo);
+  log_printhex ("XXX iv", iv, ivlen);
+  log_debug ("XXX ivlen=%d\n", ivlen);
+  log_debug ("XXX s2k_mode=%d\n", s2k_mode);
+  log_debug ("XXX s2k_algo=%d\n", s2k_algo);
+  log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt);
+  log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count);
+  log_debug ("XXX curve='%s'\n", curve);
+  for (idx=0; skey[idx]; idx++)
+    gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1)
+                       ? "skey(e)" : "skey(_)", skey[idx]);
+#endif /*0*/
+
+  err = get_keygrip (pubkey_algo, curve, skey, grip);
   if (err)
     goto leave;
 
@@ -850,7 +869,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       if (err)
         goto leave;
 
-      err = convert_transfer_key (&s_skey, pubkey_algo, skey, s_pgp);
+      err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
       if (err)
         goto leave;
     }
@@ -871,6 +890,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       pi_arg.is_v4 = is_v4;
       pi_arg.is_protected = is_protected;
       pi_arg.pubkey_algo = pubkey_algo;
+      pi_arg.curve = curve;
       pi_arg.protect_algo = protect_algo;
       pi_arg.iv = iv;
       pi_arg.ivlen = ivlen;
@@ -929,6 +949,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
 
  leave:
+  xfree (curve);
   gcry_sexp_release (s_skey);
   gcry_sexp_release (list);
   gcry_sexp_release (top_list);
@@ -1223,11 +1244,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
     {
       char countbuf[35];
       membuf_t mbuf;
-      void *format_args_buf_ptr[1];
-      int   format_args_buf_int[1];
       void *format_args[10+2];
-      unsigned int n;
-      gcry_sexp_t tmpkey, tmpsexp = NULL;
+      gcry_sexp_t tmpkey;
+      gcry_sexp_t tmpsexp = NULL;
 
       snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
 
@@ -1238,11 +1257,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
           put_membuf_str (&mbuf, " _ %m");
           format_args[j++] = array + i;
         }
-      put_membuf_str (&mbuf, " e %b");
-      format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n);
-      format_args_buf_int[0] = (n+7)/8;
-      format_args[j++] = format_args_buf_int;
-      format_args[j++] = format_args_buf_ptr;
+      put_membuf_str (&mbuf, " e %m");
+      format_args[j++] = array + npkey;
       put_membuf_str (&mbuf, ")\n");
       put_membuf (&mbuf, "", 1);
 
index 3f95dae..42c4b1f 100644 (file)
@@ -211,6 +211,7 @@ This format is used to transfer keys between gpg and gpg-agent.
 (openpgp-private-key
   (version V)
   (algo PUBKEYALGO)
+  (curve CURVENAME)
   (skey _ P1 _ P2 _ P3 ... e PN)
   (csum n)
   (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT))
@@ -218,6 +219,7 @@ This format is used to transfer keys between gpg and gpg-agent.
 
 * V is the packet version number (3 or 4).
 * PUBKEYALGO is a Libgcrypt algo name
+* CURVENAME is the name of the curve - only used with ECC.
 * P1 .. PN are the parameters; the public parameters are never encrypted
   the secrect key parameters are encrypted if the "protection" list is
   given.  To make this more explicit each parameter is preceded by a
index 94a2296..bcb9885 100644 (file)
@@ -280,17 +280,20 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits)
       oidstr = "1.3.6.1.4.1.11591.15.1";
       nbits = 255;
     }
-  else if (!strcmp (name, "nistp256"))
+  else if (!strcmp (name, "nistp256") || !strcmp (name, "NIST P-256"))
     {
+      /* Libgcrypt uses "NIST P-256" as standard name for this curve
+         and thus the key generation returns this value.  Thus we
+         allow both strings.  */
       oidstr = "1.2.840.10045.3.1.7";
       nbits = 256;
     }
-  else if (!strcmp (name, "nistp384"))
+  else if (!strcmp (name, "nistp384") || !strcmp (name, "NIST P-384"))
     {
       oidstr = "1.3.132.0.34";
       nbits = 384;
     }
-  else if (!strcmp (name, "nistp521"))
+  else if (!strcmp (name, "nistp521") || !strcmp (name, "NIST P-521"))
     {
       oidstr = "1.3.132.0.35";
       nbits = 521;
index 8223041..2b219a2 100644 (file)
@@ -1128,37 +1128,6 @@ import_one (ctrl_t ctrl,
 }
 
 
-/* Extract one MPI value from the S-expression PKEY which is expected
-   to hold a "public-key".  Returns NULL on error.  */
-static gcry_mpi_t
-one_mpi_from_pkey (gcry_sexp_t pkey, const char *name, size_t namelen)
-{
-  gcry_sexp_t list, l2;
-  gcry_mpi_t a;
-
-  list = gcry_sexp_find_token (pkey, "public-key", 0);
-  if (!list)
-    return NULL;
-  l2 = gcry_sexp_cadr (list);
-  gcry_sexp_release (list);
-  list = l2;
-  if (!list)
-    return NULL;
-
-  l2 = gcry_sexp_find_token (list, name, namelen);
-  if (!l2)
-    {
-      gcry_sexp_release (list);
-      return NULL;
-    }
-  a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-  gcry_sexp_release (l2);
-  gcry_sexp_release (list);
-
-  return a;
-}
-
-
 /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent.  The
    function prints diagnostics and returns an error code. */
 static gpg_error_t
@@ -1174,18 +1143,15 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
   int nskey;
   membuf_t mbuf;
   int i, j;
-  unsigned int n;
-  void *format_args_buf_ptr[PUBKEY_MAX_NSKEY];
-  int   format_args_buf_int[PUBKEY_MAX_NSKEY];
   void *format_args[2*PUBKEY_MAX_NSKEY];
   gcry_sexp_t skey, prot, tmpsexp;
+  gcry_sexp_t curve = NULL;
   unsigned char *transferkey = NULL;
   size_t transferkeylen;
   gcry_cipher_hd_t cipherhd = NULL;
   unsigned char *wrappedkey = NULL;
   size_t wrappedkeylen;
   char *cache_nonce = NULL;
-  gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL};
 
   /* Get the current KEK.  */
   err = agent_keywrap_key (ctrl, 0, &kek, &keklen);
@@ -1263,65 +1229,30 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
           || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
           || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
         {
-          /* We need special treatment for ECC algorithms.  OpenPGP
-             stores only the curve name but the agent expects a full
-             key.  This is so that we can keep all curve name
-             validation code out of gpg-agent.  */
-#if PUBKEY_MAX_NSKEY < 7
-#error  PUBKEY_MAX_NSKEY too low for ECC
-#endif
-          char *curve = openpgp_oid_to_str (pk->pkey[0]);
-          if (!curve)
+          /* The ECC case.  */
+          char *curvestr = openpgp_oid_to_str (pk->pkey[0]);
+          if (!curvestr)
             err = gpg_error_from_syserror ();
           else
             {
-              gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECC, curve);
-
-              xfree (curve);
-              if (!cparam)
-                err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
-              else
+              err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvestr);
+              xfree (curvestr);
+              if (!err)
                 {
-                  const char *s;
-
-                  /* Append the curve parameters P, A, B, G and N.  */
-                  for (i=j=0; !err && *(s = "pabgn"+i); i++)
-                    {
-                      ecc_params[i] = one_mpi_from_pkey (cparam, s, 1);
-                      if (!ecc_params[i])
-                        err = gpg_error (GPG_ERR_INV_CURVE);
-                      else
-                        {
-                          put_membuf_str (&mbuf, " _ %m");
-                          format_args[j++] = ecc_params+i;
-                        }
-                    }
-                  gcry_sexp_release (cparam);
-                  if (!err)
-                    {
-                      /* Append the public key element Q.  */
-                      put_membuf_str (&mbuf, " _ %m");
-                      format_args[j++] = pk->pkey + 1;
-
-                      /* Append the secret key element D.  Note that
-                         for ECDH we need to skip PKEY[2] because this
-                         holds the KEK which is not needed.  */
-                      i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
-                      if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
-                        {
-                          put_membuf_str (&mbuf, " e %b");
-                          format_args_buf_ptr[i]
-                            = gcry_mpi_get_opaque (pk->pkey[i],&n);
-                          format_args_buf_int[i] = (n+7)/8;
-                          format_args[j++] = format_args_buf_int + i;
-                          format_args[j++] = format_args_buf_ptr + i;
-                        }
-                      else
-                        {
-                          put_membuf_str (&mbuf, " _ %m");
-                          format_args[j++] = pk->pkey + i;
-                        }
-                    }
+                  j = 0;
+                  /* Append the public key element Q.  */
+                  put_membuf_str (&mbuf, " _ %m");
+                  format_args[j++] = pk->pkey + 1;
+
+                  /* Append the secret key element D.  For ECDH we
+                     skip PKEY[2] because this holds the KEK which is
+                     not needed by gpg-agent.  */
+                  i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
+                  if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
+                    put_membuf_str (&mbuf, " e %m");
+                  else
+                    put_membuf_str (&mbuf, " _ %m");
+                  format_args[j++] = pk->pkey + i;
                 }
             }
         }
@@ -1331,23 +1262,16 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
           for (i=j=0; i < nskey; i++)
             {
               if (!pk->pkey[i])
-                ; /* Protected keys only have NPKEY+1 elements.  */
-              else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
-                {
-                  put_membuf_str (&mbuf, " e %b");
-                  format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i],&n);
-                  format_args_buf_int[i] = (n+7)/8;
-                  format_args[j++] = format_args_buf_int + i;
-                  format_args[j++] = format_args_buf_ptr + i;
-                }
+                continue; /* Protected keys only have NPKEY+1 elements.  */
+
+              if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
+                put_membuf_str (&mbuf, " e %m");
               else
-                {
-                  put_membuf_str (&mbuf, " _ %m");
-                  format_args[j++] = pk->pkey + i;
-                }
+                put_membuf_str (&mbuf, " _ %m");
+              format_args[j++] = pk->pkey + i;
             }
         }
-      put_membuf_str (&mbuf, ")\n");
+      put_membuf_str (&mbuf, ")");
       put_membuf (&mbuf, "", 1);
       if (err)
         xfree (get_membuf (&mbuf, NULL));
@@ -1398,12 +1322,13 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
                                "(openpgp-private-key\n"
                                " (version %d)\n"
                                " (algo %s)\n"
-                               " %S\n"
+                               " %S%S\n"
                                " (csum %d)\n"
                                " %S)\n",
                                pk->version,
                                openpgp_pk_algo_name (pk->pubkey_algo),
-                               skey, (int)(unsigned long)ski->csum, prot);
+                               curve, skey,
+                               (int)(unsigned long)ski->csum, prot);
       gcry_sexp_release (skey);
       gcry_sexp_release (prot);
       if (!err)
@@ -1463,8 +1388,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
     }
 
  leave:
-  for (i=0; i < DIM (ecc_params); i++)
-    gcry_mpi_release (ecc_params[i]);
+  gcry_sexp_release (curve);
   xfree (cache_nonce);
   xfree (wrappedkey);
   xfree (transferkey);
index f3052e4..314cf9b 100644 (file)
@@ -2086,29 +2086,30 @@ ask_keysize (int algo, unsigned int primary_keysize)
 }
 
 
-/* Ask for the key size.  ALGO is the algorithm.  If PRIMARY_KEYSIZE
-   is not 0, the function asks for the size of the encryption
-   subkey. */
+/* Ask for the curve.  ALGO is the selected algorithm which this
+   function may adjust.  Returns a malloced string with the name of
+   the curve.  */
 static char *
-ask_curve (void)
+ask_curve (int *algo)
 {
   struct {
     const char *name;
     int available;
     int expert_only;
+    int fix_curve;
     const char *pretty_name;
   } curves[] = {
 #if GPG_USE_EDDSA
-    { "Ed25519",         0, 0, "Curve 25519" },
+    { "Curve25519",      0, 0, 1, "Curve 25519" },
 #endif
 #if GPG_USE_ECDSA || GPG_USE_ECDH
-    { "NIST P-256",      0, 1, },
-    { "NIST P-384",      0, 0, },
-    { "NIST P-521",      0, 1, },
-    { "brainpoolP256r1", 0, 1, "Brainpool P-256" },
-    { "brainpoolP384r1", 0, 1, "Brainpool P-384" },
-    { "brainpoolP512r1", 0, 1, "Brainpool P-512" },
-    { "secp256k1", 0, 1 },
+    { "NIST P-256",      0, 1, 0, },
+    { "NIST P-384",      0, 0, 0, },
+    { "NIST P-521",      0, 1, 0, },
+    { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" },
+    { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" },
+    { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" },
+    { "secp256k1",       0, 1, 0  },
 #endif
   };
   int idx;
@@ -2127,9 +2128,14 @@ ask_curve (void)
       if (!opt.expert && curves[idx].expert_only)
         continue;
 
+      /* FIXME: The strcmp below is a temporary hack during
+         development.  It shall be removed as soon as we have proper
+         Curve25519 support in Libgcrypt.  */
       gcry_sexp_release (keyparms);
       rc = gcry_sexp_build (&keyparms, NULL,
-                            "(public-key(ecc(curve %s)))", curves[idx].name);
+                            "(public-key(ecc(curve %s)))",
+                            (!strcmp (curves[idx].name, "Curve25519")
+                             ? "Ed25519" : curves[idx].name));
       if (rc)
         continue;
       if (!gcry_pk_get_curve (keyparms, 0, NULL))
@@ -2171,7 +2177,22 @@ ask_curve (void)
         tty_printf (_("Invalid selection.\n"));
       else
         {
-          result = xstrdup (curves[idx].name);
+          if (curves[idx].fix_curve)
+            log_info ("WARNING: Curve25519 is an experimental algorithm and"
+                      " not yet specified by OpenPGP.  The current"
+                      " implementation may change with the next GnuPG release"
+                      " and thus rendering the key unusable!\n");
+
+          /* If the user selected a signing algorithm and Curve25519
+             we need to update the algo and and the curve name.  */
+          if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA)
+              && curves[idx].fix_curve)
+            {
+              *algo = PUBKEY_ALGO_EDDSA;
+              result = xstrdup ("Ed25519");
+            }
+          else
+            result = xstrdup (curves[idx].name);
           break;
         }
     }
@@ -3459,16 +3480,16 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
         {
           /* Create primary and subkey at once.  */
           both = 1;
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = pKEYTYPE;
-          sprintf( r->u.value, "%d", algo );
-          r->next = para;
-          para = r;
           if (algo == PUBKEY_ALGO_ECDSA
               || algo == PUBKEY_ALGO_EDDSA
               || algo == PUBKEY_ALGO_ECDH)
             {
-              curve = ask_curve ();
+              curve = ask_curve (&algo);
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = pKEYTYPE;
+              sprintf( r->u.value, "%d", algo);
+              r->next = para;
+              para = r;
               nbits = 0;
               r = xmalloc_clear (sizeof *r + strlen (curve));
               r->key = pKEYCURVE;
@@ -3478,6 +3499,11 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
             }
           else
             {
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = pKEYTYPE;
+              sprintf( r->u.value, "%d", algo);
+              r->next = para;
+              para = r;
               nbits = ask_keysize (algo, 0);
               r = xmalloc_clear( sizeof *r + 20 );
               r->key = pKEYLENGTH;
@@ -3501,9 +3527,43 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
           strcpy( r->u.value, "encrypt" );
           r->next = para;
           para = r;
+
+          if (algo == PUBKEY_ALGO_ECDSA
+              || algo == PUBKEY_ALGO_EDDSA
+              || algo == PUBKEY_ALGO_ECDH)
+            {
+              if (algo == PUBKEY_ALGO_EDDSA
+                  && subkey_algo == PUBKEY_ALGO_ECDH)
+                {
+                  /* Need to switch to a different curve for the
+                     encryption key.  */
+                  xfree (curve);
+                  curve = xstrdup ("Curve25519");
+                }
+              r = xmalloc_clear (sizeof *r + strlen (curve));
+              r->key = pSUBKEYCURVE;
+              strcpy (r->u.value, curve);
+              r->next = para;
+              para = r;
+            }
         }
-      else
+      else /* Create only a single key.  */
         {
+          /* For ECC we need to ask for the curve before storing the
+             algo becuase ask_curve may change the algo.  */
+          if (algo == PUBKEY_ALGO_ECDSA
+              || algo == PUBKEY_ALGO_EDDSA
+              || algo == PUBKEY_ALGO_ECDH)
+            {
+              curve = ask_curve (&algo);
+              nbits = 0;
+              r = xmalloc_clear (sizeof *r + strlen (curve));
+              r->key = pKEYCURVE;
+              strcpy (r->u.value, curve);
+              r->next = para;
+              para = r;
+            }
+
           r = xmalloc_clear( sizeof *r + 20 );
           r->key = pKEYTYPE;
           sprintf( r->u.value, "%d", algo );
@@ -3528,13 +3588,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
           || algo == PUBKEY_ALGO_EDDSA
           || algo == PUBKEY_ALGO_ECDH)
         {
-          if (!both)
-            curve = ask_curve ();
-          r = xmalloc_clear (sizeof *r + strlen (curve));
-          r->key = both? pSUBKEYCURVE : pKEYCURVE;
-          strcpy (r->u.value, curve);
-          r->next = para;
-          para = r;
+          /* The curve has already been set.  */
         }
       else
         {
@@ -4031,11 +4085,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   else if (algo == PUBKEY_ALGO_ECDSA
            || algo == PUBKEY_ALGO_EDDSA
            || algo == PUBKEY_ALGO_ECDH)
-    {
-      curve = ask_curve ();
-      if (curve && !strcmp (curve, "Ed25519"))
-        algo = PUBKEY_ALGO_EDDSA;
-    }
+    curve = ask_curve (&algo);
   else
     nbits = ask_keysize (algo, 0);
 
index f708788..424b052 100644 (file)
@@ -2240,6 +2240,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
          pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
                                                 read_rest (inp, pktlen),
                                                 pktlen * 8);
+          /* Mark that MPI as protected - we need this information for
+             importing a key.  The OPAQUE flag can't be used because
+             we also store public EdDSA values in opaque MPIs.  */
+          if (pk->pkey[npkey])
+            gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
          pktlen = 0;
          if (list_mode)
             es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
@@ -2252,6 +2257,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
              if (ski->is_protected)
                {
                  pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen);
+                  if (pk->pkey[i])
+                    gcry_mpi_set_flag (pk->pkey[i], GCRYMPI_FLAG_USER1);
                  if (list_mode)
                    es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i);
                }
index 062a1ae..cb04467 100755 (executable)
@@ -188,7 +188,7 @@ echo 'This is one line' >z
 for msg in $tests; do
    info "checking: $msg"
    eval "(IFS=; echo \"\$$msg\")" >x
-   $GPG -o y --yes x || error "decryption of $msg failed"
+   PINENTRY_USER_DATA=ecc $GPG -o y --yes x || error "decryption of $msg failed"
    cmp y z || error "$msg: mismatch"
 done
 
@@ -204,7 +204,7 @@ for i in $plain_files $data_files ; do
   for k in $mainkeyids ; do
     info "file: $i key: $k"
     $GPG ${opt_always} -e -o x --yes -r $k $i
-    $GPG -o y --yes x
+    PINENTRY_USER_DATA=ecc $GPG -o y --yes x
     cmp $i y || error "$i,$k: mismatch"
   done
 done
@@ -217,7 +217,7 @@ info "Checking ECC signing and verifiction."
 for i in $plain_files $data_files ; do
   for k in $mainkeyids ; do
     info "file: $i key: $k"
-    $GPG -s -o x --yes -u $k $i
+    PINENTRY_USER_DATA=ecc $GPG -s -o x --yes -u $k $i
     $GPG -o y --yes x || error "verify of $i,$k failed"
     cmp $i y || error "$i,$k: mismatch"
   done