gpg: Rework ECC support and add experimental support for Ed25519.
authorWerner Koch <wk@gnupg.org>
Fri, 15 Nov 2013 07:59:45 +0000 (08:59 +0100)
committerWerner Koch <wk@gnupg.org>
Fri, 15 Nov 2013 08:01:11 +0000 (09:01 +0100)
* agent/findkey.c (key_parms_from_sexp): Add algo name "ecc".
(agent_is_dsa_key): Ditto.
(agent_is_eddsa_key): New.  Not finished, though.
* agent/pksign.c (do_encode_eddsa): New.
(agent_pksign_do): Use gcry_log_debug functions.
* agent/protect.c (agent_protect): Parse a flags parameter.
* g10/keygen.c (gpg_curve_to_oid): Move to ...
* common/openpgp-oid.c (openpgp_curve_to_oid): here and rename.
(oid_ed25519): New.
(openpgp_oid_is_ed25519): New.
(openpgp_oid_to_curve): New.
* common/t-openpgp-oid.c (test_openpgp_oid_is_ed25519): New.
* g10/build-packet.c (gpg_mpi_write): Write the length header also for
opaque MPIs.
(gpg_mpi_write_nohdr): New.
(do_key): Use gpg_mpi_write_nohdr depending on algorithm.
(do_pubkey_enc): Ditto.
* g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Use
gpg_mpi_write_nohdr.
* g10/export.c (transfer_format_to_openpgp):
* g10/keygen.c (ecckey_from_sexp): Return the error.
(gen_ecc): Repalce arg NBITS by CURVE.
(read_parameter_file): Add keywords "Key-Curve" and "Subkey-Curve".
(ask_curve): New.
(generate_keypair, generate_subkeypair): Use ask_curve.
(do_generate_keypair): Also pass curve name.
* g10/keylist.c (list_keyblock_print, list_keyblock_colon): Print
curve name.
* g10/parse-packet.c (mpi_read): Remove workaround for
Libcgrypt < 1.5.
(parse_key): Fix ECC case.  Print the curve name.
* g10/pkglue.c (mpi_from_sexp): Rename to get_mpi_from_sexp.
(pk_verify, pk_check_secret_key): Add special case for Ed25519.
* g10/seskey.c (encode_md_value): Ditto.
* g10/sign.c (do_sign, hash_for, sign_file): Ditto.
--

Be warned that this code is subject to further changes and that the
format will very likely change before a release.  There are also known
bugs and missing code.

Signed-off-by: Werner Koch <wk@gnupg.org>
20 files changed:
agent/agent.h
agent/findkey.c
agent/pksign.c
agent/protect.c
common/openpgp-oid.c
common/t-openpgp-oid.c
common/util.h
doc/DETAILS
g10/build-packet.c
g10/ecdh.c
g10/export.c
g10/keygen.c
g10/keylist.c
g10/main.h
g10/packet.h
g10/parse-packet.c
g10/pkglue.c
g10/pkglue.h
g10/seskey.c
g10/sign.c

index ae4e468..d409300 100644 (file)
@@ -324,6 +324,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
                                         const unsigned char *grip,
                                         gcry_sexp_t *result);
 int agent_is_dsa_key (gcry_sexp_t s_key);
+int agent_is_eddsa_key (gcry_sexp_t s_key);
 int agent_key_available (const unsigned char *grip);
 gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
                                       int *r_keytype,
index d11f088..aa2c6a2 100644 (file)
@@ -729,6 +729,11 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
       algoname = "dsa";
       elems = "pqgy";
     }
+  else if (n==3 && !memcmp (name, "ecc", 3))
+    {
+      algoname = "ecc";
+      elems = "pabgnq";
+    }
   else if (n==5 && !memcmp (name, "ecdsa", 5))
     {
       algoname = "ecdsa";
@@ -788,6 +793,8 @@ agent_is_dsa_key (gcry_sexp_t s_key)
 
   if (!strcmp (algoname, "dsa"))
     return GCRY_PK_DSA;
+  else if (!strcmp (algoname, "ecc"))
+    return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag.  */
   else if (!strcmp (algoname, "ecdsa"))
     return GCRY_PK_ECDSA;
   else
@@ -795,6 +802,28 @@ agent_is_dsa_key (gcry_sexp_t s_key)
 }
 
 
+/* Return true if S_KEY is an EdDSA key as used with curve Ed25519.  */
+int
+agent_is_eddsa_key (gcry_sexp_t s_key)
+{
+  char algoname[6];
+
+  if (!s_key)
+    return 0;
+
+  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+    return 0; /* Error - assume it is not an DSA key.  */
+
+  if (!strcmp (algoname, "dsa"))
+    return GCRY_PK_DSA;
+  else if (!strcmp (algoname, "ecc"))
+    return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag.  */
+  else if (!strcmp (algoname, "ecdsa"))
+    return GCRY_PK_ECDSA;
+  else
+    return 0;
+}
+
 
 /* Return the key for the keygrip GRIP.  The result is stored at
    RESULT.  This function extracts the key from the private key
index 9c7341a..b2ee28f 100644 (file)
@@ -131,6 +131,24 @@ rfc6979_hash_algo_string (size_t mdlen)
 }
 
 
+/* Encode a message digest for use with the EdDSA algorithm
+   (i.e. curve Ed25519). */
+static gpg_error_t
+do_encode_eddsa (const byte *md, size_t mdlen, gcry_sexp_t *r_hash)
+{
+  gpg_error_t err;
+  gcry_sexp_t hash;
+
+  *r_hash = NULL;
+  err = gcry_sexp_build (&hash, NULL,
+                         "(data(flags eddsa)(hash-algo sha512)(value %b))",
+                         (int)mdlen, md);
+  if (!err)
+    *r_hash = hash;
+  return err;
+}
+
+
 /* Encode a message digest for use with an DSA algorithm. */
 static gpg_error_t
 do_encode_dsa (const byte *md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey,
@@ -400,7 +418,11 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
       int dsaalgo;
 
       /* Put the hash into a sexp */
-      if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
+      if (agent_is_eddsa_key (s_skey))
+        rc = do_encode_eddsa (ctrl->digest.value,
+                              ctrl->digest.valuelen,
+                              &s_hash);
+      else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
         rc = do_encode_raw_pkcs1 (ctrl->digest.value,
                                   ctrl->digest.valuelen,
                                   gcry_pk_get_nbits (s_skey),
@@ -421,10 +443,8 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
       if (DBG_CRYPTO)
         {
-          log_debug ("skey:\n");
-          gcry_sexp_dump (s_skey);
-          log_debug ("hash:\n");
-          gcry_sexp_dump (s_hash);
+          gcry_log_debugsxp ("skey", s_skey);
+          gcry_log_debugsxp ("hash", s_hash);
         }
 
       /* sign */
@@ -437,10 +457,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         }
 
       if (DBG_CRYPTO)
-        {
-          log_debug ("result:\n");
-          gcry_sexp_dump (s_sig);
-        }
+        gcry_log_debugsxp ("rslt", s_sig);
     }
 
  leave:
index b29f494..749867c 100644 (file)
@@ -467,6 +467,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   int depth = 0;
   unsigned char *p;
   gcry_md_hd_t md;
+  int have_curve = 0;
 
   /* Create an S-expression with the protected-at timestamp.  */
   memcpy (timestamp_exp, "(12:protected-at15:", 19);
@@ -499,6 +500,11 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   if (!protect_info[infidx].algo)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 
+  /* The parser below is a complete mess: To make it robust for ECC
+     use we should reorder the s-expression to include only what we
+     really need and thus guarantee the right order for saving stuff.
+     This should be done before calling this function and maybe with
+     the help of the new gcry_sexp_extract_param.  */
   parmlist      = protect_info[infidx].parmlist;
   prot_from_idx = protect_info[infidx].prot_from;
   prot_to_idx   = protect_info[infidx].prot_to;
@@ -522,10 +528,19 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
               /* This is a private ECC key but the first parameter is
                  the name of the curve.  We change the parameter list
                  here to the one we expect in this case.  */
+              have_curve = 1;
               parmlist = "?qd";
               prot_from_idx = 2;
               prot_to_idx = 2;
             }
+          else if (n == 5 && !memcmp (s, "flags", 5)
+                   && i == 1 && have_curve)
+            {
+              /* "curve" followed by "flags": Change again.  */
+              parmlist = "??qd";
+              prot_from_idx = 3;
+              prot_to_idx = 3;
+            }
           else
             return gpg_error (GPG_ERR_INV_SEXP);
         }
index 19fadd3..a1ceba4 100644 (file)
@@ -1,5 +1,6 @@
 /* openpgp-oids.c - OID helper for OpenPGP
  * Copyright (C) 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
 #include "util.h"
 
 
+/* The OID for Curve Ed25519 in OpenPGP format.  */
+static const char oid_ed25519[] =
+  { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 };
+
+
 /* Helper for openpgp_oid_from_str.  */
 static size_t
 make_flagged_int (unsigned long value, char *buf, size_t buflen)
@@ -236,3 +242,88 @@ openpgp_oid_to_str (gcry_mpi_t a)
   xfree (string);
   return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
 }
+
+
+
+/* Return true if A represents the OID for Ed25519.  */
+int
+openpgp_oid_is_ed25519 (gcry_mpi_t a)
+{
+  const unsigned char *buf;
+  unsigned int nbits;
+  size_t n;
+
+  if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+    return 0;
+
+  buf = gcry_mpi_get_opaque (a, &nbits);
+  n = (nbits+7)/8;
+  return (n == DIM (oid_ed25519)
+          && !memcmp (buf, oid_ed25519, DIM (oid_ed25519)));
+}
+
+
+
+/* Map the Libgcrypt ECC curve NAME to an OID.  If R_NBITS is not NULL
+   store the bit size of the curve there.  Returns NULL for unknown
+   curve names.  */
+const char *
+openpgp_curve_to_oid (const char *name, unsigned int *r_nbits)
+{
+  unsigned int nbits = 0;
+  const char *oidstr;
+
+  if (!name)
+    oidstr = NULL;
+  else if (!strcmp (name, "Ed25519"))
+    {
+      oidstr = "1.3.6.1.4.1.3029.1.5.1";
+      nbits = 255;
+    }
+  else if (!strcmp (name, "nistp256"))
+    {
+      oidstr = "1.2.840.10045.3.1.7";
+      nbits = 256;
+    }
+  else if (!strcmp (name, "nistp384"))
+    {
+      oidstr = "1.3.132.0.34";
+      nbits = 384;
+    }
+  else if (!strcmp (name, "nistp521"))
+    {
+      oidstr = "1.3.132.0.35";
+      nbits = 521;
+    }
+  else
+    oidstr = NULL;
+
+  if (r_nbits)
+    *r_nbits = nbits;
+  return oidstr;
+}
+
+
+/* Map an OpenPGP OID to the Libgcrypt curve NAME.  If R_NBITS is not
+   NULL store the bit size of the curve there.  Returns "?" for
+   unknown curve names.  */
+const char *
+openpgp_oid_to_curve (const char *oid)
+{
+  const char *name;
+
+  if (!oid)
+    name = "";
+  else if (!strcmp (oid, "1.3.6.1.4.1.3029.1.5.1"))
+    name = "Ed25519";
+  else if (!strcmp (oid, "1.2.840.10045.3.1.7"))
+    name = "NIST P-256";
+  else if (!strcmp (oid, "1.3.132.0.34"))
+    name = "NIST P-384";
+  else if (!strcmp (oid, "1.3.132.0.35"))
+    name = "NIST P-521";
+  else /* FIXME: Lookup via Libgcrypt.  */
+    name = "?";
+
+  return name;
+}
index 80e5763..d101b75 100644 (file)
@@ -35,7 +35,7 @@
 static void
 test_openpgp_oid_from_str (void)
 {
-  static char *sample_oids[] =
+   static char *sample_oids[] =
     {
       "0.0",
       "1.0",
@@ -134,6 +134,41 @@ test_openpgp_oid_to_str (void)
 }
 
 
+static void
+test_openpgp_oid_is_ed25519 (void)
+{
+  static struct
+  {
+    int yes;
+    const char *oidstr;
+  } samples[] = {
+    { 0, "0.0" },
+    { 0, "1.3.132.0.35" },
+    { 0, "1.3.6.1.4.1.3029.1.5.0" },
+    { 1, "1.3.6.1.4.1.3029.1.5.1" },
+    { 0, "1.3.6.1.4.1.3029.1.5.2" },
+    { 0, "1.3.6.1.4.1.3029.1.5.1.0" },
+    { 0, "1.3.6.1.4.1.3029.1.5" },
+    { 0, NULL },
+  };
+  gpg_error_t err;
+  gcry_mpi_t a;
+  int idx;
+
+  for (idx=0; samples[idx].oidstr; idx++)
+    {
+      err = openpgp_oid_from_str (samples[idx].oidstr, &a);
+      if (err)
+        fail (idx, err);
+
+      if (openpgp_oid_is_ed25519 (a) != samples[idx].yes)
+        fail (idx, 0);
+
+      gcry_mpi_release (a);
+    }
+
+}
+
 
 int
 main (int argc, char **argv)
@@ -143,6 +178,7 @@ main (int argc, char **argv)
 
   test_openpgp_oid_from_str ();
   test_openpgp_oid_to_str ();
+  test_openpgp_oid_is_ed25519 ();
 
   return 0;
 }
index 13b702c..f938888 100644 (file)
@@ -215,6 +215,9 @@ size_t percent_unescape_inplace (char *string, int nulrepl);
 /*-- openpgp-oid.c --*/
 gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
 char *openpgp_oid_to_str (gcry_mpi_t a);
+int openpgp_oid_is_ed25519 (gcry_mpi_t a);
+const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits);
+const char *openpgp_oid_to_curve (const char *oid);
 
 
 
index 100755a..a52f51c 100644 (file)
@@ -32,8 +32,8 @@ fpr:::::::::AB059359A3B81F410FCFF97F5CE086B5B5A18FF4:
 #+end_example
 
 The double =--with-fingerprint= prints the fingerprint for the subkeys
-too.  Old versions of gpg used a lighly different format and required
-the use of the option =--fixed-list-mode= to conform to format
+too.  Old versions of gpg used a slighrly different format and required
+the use of the option =--fixed-list-mode= to conform to the format
 described here.
 
 ** Description of the fields
@@ -201,6 +201,11 @@ described here.
     For sig records, this is the used hash algorithm.  For example:
     2 = SHA-1, 8 = SHA-256.
 
+*** Field 17 - Curve name
+
+    For pub, sub, sec, and sbb records this field is used for the ECC
+    curve name.
+
 ** Special fields
 
 *** PKD - Public key data
index 159b783..6681b34 100644 (file)
@@ -166,9 +166,14 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
     {
       unsigned int nbits;
       const void *p;
+      unsigned int lenhdr[2];
 
       p = gcry_mpi_get_opaque (a, &nbits);
-      rc = iobuf_write (out, p, (nbits+7)/8);
+      lenhdr[0] = nbits >> 8;
+      lenhdr[1] = nbits;
+      rc = iobuf_write (out, lenhdr, 2);
+      if (!rc)
+        rc = iobuf_write (out, p, (nbits+7)/8);
     }
   else
     {
@@ -191,6 +196,29 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
 }
 
 
+/*
+ * Write an opaque MPI to the output stream without length info.
+ */
+gpg_error_t
+gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a)
+{
+  int rc;
+
+  if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+    {
+      unsigned int nbits;
+      const void *p;
+
+      p = gcry_mpi_get_opaque (a, &nbits);
+      rc = iobuf_write (out, p, (nbits+7)/8);
+    }
+  else
+    rc = gpg_error (GPG_ERR_BAD_MPI);
+
+  return rc;
+}
+
+
 /* Calculate the length of a packet described by PKT.  */
 u32
 calc_packet_length( PACKET *pkt )
@@ -302,7 +330,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
 
   for (i=0; i < npkey; i++ )
     {
-      err = gpg_mpi_write (a, pk->pkey[i]);
+      if ((pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0))
+          || (pk->pubkey_algo == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2))
+        err = gpg_mpi_write_nohdr (a, pk->pkey[i]);
+      else
+        err = gpg_mpi_write (a, pk->pkey[i]);
       if (err)
         goto leave;
     }
@@ -473,7 +505,12 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
     write_fake_data( a, enc->data[0] );
 
   for (i=0; i < n && !rc ; i++ )
-    rc = gpg_mpi_write (a, enc->data[i]);
+    {
+      if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
+        rc = gpg_mpi_write_nohdr (a, enc->data[i]);
+      else
+        rc = gpg_mpi_write (a, enc->data[i]);
+    }
 
   if (!rc)
     {
index 8b1949c..752181e 100644 (file)
@@ -197,11 +197,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi,
 
     obuf = iobuf_temp();
     /* variable-length field 1, curve name OID */
-    err = gpg_mpi_write (obuf, pkey[0]);
+    err = gpg_mpi_write_nohdr (obuf, pkey[0]);
     /* fixed-length field 2 */
     iobuf_put (obuf, PUBKEY_ALGO_ECDH);
     /* variable-length field 3, KDF params */
-    err = (err ? err : gpg_mpi_write (obuf, pkey[2]));
+    err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2]));
     /* fixed-length field 4 */
     iobuf_write (obuf, "Anonymous Sender    ", 20);
     /* fixed-length field 5, recipient fp */
index 7fbcb34..01bdd5e 100644 (file)
@@ -583,7 +583,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
         goto leave;
       curvename = gcry_pk_get_curve (s_pubkey, 0, NULL);
       gcry_sexp_release (s_pubkey);
-      curveoidstr = gpg_curve_to_oid (curvename, NULL);
+      curveoidstr = openpgp_curve_to_oid (curvename, NULL);
       if (!curveoidstr)
         {
           log_error ("no OID known for curve '%s'\n", curvename);
index 3b02f04..9c371bd 100644 (file)
 enum para_name {
   pKEYTYPE,
   pKEYLENGTH,
+  pKEYCURVE,
   pKEYUSAGE,
   pSUBKEYTYPE,
   pSUBKEYLENGTH,
+  pSUBKEYCURVE,
   pSUBKEYUSAGE,
   pAUTHKEYTYPE,
   pNAMEREAL,
@@ -1071,40 +1073,6 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
   return err;
 }
 
-/* Map the Libgcrypt ECC curve NAME to an OID.  If R_NBITS is not NULL
-   store the bit size of the curve there.  Returns NULL for unknown
-   curve names.  */
-const char *
-gpg_curve_to_oid (const char *name, unsigned int *r_nbits)
-{
-  unsigned int nbits = 0;
-  const char *oidstr;
-
-  if (!name)
-    oidstr = NULL;
-  else if (!strcmp (name, "NIST P-256"))
-    {
-      oidstr = "1.2.840.10045.3.1.7";
-      nbits = 256;
-    }
-  else if (!strcmp (name, "NIST P-384"))
-    {
-      oidstr = "1.3.132.0.34";
-      nbits = 384;
-    }
-  else if (!strcmp (name, "NIST P-521"))
-    {
-      oidstr = "1.3.132.0.35";
-      nbits = 521;
-    }
-  else
-    oidstr = NULL;
-
-  if (r_nbits)
-    *r_nbits = nbits;
-  return oidstr;
-}
-
 
 static gpg_error_t
 ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
@@ -1142,7 +1110,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
       goto leave;
     }
   gcry_sexp_release (l2);
-  oidstr = gpg_curve_to_oid (curve, &nbits);
+  oidstr = openpgp_curve_to_oid (curve, &nbits);
   if (!oidstr)
     {
       /* That can't happen because we used one of the curves
@@ -1188,7 +1156,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
           array[i] = NULL;
         }
     }
-  return 0;
+  return err;
 }
 
 
@@ -1534,31 +1502,24 @@ gen_dsa (unsigned int nbits, KBNODE pub_root,
  * Generate an ECC key
  */
 static gpg_error_t
-gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root,
+gen_ecc (int algo, const char *curve, kbnode_t pub_root,
          u32 timestamp, u32 expireval, int is_subkey,
          int keygen_flags, char **cache_nonce_addr)
 {
   gpg_error_t err;
-  const char *curve;
   char *keyparms;
 
   assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH);
 
-  /* For now we may only use one of the 3 NIST curves.  See also
-     gpg_curve_to_oid.  */
-  if (nbits <= 256)
-    curve = "NIST P-256";
-  else if (nbits <= 384)
-    curve = "NIST P-384";
-  else
-    curve = "NIST P-521";
+  if (!curve || !*curve)
+    return gpg_error (GPG_ERR_UNKNOWN_CURVE);
 
-  keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))",
-                           algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
+  keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s%s)))",
                            strlen (curve), curve,
-                           ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
-                            && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
-                           "(transient-key)" : "" );
+                           (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
+                             && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
+                            " transient-key" : ""),
+                           (!strcmp (curve, "Ed25519")? " eddsa":""));
   if (!keyparms)
     err = gpg_error_from_syserror ();
   else
@@ -2082,6 +2043,98 @@ 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. */
+static char *
+ask_curve (void)
+{
+  struct {
+    const char *name;
+    int available;
+    int expert_only;
+    const char *pretty_name;
+  } curves[] = {
+    { "Ed25519",         0, 0, "Curve 25519" },
+    { "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" },
+  };
+  int idx;
+  char *answer;
+  char *result = NULL;
+  gcry_sexp_t keyparms;
+
+  tty_printf (_("Please select which elliptic curve you want:\n"));
+
+  keyparms = NULL;
+  for (idx=0; idx < DIM(curves); idx++)
+    {
+      int rc;
+
+      curves[idx].available = 0;
+      if (!opt.expert && curves[idx].expert_only)
+        continue;
+
+      gcry_sexp_release (keyparms);
+      rc = gcry_sexp_build (&keyparms, NULL,
+                            "(public-key(ecc(curve %s)))", curves[idx].name);
+      if (rc)
+        continue;
+      if (!gcry_pk_get_curve (keyparms, 0, NULL))
+        continue;
+
+      curves[idx].available = 1;
+      tty_printf (_("   (%d) %s\n"), idx + 1,
+                  curves[idx].pretty_name?
+                  curves[idx].pretty_name:curves[idx].name);
+    }
+  gcry_sexp_release (keyparms);
+
+
+  for (;;)
+    {
+      answer = cpr_get ("keygen.curve", _("Your selection? "));
+      cpr_kill_prompt ();
+      idx = *answer? atoi (answer) : 1;
+      if (*answer && !idx)
+        {
+          /* See whether the user entered the name of the curve.  */
+          for (idx=0; idx < DIM(curves); idx++)
+            {
+              if (!opt.expert && curves[idx].expert_only)
+                continue;
+              if (!stricmp (curves[idx].name, answer)
+                  || (curves[idx].pretty_name
+                      && !stricmp (curves[idx].pretty_name, answer)))
+                break;
+            }
+          if (idx == DIM(curves))
+            idx = -1;
+        }
+      else
+        idx--;
+      xfree(answer);
+      answer = NULL;
+      if (idx < 0 || idx >= DIM (curves) || !curves[idx].available)
+        tty_printf (_("Invalid selection.\n"));
+      else
+        {
+          result = xstrdup (curves[idx].name);
+          break;
+        }
+    }
+
+  if (!result)
+    result = xstrdup (curves[0].name);
+
+  return result;
+}
+
+
 /****************
  * Parse an expire string and return its value in seconds.
  * Returns (u32)-1 on error.
@@ -2539,7 +2592,7 @@ do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled)
 /* Basic key generation.  Here we divert to the actual generation
    routines based on the requested algorithm.  */
 static int
-do_create (int algo, unsigned int nbits, KBNODE pub_root,
+do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root,
            u32 timestamp, u32 expiredate, int is_subkey,
            int keygen_flags, char **cache_nonce_addr)
 {
@@ -2561,7 +2614,7 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root,
     err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey,
                    keygen_flags, cache_nonce_addr);
   else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
-    err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
+    err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey,
                    keygen_flags, cache_nonce_addr);
   else if (algo == PUBKEY_ALGO_RSA)
     err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
@@ -2974,7 +3027,6 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
            * but because we do this always, why not here.  */
           STRING2KEY *s2k;
           DEK *dek;
-          static int count;
 
           s2k = xmalloc ( sizeof *s2k );
           s2k->mode = opt.s2k_mode;
@@ -3058,9 +3110,11 @@ read_parameter_file( const char *fname )
     } keywords[] = {
        { "Key-Type",       pKEYTYPE},
        { "Key-Length",     pKEYLENGTH },
+       { "Key-Curve",      pKEYCURVE },
        { "Key-Usage",      pKEYUSAGE },
        { "Subkey-Type",    pSUBKEYTYPE },
        { "Subkey-Length",  pSUBKEYLENGTH },
+       { "Subkey-Curve",   pSUBKEYCURVE },
        { "Subkey-Usage",   pSUBKEYUSAGE },
        { "Name-Real",      pNAMEREAL },
        { "Name-Email",     pNAMEEMAIL },
@@ -3340,6 +3394,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
   else
     {
       int subkey_algo;
+      char *curve = NULL;
 
       /* Fixme: To support creating a primary key by keygrip we better
          also define the keyword for the parameter file.  Note that
@@ -3355,12 +3410,24 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
           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;
-         sprintf( r->u.value, "%u", nbits);
-         r->next = para;
-         para = r;
+          if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
+            {
+              curve = ask_curve ();
+              r = xmalloc_clear (sizeof *r + strlen (curve));
+              r->key = pKEYCURVE;
+              strcpy (r->u.value, curve);
+              r->next = para;
+              para = r;
+            }
+          else
+            {
+              nbits = ask_keysize (algo, 0);
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = pKEYLENGTH;
+              sprintf( r->u.value, "%u", nbits);
+              r->next = para;
+              para = r;
+            }
           r = xmalloc_clear( sizeof *r + 20 );
           r->key = pKEYUSAGE;
           strcpy( r->u.value, "sign" );
@@ -3400,12 +3467,27 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
           nbits = 0;
         }
 
-      nbits = ask_keysize (both? subkey_algo : algo, nbits);
-      r = xmalloc_clear( sizeof *r + 20 );
-      r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
-      sprintf( r->u.value, "%u", nbits);
-      r->next = para;
-      para = r;
+      if (algo == PUBKEY_ALGO_ECDSA || 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;
+        }
+      else
+        {
+          nbits = ask_keysize (both? subkey_algo : algo, nbits);
+          r = xmalloc_clear( sizeof *r + 20 );
+          r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+          sprintf( r->u.value, "%u", nbits);
+          r->next = para;
+          para = r;
+        }
+
+      xfree (curve);
     }
 
   expire = ask_expire_interval(0,NULL);
@@ -3630,6 +3712,7 @@ do_generate_keypair (struct para_data_s *para,
   if (!card)
     err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
                      get_parameter_uint( para, pKEYLENGTH ),
+                     get_parameter_value (para, pKEYCURVE),
                      pub_root,
                      timestamp,
                      get_parameter_u32( para, pKEYEXPIRE ), 0,
@@ -3681,6 +3764,7 @@ do_generate_keypair (struct para_data_s *para,
         {
           err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL),
                            get_parameter_uint (para, pSUBKEYLENGTH),
+                           get_parameter_value (para, pSUBKEYCURVE),
                            pub_root,
                            timestamp,
                            get_parameter_u32 (para, pSUBKEYEXPIRE), 1,
@@ -3827,7 +3911,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   int algo;
   unsigned int use;
   u32 expire;
-  unsigned int nbits;
+  unsigned int nbits = 0;
+  char *curve = NULL;
   u32 cur_time;
   char *hexgrip = NULL;
   char *serialno = NULL;
@@ -3881,7 +3966,14 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   hexgrip = NULL;
   algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
   assert (algo);
-  nbits = hexgrip? 0 : ask_keysize (algo, 0);
+
+  if (hexgrip)
+    nbits = 0;
+  else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
+    curve = ask_curve ();
+  else
+    nbits = ask_keysize (algo, 0);
+
   expire = ask_expire_interval (0, NULL);
   if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
                                                _("Really create? (y/N) ")))
@@ -3894,7 +3986,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
     err = do_create_from_keygrip (ctrl, algo, hexgrip,
                                   keyblock, cur_time, expire, 1);
   else
-    err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
+    err = do_create (algo, nbits, curve,
+                     keyblock, cur_time, expire, 1, 0, NULL);
   if (err)
     goto leave;
 
@@ -3911,6 +4004,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   write_status_text (STATUS_KEY_CREATED, "S");
 
  leave:
+  xfree (curve);
   xfree (hexgrip);
   xfree (serialno);
   if (err)
index d45aed6..356fac3 100644 (file)
@@ -817,6 +817,17 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
           nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo),
           keystr_from_pk (pk), datestr_from_pk (pk));
 
+  if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+      || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+    {
+      char *curve = openpgp_oid_to_str (pk->pkey[0]);
+      const char *name = openpgp_oid_to_curve (curve);
+      if (!*name || *name == '?')
+        name = curve;
+      es_fprintf (es_stdout, " %s", name);
+      xfree (curve);
+    }
+
   if (pk->flags.revoked)
     {
       es_fprintf (es_stdout, " [");
@@ -940,6 +951,18 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
                   s2k_char,
                  nbits_from_pk (pk2), pubkey_letter (pk2->pubkey_algo),
                  keystr_from_pk (pk2), datestr_from_pk (pk2));
+
+          if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
+            {
+              char *curve = openpgp_oid_to_str (pk2->pkey[0]);
+              const char *name = openpgp_oid_to_curve (curve);
+              if (!*name || *name == '?')
+                name = curve;
+              es_fprintf (es_stdout, " %s", name);
+              xfree (curve);
+            }
+
          if (pk2->flags.revoked)
            {
              es_fprintf (es_stdout, " [");
@@ -1172,16 +1195,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
   es_putc (':', es_stdout);
   es_putc (':', es_stdout);
   print_capabilities (pk, keyblock);
+  es_putc (':', es_stdout);            /* End of field 13. */
+  es_putc (':', es_stdout);            /* End of field 14. */
   if (secret)
     {
-      es_putc (':', es_stdout);                /* End of field 13. */
-      es_putc (':', es_stdout);                /* End of field 14. */
       if (stubkey)
        es_putc ('#', es_stdout);
       else if (serialno)
-        es_fputs(serialno, es_stdout);
-      es_putc (':', es_stdout);                /* End of field 15. */
+        es_fputs (serialno, es_stdout);
+    }
+  es_putc (':', es_stdout);            /* End of field 15. */
+  es_putc (':', es_stdout);            /* End of field 16. */
+  if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+      || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+    {
+      char *curve = openpgp_oid_to_str (pk->pkey[0]);
+      const char *name = openpgp_oid_to_curve (curve);
+      if (!*name || *name == '?')
+        name = curve;
+      es_fputs (name, es_stdout);
+      xfree (curve);
     }
+  es_putc (':', es_stdout);            /* End of field 17. */
   es_putc ('\n', es_stdout);
 
   print_revokers (pk);
@@ -1285,16 +1320,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
                  /* fixme: add LID and ownertrust here */
            );
          print_capabilities (pk2, NULL);
+          es_putc (':', es_stdout);    /* End of field 13. */
+          es_putc (':', es_stdout);    /* End of field 14. */
           if (secret)
             {
-              es_putc (':', es_stdout);        /* End of field 13. */
-              es_putc (':', es_stdout);        /* End of field 14. */
               if (stubkey)
                 es_putc ('#', es_stdout);
               else if (serialno)
                 es_fputs (serialno, es_stdout);
-              es_putc (':', es_stdout);        /* End of field 15. */
             }
+          es_putc (':', es_stdout);    /* End of field 15. */
+          es_putc (':', es_stdout);    /* End of field 16. */
+          if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+              || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+            {
+              char *curve = openpgp_oid_to_str (pk->pkey[0]);
+              const char *name = openpgp_oid_to_curve (curve);
+              if (!*name || *name == '?')
+                name = curve;
+              es_fputs (name, es_stdout);
+              xfree (curve);
+            }
+          es_putc (':', es_stdout);    /* End of field 17. */
          es_putc ('\n', es_stdout);
          if (fpr > 1)
            print_fingerprint (pk2, 0);
index 15d3b76..fd4e5e9 100644 (file)
@@ -230,7 +230,6 @@ void keyedit_passwd (ctrl_t ctrl, const char *username);
 void show_basic_key_info (KBNODE keyblock);
 
 /*-- keygen.c --*/
-const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits);
 u32 parse_expire_string(const char *string);
 u32 ask_expire_interval(int object,const char *def_expire);
 u32 ask_expiredate(void);
index fa32ab1..b3956ef 100644 (file)
@@ -445,6 +445,7 @@ PACKET *create_gpg_control ( ctrlpkttype_t type,
 /*-- build-packet.c --*/
 int build_packet( iobuf_t inp, PACKET *pkt );
 gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a);
+gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a);
 u32 calc_packet_length( PACKET *pkt );
 void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
                        const byte *buffer, size_t buflen );
index 9c04362..3b2698f 100644 (file)
@@ -140,22 +140,13 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
       nread++;
     }
 
-  if (nread >= 2 && !(buf[0] << 8 | buf[1]))
-    {
-      /* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero)
-         MPIs.  We fix this here.  */
-      a = gcry_mpi_new (0);
-    }
-  else
-    {
-      if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
-       a = NULL;
-    }
+  if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
+    a = NULL;
 
  leave:
   gcry_free (buf);
   if (nread > *ret_nread)
-    log_bug ("mpi larger than packet");
+    log_bug ("mpi larger than packet (%zu/%u)", nread, *ret_nread);
   else
     *ret_nread = nread;
   return a;
@@ -1999,8 +1990,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       for (i = 0; i < npkey; i++)
         {
-          if ((algorithm == PUBKEY_ALGO_ECDSA
-               || algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2))
+          if ((algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
+              || (algorithm == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2))
             {
               size_t n;
              err = read_size_body (inp, pktlen, &n, pk->pkey+i);
@@ -2020,6 +2011,14 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
             {
               es_fprintf (listfp, "\tpkey[%d]: ", i);
               mpi_print (listfp, pk->pkey[i], mpi_print_mode);
+              if ((algorithm == PUBKEY_ALGO_ECDSA
+                   || algorithm == PUBKEY_ALGO_ECDH) && i==0)
+                {
+                  char *curve = openpgp_oid_to_str (pk->pkey[0]);
+                  es_fprintf (listfp, " %s (%s)",
+                              openpgp_oid_to_curve (curve), curve);
+                  xfree (curve);
+                }
               es_putc ('\n', listfp);
             }
         }
index 3a078bd..7e50a1c 100644 (file)
 /* FIXME: Better chnage the fucntion name because mpi_ is used by
    gcrypt macros.  */
 gcry_mpi_t
-mpi_from_sexp (gcry_sexp_t sexp, const char * item)
+get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt)
 {
   gcry_sexp_t list;
   gcry_mpi_t data;
 
   list = gcry_sexp_find_token (sexp, item, 0);
   assert (list);
-  data = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG);
+  data = gcry_sexp_nth_mpi (list, 1, mpifmt);
   assert (data);
   gcry_sexp_release (list);
   return data;
@@ -58,6 +58,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
   gcry_sexp_t s_sig, s_hash, s_pkey;
   int rc;
   const int pkalgo = map_pk_openpgp_to_gcry (algo);
+  int is_ed25519 = 0;
 
   /* Make a sexp from pkey.  */
   if (pkalgo == GCRY_PK_DSA)
@@ -79,15 +80,24 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
     }
   else if (pkalgo == GCRY_PK_ECDSA) /* Same as GCRY_PK_ECDH */
     {
-      char *curve = openpgp_oid_to_str (pkey[0]);
-      if (!curve)
-        rc = gpg_error_from_syserror ();
+      is_ed25519 = openpgp_oid_is_ed25519 (pkey[0]);
+      if (is_ed25519)
+        rc = gcry_sexp_build (&s_pkey, NULL,
+                              "(public-key(ecc(curve Ed25519)"
+                              "(flags eddsa)(q%m)))",
+                              pkey[1]);
       else
         {
-          rc = gcry_sexp_build (&s_pkey, NULL,
-                                "(public-key(ecdsa(curve %s)(q%m)))",
-                                curve, pkey[1]);
-          xfree (curve);
+          char *curve = openpgp_oid_to_str (pkey[0]);
+          if (!curve)
+            rc = gpg_error_from_syserror ();
+          else
+            {
+              rc = gcry_sexp_build (&s_pkey, NULL,
+                                    "(public-key(ecdsa(curve %s)(q%m)))",
+                                    curve, pkey[1]);
+              xfree (curve);
+            }
         }
     }
   else
@@ -97,8 +107,18 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
     BUG ();  /* gcry_sexp_build should never fail.  */
 
   /* Put hash into a S-Exp s_hash. */
-  if (gcry_sexp_build (&s_hash, NULL, "%m", hash))
-    BUG (); /* gcry_sexp_build should never fail.  */
+  if (is_ed25519)
+    {
+      if (gcry_sexp_build (&s_hash, NULL,
+                           "(data(flags eddsa)(hash-algo sha512)(value %m))",
+                           hash))
+        BUG (); /* gcry_sexp_build should never fail.  */
+    }
+  else
+    {
+      if (gcry_sexp_build (&s_hash, NULL, "%m", hash))
+        BUG (); /* gcry_sexp_build should never fail.  */
+    }
 
   /* Put data into a S-Exp s_sig. */
   s_sig = NULL;
@@ -114,6 +134,9 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
     {
       if (!data[0] || !data[1])
         rc = gpg_error (GPG_ERR_BAD_MPI);
+      else if (is_ed25519)
+        rc = gcry_sexp_build (&s_sig, NULL,
+                              "(sig-val(eddsa(r%M)(s%M)))", data[0], data[1]);
       else
         rc = gcry_sexp_build (&s_sig, NULL,
                               "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]);
@@ -223,8 +246,8 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
       size_t fpn;
 
       /* Get the shared point and the ephemeral public key.  */
-      shared = mpi_from_sexp (s_ciph, "s");
-      public = mpi_from_sexp (s_ciph, "e");
+      shared = get_mpi_from_sexp (s_ciph, "s", GCRYMPI_FMT_USG);
+      public = get_mpi_from_sexp (s_ciph, "e", GCRYMPI_FMT_USG);
       gcry_sexp_release (s_ciph);
       s_ciph = NULL;
       if (DBG_CIPHER)
@@ -256,9 +279,9 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
   else /* Elgamal or RSA case.  */
     { /* Fixme: Add better error handling or make gnupg use
          S-expressions directly.  */
-      resarr[0] = mpi_from_sexp (s_ciph, "a");
+      resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG);
       if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E)
-        resarr[1] = mpi_from_sexp (s_ciph, "b");
+        resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG);
     }
 
   gcry_sexp_release (s_ciph);
@@ -296,15 +319,25 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey)
     }
   else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH)
     {
-      char *curve = openpgp_oid_to_str (skey[0]);
-      if (!curve)
-        rc = gpg_error_from_syserror ();
-      else
+      if (openpgp_oid_is_ed25519 (skey[0]))
         {
           rc = gcry_sexp_build (&s_skey, NULL,
-                                "(private-key(ecdsa(curve%s)(q%m)(d%m)))",
-                                curve, skey[1], skey[2]);
-          xfree (curve);
+                                "(private-key(ecc(curve Ed25519)"
+                                "(flags eddsa)(q%m)(d%m)))",
+                                skey[1], skey[2]);
+        }
+      else
+        {
+          char *curve = openpgp_oid_to_str (skey[0]);
+          if (!curve)
+            rc = gpg_error_from_syserror ();
+          else
+            {
+              rc = gcry_sexp_build (&s_skey, NULL,
+                                    "(private-key(ecdsa(curve%s)(q%m)(d%m)))",
+                                    curve, skey[1], skey[2]);
+              xfree (curve);
+            }
         }
     }
   else
index e5165f7..48bfbb5 100644 (file)
@@ -23,7 +23,7 @@
 #include "packet.h"  /* For PKT_public_key.  */
 
 /*-- pkglue.c --*/
-gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item);
+gcry_mpi_t get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt);
 
 int pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data,
                gcry_mpi_t *pkey);
index ac6e6d6..e7f4997 100644 (file)
@@ -264,7 +264,12 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo)
 
   pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo);
 
-  if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
+  if (pkalgo == GCRY_PK_ECDSA && openpgp_oid_is_ed25519 (pk->pkey[0]))
+    {
+      frame = gcry_mpi_set_opaque_copy (NULL, gcry_md_read (md, hash_algo),
+                                        8*gcry_md_get_algo_dlen (hash_algo));
+    }
+  else if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
     {
       /* It's a DSA signature, so find out the size of q.  */
 
index 8944067..cfac5de 100644 (file)
@@ -281,11 +281,16 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig,
         ;
       else if (pksk->pubkey_algo == GCRY_PK_RSA
                || pksk->pubkey_algo == GCRY_PK_RSA_S)
-        sig->data[0] = mpi_from_sexp (s_sigval, "s");
+        sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
+      else if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
+        {
+          sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE);
+          sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE);
+        }
       else
         {
-          sig->data[0] = mpi_from_sexp (s_sigval, "r");
-          sig->data[1] = mpi_from_sexp (s_sigval, "s");
+          sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG);
+          sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
         }
 
       gcry_sexp_release (s_sigval);
@@ -422,6 +427,10 @@ match_dsa_hash (unsigned int qbytes)
   usable for the pubkey algorithm.  If --preferred-digest-prefs isn't
   set, then take the OpenPGP default (i.e. SHA-1).
 
+  Note that Ed25519+EdDSA takes an input of arbitrary length and thus
+  we don't enforce any particular algorithm like we do for standard
+  ECDSA. However, we use SHA256 as the default algorithm.
+
   Possible improvement: Use the highest-ranked usable algorithm from
   the signing key prefs either before or after using the personal
   list?
@@ -437,6 +446,14 @@ hash_for (PKT_public_key *pk)
     {
       return recipient_digest_algo;
     }
+  else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+           && openpgp_oid_is_ed25519 (pk->pkey[0]))
+    {
+      if (opt.personal_digest_prefs)
+        return opt.personal_digest_prefs[0].value;
+      else
+        return DIGEST_ALGO_SHA256;
+    }
   else if (pk->pubkey_algo == PUBKEY_ALGO_DSA
            || pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
     {
@@ -927,7 +944,8 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
            for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
              {
                if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
-                    || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
+                    || (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+                        && !openpgp_oid_is_ed25519 (sk_rover->pk->pkey[1])))
                  {
                    int temp_hashlen = (gcry_mpi_get_nbits
                                         (sk_rover->pk->pkey[1]));
@@ -1492,8 +1510,13 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
        else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA)
          digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8);
         else if(pksk->pubkey_algo == PUBKEY_ALGO_ECDSA )
-         digest_algo = match_dsa_hash (ecdsa_qbits_from_Q
-                                        (gcry_mpi_get_nbits (pksk->pkey[1]))/8);
+          {
+            if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
+              digest_algo = DIGEST_ALGO_SHA256;
+            else
+              digest_algo = match_dsa_hash
+                (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8);
+          }
        else
          digest_algo = DIGEST_ALGO_SHA1;
       }